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


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去掉字符串里的所有空格
Feb 08 Javascript
javascript开发随笔一 preventDefault的必要
Nov 25 Javascript
探讨jQuery的ajax使用场景(c#)
Dec 03 Javascript
jQuery中的read和JavaScript中的onload函数的区别
Aug 27 Javascript
jquery实现模拟百分比进度条渐变效果代码
Oct 29 Javascript
判断是否存在子节点的实现代码
May 18 Javascript
完美实现js焦点轮播效果(二)(图片可滚动)
Mar 07 Javascript
ES6新增数据结构WeakSet的用法详解
Aug 07 Javascript
vue 之 .sync 修饰符示例详解
Apr 21 Javascript
JavaScript代码压缩工具UglifyJS和Google Closure Compiler的基本用法
Apr 13 Javascript
vue 解决uglifyjs-webpack-plugin打包出现报错的问题
Aug 04 Javascript
openlayers 3实现车辆轨迹回放
Sep 24 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中常用的预定义变量小结
2012/05/09 PHP
解析php利用正则表达式解决采集内容排版的问题
2013/06/20 PHP
ThinkPHP3.2.2的插件控制器功能简述
2014/07/09 PHP
修改WordPress中文章编辑器的样式的方法详解
2015/12/15 PHP
ThinkPHP下表单令牌错误与解决方法分析
2017/05/20 PHP
javascript静态的url如何传递
2007/05/03 Javascript
javascript+xml技术实现分页浏览
2008/07/27 Javascript
JS 对象介绍
2010/01/20 Javascript
JS 图片缩放效果代码
2010/06/09 Javascript
javascript 判断中文字符长度的函数代码
2012/08/27 Javascript
为什么要在引入的css或者js文件后面加参数的详细讲解
2013/05/03 Javascript
微信小程序入门之广告条实现方法示例
2018/12/05 Javascript
js逆向解密之网络爬虫
2019/05/30 Javascript
在Linux下使用Python的matplotlib绘制数据图的教程
2015/06/11 Python
Diango + uwsgi + nginx项目部署的全过程(可外网访问)
2018/04/22 Python
pandas 读取各种格式文件的方法
2018/06/22 Python
python实现自动登录
2018/09/17 Python
基于python3的socket聊天编程
2020/02/17 Python
python tkinter之顶层菜单、弹出菜单实例
2020/03/04 Python
Django Admin设置应用程序及模型顺序方法详解
2020/04/01 Python
Python如何把十进制数转换成ip地址
2020/05/25 Python
OpenCV利用python来实现图像的直方图均衡化
2020/10/21 Python
谈谈对css属性box-sizing的了解
2017/01/04 HTML / CSS
美国著名的品牌折扣店:Burlington
2017/06/08 全球购物
澳大利亚首屈一指的鞋类品牌:Tony Bianco
2018/03/13 全球购物
详细的大学生创业计划书模板
2014/01/27 职场文书
煤矿安全生产责任书
2014/04/15 职场文书
中学教师师德承诺书
2014/05/23 职场文书
初中作文评语
2014/12/25 职场文书
五年级小学生评语
2014/12/26 职场文书
优秀班干部主要事迹材料
2015/11/04 职场文书
安全生产标语口号
2015/12/26 职场文书
2016大学优秀学生干部事迹材料
2016/03/01 职场文书
Java数组与堆栈相关知识总结
2021/06/29 Java/Android
淡雅古典唯美少女娇媚宁静迷人写真
2022/03/21 杂记
Linux中sftp常用命令整理
2022/06/28 Servers