详解微信小程序支付流程与梳理


Posted in Javascript onJuly 16, 2019

花了几天把小程序的支付模块接口写了一下,可能有着公众号开发的一点经验,没有入太多的坑,在此我想记录一下整个流程。

首先先把小程序微信支付的图搬过来:

详解微信小程序支付流程与梳理

相信会来查百度的同学们基本都是对文档的说明不是很理解。我下面大概总结一下整个业务逻辑的过程。

微信小程序的商户系统一般是以接口的形式开发的,小程序通过调用与后端约定好的接口进行参数的传递以及数据的接收。在小程序支付这块,还需要跟微信服务器进行交互。过程大致是这样的:

一.小程序调用登录接口获取code,传递给商户服务器用来获取用户的openID

我们知道在微信平台中,同一个公众号的openID都是不同的,它是用户身份识别的id,也就是说,我们通过openID来区分不同的用户,这个有微信开发基础的应该都很熟悉。为了知道谁在支付,我们需要先获取当前用户的openid,那么openID应该怎么获取呢?看下图:

详解微信小程序支付流程与梳理

  1. 小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
  2. 开发者服务器以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

这里要??碌氖牵?绾文阈吹氖怯泄厣唐仿蚵舻男〕绦颍?敲葱枰?谥Ц肚霸俅渭觳庖幌驴獯媪浚?蛭?没?峦甓┑ズ蟛灰欢?砩暇突岣犊睿?绻?诟犊畹钠诩淇獯媪棵涣吮慊岢鱿治侍狻R滴衤呒?揖筒凰堤?嗔耍?馊【鲇谀阈创?氲难辖餍浴?/p>

在我们引入了上面那个文件后,先实例化这个类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;

再次签名完成后,就把五个参数返回给小程序。

四,小程序获取五个参数后,鉴权调起支付

伪代码(小程序端)

// 提交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;

如果一切正常的话,在微信开发者工具就会显示这个二维码,

详解微信小程序支付流程与梳理

如果在真机上测试的话,就会直接弹出支付页面。小程序会直接显示支付成功或者失败的页面,然后微信服务器就会开始访问我们之前设置的支付回调地址来推送支付结果,根据结果可以来更新订单的状态。这里我就不写业务逻辑了,大概讲一下就好。

五,支付回调

实际上我们需要重写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()。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery multiSelect 多选下拉框
Jul 09 Javascript
JavaScript类库D
Oct 24 Javascript
jQuery操作select下拉框的text值和value值的方法
May 31 Javascript
微信小程序-拍照或选择图片并上传文件
Jan 06 Javascript
如何使用Bootstrap 按钮实例详解
Mar 29 Javascript
jQuery实现QQ空间汉字转拼音功能示例
Jul 10 jQuery
Vue中父组件向子组件通信的方法
Jul 11 Javascript
Vue2.0实现将页面中表格数据导出excel的实例
Aug 09 Javascript
Vue Element使用icon图标教程详解(第三方)
Feb 07 Javascript
JS数组方法join()用法实例分析
Jan 18 Javascript
js点击事件的执行过程实例分析【冒泡与捕获】
Apr 11 Javascript
JavaScript最完整的深浅拷贝实现方式详解
Feb 28 Javascript
如何在项目中使用log4.js的方法步骤
Jul 16 #Javascript
JAVA面试题 static关键字详解
Jul 16 #Javascript
微信小程序实现下拉框功能
Jul 16 #Javascript
javascript中的this作用域详解
Jul 15 #Javascript
微信小程序页面上下滚动效果
Nov 18 #Javascript
node.js实现上传文件功能
Jul 15 #Javascript
js canvas实现5张图片合成一张图片
Jul 15 #Javascript
You might like
php empty函数 使用说明
2009/08/10 PHP
PHP Ajax实现无刷新附件上传
2016/08/17 PHP
PHP导出带样式的Excel示例代码
2016/08/28 PHP
php 使用redis锁限制并发访问类示例
2016/11/02 PHP
CentOS7系统搭建LAMP及更新PHP版本操作详解
2020/03/26 PHP
js+JQuery返回顶部功能如何实现
2012/12/03 Javascript
jQuery多媒体插件jQuery Media Plugin使用详解
2014/12/19 Javascript
jQuery+html5实现div弹出层并遮罩背景
2015/04/15 Javascript
深入浅析JavaScript面向对象和原型函数
2016/02/06 Javascript
js图片上传前预览功能(兼容所有浏览器)
2016/08/24 Javascript
Javascript中的getter和setter初识
2017/08/17 Javascript
Vue2.x中利用@font-size引入字体图标报错的解决方法
2018/09/28 Javascript
详解vue使用插槽分发内容slot的用法
2019/03/28 Javascript
vue用BMap百度地图实现即时搜索功能
2019/09/26 Javascript
如何利用node.js开发一个生成逐帧动画的小工具
2019/12/01 Javascript
详解vue之自行实现派发与广播(dispatch与broadcast)
2021/01/19 Vue.js
[01:09:50]VP vs Pain 2018国际邀请赛小组赛BO2 第二场
2018/08/20 DOTA
python通过smpt发送邮件的方法
2015/04/30 Python
python实现K最近邻算法
2018/01/29 Python
Python cookbook(数据结构与算法)同时对数据做转换和换算处理操作示例
2018/03/23 Python
Python3使用pandas模块读写excel操作示例
2018/07/03 Python
python遍历小写英文字母的方法
2019/01/02 Python
Python3中编码与解码之Unicode与bytes的讲解
2019/02/28 Python
使用pyinstaller打包PyQt4程序遇到的问题及解决方法
2019/06/24 Python
Pycharm 文件更改目录后,执行路径未更新的解决方法
2019/07/19 Python
python并发编程多进程之守护进程原理解析
2019/08/20 Python
Scrapy框架实现的登录网站操作示例
2020/02/06 Python
Python实现AI换脸功能
2020/04/10 Python
如何使用 Python 读取文件和照片的创建日期
2020/09/05 Python
scrapy-redis分布式爬虫的搭建过程(理论篇)
2020/09/29 Python
详解CSS3:overflow属性
2020/11/17 HTML / CSS
Pytorch 统计模型参数量的操作 param.numel()
2021/05/13 Python
浅谈Python 中的复数问题
2021/05/19 Python
python 爬取吉首大学网站成绩单
2021/06/02 Python
Python函数式编程中itertools模块详解
2021/09/15 Python
python机器学习实现oneR算法(以鸢尾data为例)
2022/03/03 Python