详解微信小程序支付流程
转发博主 https://blog.csdn.net/qq_38378384/article/details/80882980
花了几天把小程序的支付模块接口写了一下,可能有着公众号开发的一点经验,没有入太多的坑,在此我想记录一下整个流程。
首先先把小程序微信支付的图搬过来:
相信会来查百度的同学们基本都是对文档的说明不是很理解。我下面大概总结一下整个业务逻辑的过程。
微信小程序的商户系统一般是以接口的形式开发的,小程序通过调用与后端约定好的接口进行参数的传递以及数据的接收。在小程序支付这块,还需要跟微信服务器进行交互。过程大致是这样的:
一.小程序调用登录接口获取code,传递给商户服务器用来获取用户的openID
我们知道在微信平台中,同一个公众号的openID都是不同的,它是用户身份识别的id,也就是说,我们通过openID来区分不同的用户,这个有微信开发基础的应该都很熟悉。为了知道谁在支付,我们需要先获取当前用户的openid,那么openID应该怎么获取呢?看下图:
小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。
看不懂吗?不急,听我慢慢解释,这个业务流程大致就是首先你得先在小程序的代码中调用wx.login()来向微信获取到code,拿到了之后把code通过request传给商户服务器,再由商户服务器通过骚操作来跟微信服务器要session_key和openID。
伪代码如下(小程序端):
- getToken: function () {
- //调用登录接口
- wx.login({
- success: function (res) {
- var code = res.code;
- wx.request({
- url: 商户服务器接口地址,
- data: {
- code: code
- },
- method: 'POST',
- success: function (res) {
- wx.setStorageSync('token', res.data.token); //存在小程序缓存中
- },
- fail: function (res) {
- console.log(res.data);
- }
- })
- }
- })
- }
调用这几行代码就可以向跟微信服务器要code,并且将code传到商户服务器中,记住这里最好使用post发送请求,安全性的东西我应该不用讲了,因为避免其他人滥用接口,于是我们使用token来进行验证。并将商户服务器返回的token存在小程序缓存中。
那么服务器端应该怎么做呢?
我门通过小程序提交的code,和小程序的APPID以及APPSECRET和拼接下列的url,并用curl进行get请求。
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
返回的数据是一个json对象,我门通过使用json_decode(JSON,true)解析为数组,数据包括用户的openID以及session_key,获取到了后我们应该将openID存入数据库中,它代表着用户的身份,那么令牌应该怎么生成呢。
二.token的生成以及缓存
我们根据一个用户表将id和openid联系起来,对应openID的id则是用户的uid,我们可以这么封装
- //要缓存的数据数组
- $cacheValue = $result; //包含openID和session_key
- $cacheValue['uid'] =$uid; //用户id
- $cacheValue['scope'] =ScopeEnum::User; //用户权限级别
缓存的方式我们可以选择redis,memcache, 文件缓存等等,采用键值对(key-value)的方式进行存储,记得设置好过期时间。这里的key我们用token来赋值,token可以通过这样的方式进行生成:
- //获取32位随机字符串
- $str = getRandChar(32); //自定义方法生成32位随机串
- //三组字符串进行md5加密
- $timeStamp =$_SERVER['REQUEST_TIME_FLOAT'];
- //salt
- $salt = config('secure.token_salt'); //随机字符串
- //返回token
- return md5($str.$timeStamp.$salt);
这种算法基本保障了token的唯一性。因为值是我们获取到的openID和session_key所在的数组,所以需要将数组转成json才能存进去。以后的代码当我们需要openID或者uid等时可以直接通过取缓存的方式来取。
三,调用统一下单接口,获取prepay_id,再次签名
在你写完了订单操作后,如何让用户支付订单费用呢?这里就是重点了,我一步一步来说:
1.下载微信JS-SDK:
(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1)
解压打开进入lib文件夹中:
我们需要将lib中的文件放到我们的框架中,例如我使用的是tp5,就放到extend下,最好是在extend下建个子文件夹。其中WxPay.Api.php是入口,WxPay.Config.php是配置文件。下好后需要改动一些地方。在WxPay.Config.php中修改下列的东西改成你的。
然后在WxPay.Api.php中require一下WxPay.Notify.php,如图:
在某个控制器或者服务层的代码先是用Loader::import()引入WxPay.Api.php,相当于五个都引入了。
2.调用统一下单api
这里要啰嗦的是,如何你写的是有关商品买卖的小程序,那么需要在支付前再次检测一下库存量,因为用户下完订单后不一定马上就会付款,如果在付款的期间库存量没了便会出现问题。业务逻辑我就不说太多了,这取决于你写代码的严谨性。
在我们引入了上面那个文件后,先实例化这个类WxPayUnifiedOrder,把需要的参数通过调用对应的方法传入。
伪代码如下:
- //调用微信支付统一下单接口
- $wxOrderData = new \WxPayUnifiedOrder();
- //设置相关参数
- $wxOrderData->SetOut_trade_no($this->orderNO);
- $wxOrderData->SetTrade_type('JSAPI');
- $wxOrderData->SetTotal_fee($totalPrice * 100); //这里的价格单位是分
- $wxOrderData->SetBody('Mc');
- $wxOrderData->SetOpenid($openid);
- $wxOrderData->SetNotify_url(config('secure.pay_back_url'));//支付回调
其中第一个是你的订单号,订单号的生成方法可以自定义,第二个是死参数,第三个是总订单价格,第四个是名称如果是中文的话要转码,第四个是openID,这个这时候就可以从缓存中取了。最后一个是支付回调,就是支付成功后微信要访问的地址。必须是公网能访问的,或者你使用ngrok来进行反向代理转发本地的服务器。
参数设置好了之后,就直接调用SDK的方法了
$wxOrder = \WxPayApi::unifiedOrder($wxOrderData);
如果参数没有错误的话,返回的数据中会含有prepay_id,这个是我们需要的参数。
3.再次签名
- // 提交JSAPI输入对象
- $jsApiPayData = new \WxPayJsApiPay();
- //设置appid
- $jsApiPayData->SetAppid(config('wx.app_id'));
- //timeStamp
- $jsApiPayData->SetTimeStamp((string)time());
- //随机串
- $randStr = md5(time().mt_rand(0,1000));
- $jsApiPayData->SetNonceStr($randStr);
- //数据报
- $jsApiPayData->SetPackage('prepay_id='.$wxOrder['prepay_id']);
- //类型
- $jsApiPayData->SetSignType('MD5');
- //生成签名
- $sign = $jsApiPayData->MakeSign();
- //获得签名数组
- $signData = $jsApiPayData->GetValues();
- //增加字段paySign
- $signData['paySign']=$sign;
- //删除signData中的app_Id字段
- unset($signData['appId']);
- return $signData;
再次签名完成后,就把五个参数返回给小程序。
四,小程序获取五个参数后,鉴权调起支付
伪代码(小程序端)
- pay: function () {
- var token = wx.getStorageSync('token');
- var that = this;
- wx.request({
- url: baseUrl + '/order',
- header: {
- token: token
- },
- data: { //产品的数据
- products:
- [
- {
- product_id: 1, count: 1
- },
- {
- product_id: 2, count: 1
- }
- ]
- },
- method: 'POST',
- success: function (res) {
- console.log(res.data);
- if (res.data.pass) {
- wx.setStorageSync('order_id', res.data.order_id);
- that.getPreOrder(token, res.data.order_id); //调用getPreOrder
- }
- else {
- console.log('订单未创建成功');
- }
- }
- })
- },
- getPreOrder: function (token, orderID) {
- if (token) {
- wx.request({
- url: baseUrl + '/pay/pre_order',
- method: 'POST',
- header: {
- token: token
- },
- data: {
- id: orderID
- },
- success: function (res) {
- var preData = res.data;
- console.log(preData);
- wx.requestPayment({ //请求支付
- timeStamp: preData.timeStamp.toString(),
- nonceStr: preData.nonceStr,
- package: preData.package,
- signType: preData.signType,
- paySign: preData.paySign,
- success: function (res) {
- console.log(res.data);
- },
- fail: function (error) {
- console.log(error);
- }
- })
- }
- })
- }
- },
如果一切正常的话,在微信开发者工具就会显示这个二维码,
如果在真机上测试的话,就会直接弹出支付页面。小程序会直接显示支付成功或者失败的页面,然后微信服务器就会开始访问我们之前设置的支付回调地址来推送支付结果,根据结果可以来更新订单的状态。这里我就不写业务逻辑了,大概讲一下就好。
五,支付回调
实际上我们需要重写WxPayNotify类的NotifyProcess方法,这里记得Loader::impor()引入那个入口类。
- /**
- *
- * 回调方法入口,子类可重写该方法
- * 注意:
- * 1、微信回调超时时间为2s,建议用户使用异步处理流程,确认成功之后立刻回复微信服务器
- * 2、微信服务器在调用失败或者接到回包为非确认包的时候,会发起重试,需确保你的回调是可以重入
- * @param array $data 回调解释出的参数
- * @param string $msg 如果回调处理失败,可以将错误信息输出到该方法
- * @return true 回调出来完成不需要继续回调,false回调处理未完成需要继续回调
- */
- public function NotifyProcess($data, &$msg)
- {
- //TODO 用户基础该类之后需要重写该方法,成功的时候返回true,失败返回false
- return true;
- }
也就是说你需要写个新类继承WxPayNotify,再重写NotifyProcess方法,根据检查$data['result_code']是否为SUCCESS可以判断成功与否,成功的话你可以根据业务需求写业务逻辑,最后return true 即可。这时候会想,我重写了这个方法后微信怎么调用呢,其实这里微信不是要直接调用这个方法,你应该在微信支付回调的方法中实例化这个新类,然后根据获得的对象去调用Handle()方法。$obj = new 新类(),$obj->Handle()。
详解微信小程序支付流程的更多相关文章
- 详解微信小程序开发(项目从零开始)
一.序 微信小程序,估计大家都不陌生,现在应用场景特别多.今天就系统的介绍一下小程序开发.注意,这里只从项目代码上做解析,不涉及小程序如何申请.打包.发布的东西.(这些跟着微信官方文档的流程走就好). ...
- 微信小程序支付及退款流程详解
微信小程序的支付和退款流程 近期在做微信小程序时,涉及到了小程序的支付和退款流程,所以也大概的将这方面的东西看了一个遍,就在这篇博客里总结一下. 首先说明一下,微信小程序支付的主要逻辑集中在后端,前端 ...
- 微信小程序支付步骤
http://blog.csdn.net/wangsf789/article/details/53419781 最近开发微信小程序进入到支付阶段,一直以来从事App开发,所以支付流程还是熟记于心的.但 ...
- php对接微信小程序支付
前言:这里我就假装你已经注册了微信小程序,并且基本的配置都已经好了.注: 个人注册小程序不支持微信支付,所以我还是假装你是企业或者个体工商户的微信小程序,其他的商户号注册,二者绑定,授权,支付开通,就 ...
- 微信小程序支付接入实战
1. 微信小程序支付接入实战 1.1. 需求 最近接到一个小程序微信支付的需求,需要我写后台支持,本着能不自己写就不自己写的cv原则,在网上找到了些第三方程序,经过尝试后,最后决定了这不要脸作者的 ...
- 通俗易懂,C#如何安全、高效地玩转任何种类的内存之Span的脾气秉性(二)。 异步委托 微信小程序支付证书及SSL证书使用 SqlServer无备份下误删数据恢复 把list集合的内容写入到Xml中,通过XmlDocument方式写入Xml文件中 通过XDocument方式把List写入Xml文件
通俗易懂,C#如何安全.高效地玩转任何种类的内存之Span的脾气秉性(二). 前言 读完上篇<通俗易懂,C#如何安全.高效地玩转任何种类的内存之Span的本质(一).>,相信大家对sp ...
- .NET Core 微信小程序支付——(统一下单)
最近公司研发了几个电商小程序,还有一个核心的电商直播,只要是电商一般都会涉及到交易信息,离不开支付系统,这里我们统一实现小程序的支付流程(与服务号实现步骤一样). 目录1.开通小程序的支付能力2.商户 ...
- Java实现微信小程序支付(完整版)
在开发微信小程序支付的功能前,我们先熟悉下微信小程序支付的业务流程图: 不熟悉流程的建议还是仔细阅读微信官方的开发者文档. 一,准备工作 事先需要申请企业版小程序,并开通“微信支付”(即商户功能).并 ...
- Asp.net Core 微信小程序支付
最近要做一个微信小程序支付的功能 在网上找了一下 .net Core做微信支付的博客 和 demo 几乎没有 自己研究了好几天 参考了 很多 大牛的博客 勉强做出来了 因为参数都没有 比如 opid ...
随机推荐
- Use of Function Arctan
Use of Function Arctan Time Limit:10000MS Memory Limit:0KB 64bit IO Format:%lld & %llu S ...
- centos7安装mxnet
pip install mxnet-cu90 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com 安装sklearn时总报错 ...
- 开发过程中git的使用
使用clone命令可以直接将git添加到本地库: 主要是针对分支的操作: 首先可以将创建一个属于自己的分支并往上面提交代码,最后合并到dev分支和master分支上面: 前提(master已经有一个文 ...
- 洛谷P3768 简单的数学题 莫比乌斯反演+杜教筛
题意简述 求出这个式子 \[ \sum_{i=1}^n\sum_{j=1}^n ij(i,j) \bmod p \] 做法 先用莫比乌斯反演拆一下式子 \[ \begin{split} \sum_{i ...
- ZROI 19.07.28 组合计数/lb
T1 题意:\(n\)个变量,\(0 \leq x_i \leq c_i\),求\(\sum x_i = A\)方案数.\(n \leq 32\). Sol: \(n \leq 10\)的时候容斥很水 ...
- hadoop中yarn
一.yarn的概述 Apache Yarn(Yet Another Resource Negotiator的缩写)是hadoop集群资源管理器系统,Yarn从hadoop 2引入,最初是为了改善Map ...
- .Net 网站配置文件 webconfig 配置。 字体图标+视频播放 以及 文件上传
ASP.NET MVC 上传大文件时404 原来IIS7的上传文件大小,即便是在经典模式下,也一定要在system.webServer里设置,加上去就OK了 <system.webServer& ...
- compile and link C/CPP programs on Mac
ref: https://stackoverflow.com/questions/29987716/cannot-use-gsl-library-on-macos-ld-symbols-not-fou ...
- urllib2之开放代理与私密代理
1.开放代理 import urllib2 #构建代理对象 httpproxy_handler = urllib2.ProxyHandler({'http':'填入代理IP'}) #构建opener对 ...
- Java基础之数组类型
对于Java,前面的一些基础概念不是很想写,看了看还是从数组开始写吧(毕竟数组是第一个引用类型,相对复杂一点),我也是学了JAVA不是很久,目前看完了JAVA的基础视频,还有JAVA疯狂讲义这本书的大 ...