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


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 相关文章推荐
javascript各浏览器中option元素的表现差异
Apr 07 Javascript
js禁止页面复制功能禁用页面右键菜单示例代码
Aug 29 Javascript
JQuery拖动表头边框线调整表格列宽效果代码
Sep 10 Javascript
jQuery固定元素插件scrolltofixed使用指南
Apr 21 Javascript
javascript实现对表格元素进行排序操作
Nov 18 Javascript
Node.js实用代码段之正确拼接Buffer
Mar 17 Javascript
JS短信验证码倒计时功能的实现(没有验证码,只有倒计时)
Oct 27 Javascript
Angular ng-repeat遍历渲染完页面后执行其他操作详细介绍
Dec 13 Javascript
vue中的数据绑定原理的实现
Jul 02 Javascript
详解Nuxt.js部署及踩过的坑
Aug 07 Javascript
微信小程序实现简易table表格
Jun 19 Javascript
vue 解决addRoutes多次添加路由重复的操作
Aug 04 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 adodb连接mssql解决乱码问题
2009/06/12 PHP
解析PHP强制转换类型及远程管理插件的安全隐患
2014/06/30 PHP
thinkPHP中分页用法实例分析
2015/12/26 PHP
Thinkphp5 微信公众号token验证不成功的原因及解决方法
2017/11/12 PHP
查看图片(前进后退)功能实现js代码
2013/04/24 Javascript
jQuery处理xml格式的返回数据(实例解析)
2013/11/28 Javascript
js+div实现文字滚动和图片切换效果代码
2015/08/27 Javascript
react.js 翻页插件实例代码
2017/01/19 Javascript
jQuery弹出层插件popShow用法示例
2017/01/23 Javascript
js实现字符全排列算法的简单方法
2017/05/01 Javascript
Vue自定义指令实现checkbox全选功能的方法
2018/02/28 Javascript
浅谈Angular6的服务和依赖注入
2018/06/27 Javascript
layui实现左侧菜单点击右侧内容区显示
2019/07/26 Javascript
python基于multiprocessing的多进程创建方法
2015/06/04 Python
Python使用matplotlib简单绘图示例
2018/02/01 Python
对python3新增的byte类型详解
2018/12/04 Python
pyqt5 comboBox获得下标、文本和事件选中函数的方法
2019/06/14 Python
Python 画出来六维图
2019/07/26 Python
Python 实现Image和Ndarray互相转换
2020/02/19 Python
在python中使用nohup命令说明
2020/04/16 Python
python3.5的包存放的具体路径
2020/08/16 Python
python mock测试的示例
2020/10/19 Python
巴西最大的体育用品商城:Netshoes巴西
2016/11/29 全球购物
StubHub意大利:购买和出售全球演唱会和体育赛事门票
2017/11/21 全球购物
Giuseppe Zanotti美国官方网站:将鞋履视为高级时装般精心制作
2018/02/06 全球购物
台湾东南旅游社网站:东南旅游
2019/02/11 全球购物
西班牙土拨鼠床垫公司,感觉在云端:Marmota
2019/03/18 全球购物
英国奢华护肤、美容和Spa品牌:Temple Spa
2019/11/02 全球购物
一套C#面试题
2013/10/09 面试题
几个Shell Script面试题
2012/08/31 面试题
2015年校本培训工作总结
2015/07/24 职场文书
教师外出学习心得体会
2016/01/18 职场文书
公司晚会主持词
2019/04/17 职场文书
Python文件的操作示例的详细讲解
2021/04/08 Python
java中重写父类方法加不加@Override详解
2021/06/21 Java/Android
详解Python类和对象内容
2021/06/22 Python