微信小程序____微信支付


引言

相信大家都会对微信支付非常熟悉,什么样的开发场景能少得了支付的环节呢?

Let's do it~


开发接入官方文档阅读

不知几何时起,有人就跟我提过,微信小程序的开发离不开微信官方文档的说明,需要反复看个多遍,其义自见,请允许我多说了这些废话。

参考阅读微信支付官方参考文档

对比JSAPI、JSSDK的异同点

因为小程序调起支付页面的协议是HTTPS(程序访问商户服务都是通过HTTPS)
开发部署的时候需要安装HTTPS服务器(此处就不延伸扩展安装https服务了)

现提供另外一种方式,阿里云申请免费SSL证书并在Nginx上进行安装

业务说明

业务流程

商户系统和微信支付系统主要交互:

1、小程序内调用登录接口,获取到用户的openid,api参见公共api【小程序登录API

2、商户server调用支付统一下单,api参见公共api【统一下单API

3、商户server调用再次签名,api参见公共api【再次签名

4、商户server接收支付通知,api参见公共api【支付结果通知API

5、商户server查询支付结果,api参见公共api【查询订单API


微信支付 Java后端

前面赘述了那么多官方文档的开发简介,下面开始真正的实战!

微信小程序支付的前置准备:

  • 1.微信商户平台账号
  • 2.微信小程序账号
  • 3.微信小程序开通支付接口(个人暂时不支持开通支付)

前台需要的操作:

  • 1.登录获取code,传给开发者后台
  • 6.获取后台传过来的值调用wx.requestPayment方法

后台需要的操作:

  • 2.通过前台传过来的code来获取用户的openId
  • 3.生成sign
  • 4.获取perpay_id
  • 5.再生成一次前台需要的paySign

一、小程序登录API

具体操作:

  • 小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
#示例代码
//app.js
App({
  onLaunch: function() {
    wx.login({
      success: function(res) {
        if (res.code) {
          //发起网络请求
          wx.request({
            url: 'https://test.com/onLogin',
            data: {
              code: res.code
            }
          })
        } else {
          console.log('登录失败!' + res.errMsg)
        }
      }
    });
  }
})
  • 将临时登录凭证code回传到开发者服务器
wx.request({
        url: 'https://URL',//后台URL
        data: {},//登录获取的code
        method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
        // header: {}, // 设置请求的 header//后台规定的请求头
        success: function(res){
          // success
        },
        fail: function(res) {
          // fail
        },
        complete: function(res) {
          // complete
        }
      })
  • 登录凭证校验

此波操作是在开发者服务器进行的。

临时登录凭证校验接口是一个 HTTPS 接口,开发者服务器使用 临时登录凭证code 获取 session_key 和 openid 等。

开发者服务器通过code获取openid只需要访问这个链接:

https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

请求参数

参数 必填 说明
appid 小程序唯一标识
secret 小程序的 app secret
js_code 登录时获取的 code
grant_type 填写为 authorization_code

在不满足UnionID下发条件的情况下,返回参数

参数 说明
openid 用户唯一标识
session_key 会话密钥

在满足UnionID下发条件的情况下,返回参数

参数 说明
openid 用户唯一标识
session_key 会话密钥
unionid 用户在开放平台的唯一标识符

注意:

  1. 会话密钥session_key 是对用户数据进行加密签名的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。

  2. UnionID 只在满足一定条件的情况下返回。具体参看UnionID机制说明

  3. 临时登录凭证code只能使用一次

返回说明

//正常返回的JSON数据包
{
      "openid": "OPENID",
      "session_key": "SESSIONKEY",
}

//满足UnionID返回条件时,返回的JSON数据包
{
    "openid": "OPENID",
    "session_key": "SESSIONKEY",
    "unionid": "UNIONID"
}
//错误时返回JSON数据包(示例为Code无效)
{
    "errcode": 40029,
    "errmsg": "invalid code"
}

统一下单API

商户在小程序中先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易后调起支付。

  • 接口链接

URL地址:

https://api.mch.weixin.qq.com/pay/unifiedorder

这个链接必须是xml格式的,前台是没法调用的。

请求参数

字段名 变量名 必填 类型 示例值 描述
小程序ID appid String(32) wxd678efh567hg6787 微信分配的小程序ID
商户号 mch_id String(32) 1230000109 微信支付分配的商户号
设备号 device_info String(32) 013467007045764 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"
随机字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,长度要求在32位以内。推荐随机数生成算法
签名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 通过签名算法计算得出的签名值,详见签名生成算法
签名类型 sign_type String(32) MD5 签名类型,默认为MD5,支持HMAC-SHA256和MD5。
商品描述 body String(128) 腾讯充值中心-QQ会员充值 商品简单描述,该字段请按照规范传递,具体请见参数规定
商品详情 detail String(6000) 商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见“单品优惠参数说明”
附加数据 attach String(127) 深圳分店 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
商户订单号 out_trade_no String(32) 20150806125346 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
标价币种 fee_type String(16) CNY 符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型
标价金额 total_fee Int 88 订单总金额,单位为分,详见支付金额
终端IP spbill_create_ip String(16) 123.12.12.123 APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
交易起始时间 time_start String(14) 20091225091010 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
交易结束时间 time_expire String(14) 20091227091010 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。订单失效时间是针对订单号而言的,由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期,所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则 建议:最短失效时间间隔大于1分钟
订单优惠标记 goods_tag String(32) WXG 订单优惠标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠
通知地址 notify_url String(256) http://www.weixin.qq.com/wxpay/pay.php 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
交易类型 trade_type String(16) JSAPI 小程序取值如下:JSAPI,详细说明见参数规定
商品ID product_id String(32) 12235413214070356458058 trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
指定支付方式 limit_pay String(32) no_credit 上传此参数no_credit--可限制用户不能使用信用卡支付
用户标识 openid String(128) oUpF8uMuAJO_M2pxb1Q9zNjWeS6o trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。

举例如下:

<xml>
    <appid>wx2421b1c4370ec43b</appid>
    <attach>支付测试</attach>
    <body>JSAPI支付测试</body>
    <mch_id>10000100</mch_id>
    <detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s_16G", "wxpay_goods_id":"1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":"123456", "body":"苹果手机" }, { "goods_id":"iphone6s_32G", "wxpay_goods_id":"1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category":"123789", "body":"苹果手机" } ] }]]></detail>
    <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
    <notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
    <openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
    <out_trade_no>1415659990</out_trade_no>
    <spbill_create_ip>14.23.150.211</spbill_create_ip>
    <total_fee>1</total_fee>
    <trade_type>JSAPI</trade_type>
    <sign>0CB01533B8C1EF103065174F50BCA001</sign>
 </xml> 

注:参数值用XML转义即可,CDATA标签用于说明数据不被XML解析器解析。

总结一下请求参数必填项

字段名 变量名 描述
小程序ID appid 微信分配的小程序ID
商户号 mch_id 微信支付分配的商户号
随机字符串 nonce_str 随机字符串,长度要求在32位以内。推荐随机数生成算法
签名 sign 通过签名算法计算得出的签名值,详见签名生成算法
商品描述 body 商品简单描述,该字段请按照规范传递,具体请见参数规定
商户订单号 out_trade_no 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-*且在同一个商户号下唯一。详见商户订单号
标价金额 total_fee 订单总金额,单位为分,详见支付金额
终端IP spbill_create_ip APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
通知地址 notify_url 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
交易类型 trade_type 小程序取值如下:JSAPI,详细说明见参数规定
用户标识 openid trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。

以上提及的参数格式参考微信支付参数规定

返回结果

字段名 变量名 必填 类型 示例值 描述
返回状态码 return_code String(16) SUCCESS SUCCESS/FAIL 此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
返回信息 return_msg String(128) 签名失败 返回信息,如非空,为错误原因 签名失败 参数格式校验错误

以下字段在return_code为SUCCESS的时候有返回

字段名 变量名 必填 类型 示例值 描述
小程序ID appid String(32) wx8888888888888888 调用接口提交的小程序ID
商户号 mch_id String(32) 1900000109 调用接口提交的商户号
设备号 device_info String(32) 013467007045764 自定义参数,可以为请求支付的终端设备号等
随机字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 微信返回的随机字符串
签名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 微信返回的签名值,详见签名算法
业务结果 result_code String(16) SUCCESS SUCCESS/FAIL
错误代码 err_code String(32) SYSTEMERROR 详细参见下文错误列表
错误代码描述 err_code_des String(128) 系统错误 错误信息描述

以下字段在return_code 和result_code都为SUCCESS的时候有返回

字段名 变量名 必填 类型 示例值 描述
交易类型 trade_type String(16) JSAPI 交易类型,取值为:JSAPI,NATIVE,APP等,说明详见参数规定
预支付交易会话标识 prepay_id String(64) wx201410272009395522657a690389285100 微信生成的预支付会话标识,用于后续接口调用中使用,该值有效期为2小时
二维码链接 code_url String(64) URl:weixin://wxpay/s/An4baqw trade_type为NATIVE时有返回,用于生成二维码,展示给用户进行扫码支付

举例如下:

<xml>
    <return_code><![CDATA[SUCCESS]]></return_code>
    <return_msg><![CDATA[OK]]></return_msg>
    <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
    <mch_id><![CDATA[10000100]]></mch_id>
    <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
    <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
    <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
    <result_code><![CDATA[SUCCESS]]></result_code>
    <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
    <trade_type><![CDATA[JSAPI]]></trade_type>
 </xml> 

此大项步骤用于生成sign和得到prepay_id(此处后台用JAVA由于后台代码太多,我就放在最后面,有需要的可以自己进行查看)

其中获取prepay_id生成的sign需要的参数是我遇到的一个坑,请一定要注意你往微信传的值都是sign需要的value,其中openId在参数必填里面写的是“否”,但是微信小程序支付用到的trade_type是JSAPI,所以微信小程序的openId也是必须的参数。

当这些都准备好了之后你就可以获取perpay_id。

再次签名即为小程序调起支付API

小程序调起支付数据签名字段列表:

字段名 变量名 必填 类型 示例值 描述
小程序ID appId String wxd678efh567hg6787 微信分配的小程序ID
时间戳 timeStamp String 1490840662 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
随机串 nonceStr String 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
数据包 package String prepay_id=wx2017033010242291fcfe0db70013231072 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=wx2017033010242291fcfe0db70013231072
签名方式 signType String MD5 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致

举例如下:
paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D9B4E54AB1950F51E0649E8810ACD6

调用wx.requestPayment(OBJECT)发起微信支付

Object参数说明:

参数 类型 必填 说明
timeStamp String 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
nonceStr String 随机字符串,长度为32个字符以下。
package String 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=***
signType String 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
paySign String 签名,具体签名方案参见微信公众号支付帮助文档;
success Function 接口调用成功的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)

回调结果:

回调类型 errMsg 说明
success requestPayment:ok 调用支付成功
fail requestPayment:fail cancel 用户取消支付
fail requestPayment:fail (detail message) 调用支付失败,其中 detail message 为后台返回的详细失败原因

示例代码:

wx.requestPayment(
{
'timeStamp': '',
'nonceStr': '',
'package': '',
'signType': 'MD5',
'paySign': '',
'success':function(res){},
'fail':function(res){},
'complete':function(res){}
})

本步骤在于从上步骤获取到的preppay_id作为请求参数,返回paySign参数。

有了prepay_id之后,再获取paySign(前台需要的)。这里不一定需要后台生成,前台也有相关的代码来生成。

所需要的参数:
appId,nonceStr,package,signType,timeStamp,key
这是我遇到的坑二,因为上步骤中获取到prepay_id时会给你返回一个sign,我之前以为是这个。

支付结果通知API

支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。

对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)

注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。

推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。

该链接是通过【统一下单API】中提交的参数notify_url设置,如果链接无法访问,商户将无法接收到微信通知。

通知url必须为直接可访问的url,不能携带参数。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action

代码贴士

得到sign的代码:

/**  
    * 微信支付签名算法sign  
    * @param characterEncoding  
    * @param parameters  
    * @return  
    */    
   @Test   
   public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){    
       StringBuffer sb = new StringBuffer();    
       Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)    
       Iterator it = es.iterator();    
       while(it.hasNext()) {    
           Map.Entry entry = (Map.Entry)it.next();    
           String k = (String)entry.getKey();    
           Object v = entry.getValue();    
           if(null != v && !"".equals(v)     
                   && !"sign".equals(k) && !"key".equals(k)) {    
               sb.append(k + "=" + v + "&");    
           }    
       }    

       sb.append("key=" + WeChatPayUtils.key);    
       System.out.println("字符串:"+sb.toString());  
       String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();    
       return sign;    
   }    

MD5Util工具类代码如下:

package cn.cqzdkj.utils;  

import java.security.MessageDigest;  

public class MD5Util {  
    private static String byteArrayToHexString(byte b[]) {  
        StringBuffer resultSb = new StringBuffer();  
        for (int i = 0; i < b.length; i++)  
            resultSb.append(byteToHexString(b[i]));  

        return resultSb.toString();  
    }  

    private static String byteToHexString(byte b) {  
        int n = b;  
        if (n < 0)  
            n += 256;  
        int d1 = n / 16;  
        int d2 = n % 16;  
        return hexDigits[d1] + hexDigits[d2];  
    }  

    public static String MD5Encode(String origin, String charsetname) {  
        String resultString = null;  
        try {  
            resultString = new String(origin);  
            MessageDigest md = MessageDigest.getInstance("MD5");  
            if (charsetname == null || "".equals(charsetname))  
                resultString = byteArrayToHexString(md.digest(resultString  
                        .getBytes()));  
            else  
                resultString = byteArrayToHexString(md.digest(resultString  
                        .getBytes(charsetname)));  
        } catch (Exception exception) {  
        }  
        return resultString;  
    }  

    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",  
        "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };  
}  

然后在需要的地方直接调用上面的createSign这个方法就行了。

SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();    
      parameters.put("appid", appid);    
      parameters.put("mch_id", mch_id);  
      parameters.put("nonce_str",nonce_str);   
      parameters.put("body", body);   
      parameters.put("out_trade_no", "20170215");  
      parameters.put("total_fee", 1);  
      parameters.put("spbill_create_ip", "x.x.x.x");  
      parameters.put("notify_url","http://xxxxx.com");  
      parameters.put("trade_type", "JSAPI");  
      parameters.put("openid", "oGY_ZvxxxxxM");  
      parameters.put("sign","");  


      String characterEncoding = "UTF-8";    
      String mySign = createSign(characterEncoding,parameters);    
      System.out.println("我 的签名是:"+mySign);   

小程序端完整代码如下:

/** 
 * 支付函数 
 * @param  {[type]} _payInfo [description] 
 * @return {[type]}          [description] 
 */  
pay:function(_payInfo,success,fail){  
    var payInfo = {  
        body:'',  
        total_fee:0,  
        order_sn:''  
    }  
    Object.assign(payInfo, _payInfo);  
    if(payInfo.body.length==0){  
        wx.showToast({  
            title:'支付信息描述错误'  
        })  
        return false;  
    }  
    if(payInfo.total_fee==0){  
        wx.showToast({  
            title:'支付金额不能0'  
        })  
        return false;   
    }  
    if(payInfo.order_sn.length==0){  
        wx.showToast({  
            title:'订单号不能为空'  
        })  
        return false;   
    }  
    var This = this;  
    This.getOpenid(function(openid){  
        payInfo.openid=openid;  
        This.request({  
            url:'api/pay/prepay',  
            data:payInfo,  
            success:function(res){  
                var data = res.data;  
                console.log(data);  
                if(!data.status){  
                    wx.showToast({  
                        title:data['errmsg']  
                    })  
                    return false;  
                }  
                This.request({  
                    url:'api/pay/pay',  
                    data:{prepay_id:data.data.data.prepay_id},  
                    success:function(_payResult){  
                        var payResult = _payResult.data;  
                        console.log(payResult);  
                        wx.requestPayment({  
                            'timeStamp': payResult.timeStamp.toString(),  
                            'nonceStr': payResult.nonceStr,  
                            'package': payResult.package,  
                            'signType': payResult.signType,  
                            'paySign': payResult.paySign,  
                            'success': function (succ) {  
                                success&&success(succ);  
                            },  
                            'fail': function (err) {  
                                fail&&fail(err);  
                            },  
                            'complete': function (comp) {   
  
                            }  
                        })   
                    }  
                })  
            }  
        })  
    })  
}

#参考于https://blog.csdn.net/sinat_35861727/article/details/73692794  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容