微信支付详解
最后更新时间:
一.概论
- Native支付:
商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式
返回说明:微信接口返回 url 链接(wxpay://) 前端根据url生成对应的二维码,用户扫码支付
- 付款码支付:
用户出示微信钱包中的条码、二维码,商家通过扫描用户条码即可完成收款
返回说明:(客户出示微信的条码,商家通过扫码机扫码完成支付)
备注:条码扫码机原理:扫码机在扫码时会识别条码里包含的数字+回车键事件(举例:input输入框监听回车键按键事件)
- H5支付:
商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付。
返回支付H5 url链接,用户跳转到url界面去唤醒支付
- APP支付:
商户通过在移动端应用APP中集成开放SDK调起微信支付模块来完成支付。
在APP内直接通过activity唤醒微信模块内支付模块
(每一个APP软件的每一个界面都是一个activity,另一个APP可以通过)
- 小程序支付:
在微信内打开小程序时,可以调用微信支付完成下单购买的流程
微信小程序内调起微信支付接口
二.详情
以下已小程序支付为例说明
①V2版本
V2版本介绍:(本公司项目目前皆使用的是V2版本)
V2版本默认是不需要配置证书的 ,但是配置了证书后必须使用证书方式进行调用
接口说明:
第一步:(前端小程序功能)小程序调起支付
wx.requestPayment
(
{
//时间戳
“timeStamp”: “1414561699”,
//随机字符串,不长于32位。
“nonceStr”: “5K8264ILTKCH16CQ2502SI8ZNMTM67VS”,
//小程序下单接口返回的prepay_id参数值,提交格式如:prepay_id=**
//该参数由微信支付统一下单接口返回(如附注一)
“package”: “prepay_id=wx201410272009395522657a690389285100”,
//签名类型,默认为HMAC256和MD5。
“signType”: “MD5”,
//该sign通过加密算法生成
“paySign”: “oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq/xDg==”,
“success”:function(res){},
“fail”:function(res){},
“complete”:function(res){}
}
)
附注一:
统一下单接口获取prepay_id
统一下单接口url:https://api.mch.weixin.qq.com/pay/unifiedorder
参数说明:
‘appid’ => $this->appid, //小程序 ID
‘mch_id’ => $this->mch_id, //商户号
nonce_str’ => $this->createNoncestr(), //随机字符串
‘body’ => $this->body,
‘out_trade_no’ => ‘2018013106125348’, //商户订单号
‘total_fee’ =>100, //总金额 单位 分
‘spbill_create_ip’ => ‘192.168.0.161’, //终端 IP
‘notify_url’ =>‘’, //通知地址 确保外网能正常访问
‘openid’ => $this->openid, //用户 id
‘trade_type’ => ‘JSAPI’//交易类型
‘sign’=>根据以上参数生成
(如果配置了证书:
ssl_key_addr:‘’,
ssl_cert_addr:‘’ 记得是证书的绝对地址而不是证书里面的内容 v3版本使用的是证书里的内容)
备注:①sign的生成算法(注意这个sign并不是小程序所需要的paysign而是统一下单接口需要的sign)
将以上参数去掉sign参数后 进行按字典序排序后去掉空格 在转为appid=123&mch_id=123123&…&trade_type=JSAPI形式的字符串str;再在字符串str后拼接key 得到新的字符串str(appid=123&mch_id=123123&…&trade_type=JSAPI&key=12312313)
最后将得到的str进行md5加密 得到所谓的sign
②注意v2版本 提交给微信的并非是我们常见的json形式的数据而是xml格式
(xml教程:https://www.w3school.com.cn/xml/index.asp)
以上接口成功后会拿到所需要的prepay_id
有了prepay_id后 我们就还剩一个参数没有 那便是小程序调起支付所需要的paysign
此时需要在根据上面sign的生成算法 根据值生成一个小程序使用的paysign
学名把它叫做 二次验签 参数如下
‘appId’ => $this->appid, //小程序 ID
‘timeStamp’ => ‘’ . time() . ‘’, //时间戳
‘nonceStr’ => $this->createNoncestr(), //随机串
‘package’ => ‘prepay_id=’, //数据包(上面拿到的prepay_id)
‘signType’ => ‘MD5’//签名方式
到此:小程序所需要的所有参数已经全部获取 直接调用即可拉起微信支付
②V3
V3接口是最近出来的新的方式,v3的sign生成算法和v2的逻辑上有着本质的区别
只要一个一样的 那就是都需要二次验签,具体区别如下图:
可以看到 ①v3 已经放弃了你们常见的MD5 而改用最新的RSA+SHA256(俗称RSA2加密方式)②v2的所有返回结果都是明文的xml v3改成了加密后的json但是需要解密后才能拿到真正的返回值(解密算法AES-256-GCM)
具体流程如下:
第一步:(前端小程序功能)小程序调起支付
wx.requestPayment
(
{
//时间戳
“timeStamp”: “1414561699”,
//随机字符串,不长于32位。
“nonceStr”: “5K8264ILTKCH16CQ2502SI8ZNMTM67VS”,
//小程序下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
//该参数由微信支付统一下单接口
“package”: “prepay_id=wx201410272009395522657a690389285100”,
//签名类型
“signType”: “RSA”,(只能RSA)
//该sign通过加密算法生成
“paySign”: “oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq/xDg==”,
“success”:function(res){},
“fail”:function(res){},
“complete”:function(res){}
}
)
这个功能 其实和v2的参数大相径庭 重点依旧是获取prepay_id 和 paysign
也就是重点是从后面开始 会有截然不同的体现
jsapi下单接口url:https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
参数:
‘appid’ => $this->appid, //小程序 ID
‘mch_id’ => $this->mch_id, //商户号
‘description’ => 商品描述,
‘out_trade_no’ => ‘2018013106125348’, //商户订单号
‘notify_url’ =>‘’, //通知地址 确保外网能正常访问
‘payer’:{‘openid’ => $this->openid}, //用户 id
‘amount:{‘total’ => 100}, //金额
例如:
{
“mchid”: “1900006XXX”,
“out_trade_no”: “1217752501201407033233368318”,
“appid”: “wxdace645e0bc2cXXX”,
“description”: “Image形象店-深圳腾大-QQ公仔”,
“notify_url”: “https://weixin.qq.com/“,
“amount”: {
“total”: 1,
“currency”: “CNY”
},
“payer”: {
“openid”: “o4GgauInH_RCEdvrrNGrntXDuXXX”
}
}
这就是所有要传入的参数 细心的你们会发现 参数里并没有发现sign
那么v3的接口到底如何调用 我们需要的sign又是如何生成的呢???
具体的如下:
- 第一步:
我们有了以上的参数后 就可以进行签名(第一次签名)
第一次签名 并不再是单纯的依靠我们的参数进行签名 ,而是已一种http协议格式的字符串进行签名:例如
HTTP请求方法\n URL\n 请求时间戳\n 请求随机串\n 请求报文主体\n
以上的 请求方法:get/post
url 网关
请求时间戳(自主生成)/请求随机串(自主生成)/报文主体:我们的参数
对这个字符串先进行sha256编码在对编码后的字符串进行rsa加密
得到的第一次签名的sign
备注:这个sign并不需要加入到参数中 ,而是在我们向微信提交请求时 放在请求头中
auth_info = ‘WECHATPAY2-SHA256-RSA2048 {}’.format(sign)
headers[‘Authorization’] = auth_info
如此我们便能得到prepay_id,那么就还是小程序所需要的paysign
那么小程序所需要的paysign 在v3的接口里是怎么生成的呢
如下:当我们拿到prepay_id以后
我们需要拼接一个这样格式的字符串:
‘{}\n{}\n{}\n{}\n’.format(self.mch_appid, ts, nonce, prepay_id)
举例: Appid\n时间戳\随机字符串\nprepay_id=******
对上面所诉的字符串在进行签名得到小程序调起支付所需要的paysign
返给前端:至此v3版本支付功能结束
由于V3版本设计到了RSA和sha256等算法,这两种算法都封装于openssl里面
Sha256 把一个字符串进行sha256后会成为一个256bit的哈希值(跟md5一样不可逆)
比如:A7FCFC6B5269BDCCE571798D618EA219A68B96CB87A0E21080C2E758D23E4CE9
Rsa ①则是一种对称加密算法 之然而然 就会有加密的密钥 和 解密的密钥(rsa的密钥存放在证书里))②传统的rsa 最开始是1028位的 ,最新的暴力破解已经尝试到了986位,所以 微信使用的全是最新的rsa256(密钥长度为 2056位)(这一点跟支付宝一样,支付宝提供个人沙箱环境 只支持rsa256加密))