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


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 相关文章推荐
如果文字过长,则将过长的部分变成省略号显示
Jun 26 Javascript
formValidator3.3的ajaxValidator一些异常分析
Jul 12 Javascript
jquery等待效果示例
May 01 Javascript
JavaScript中的Math.SQRT1_2属性使用简介
Jun 14 Javascript
谈一谈javascript中继承的多种方式
Feb 19 Javascript
javascript检查某个元素在数组中的索引值
Mar 30 Javascript
JS事件添加和移出的兼容写法示例
Jun 20 Javascript
总结Node.js中的一些错误类型
Aug 15 Javascript
javascript中递归的两种写法
Jan 17 Javascript
vue服务端渲染的实例代码
Aug 28 Javascript
微信小程序scroll-view组件实现滚动动画
Jan 31 Javascript
vue动态禁用控件绑定disable的例子
Oct 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
mysql中存储过程、函数的一些问题
2007/02/14 PHP
PHP中=赋值操作符对不同数据类型的不同行为
2011/01/02 PHP
PHP中的Memcache详解
2014/04/05 PHP
初学js插入节点appendChild insertBefore使用方法
2011/07/04 Javascript
jquery iframe操作详细解析
2013/11/20 Javascript
jqGrid随窗口大小变化自适应大小的示例代码
2013/12/28 Javascript
js获取和设置属性的方法
2014/02/20 Javascript
input标签内容改变的触发事件介绍
2014/06/18 Javascript
JavaScript中提前声明变量或函数例子
2014/11/12 Javascript
js+html5获取用户地理位置信息并在Google地图上显示的方法
2015/06/05 Javascript
JavaScript中Math.SQRT2属性的使用详解
2015/06/14 Javascript
jQuery实现鼠标滑向当前图片高亮显示并且其它图片变灰的方法
2015/07/27 Javascript
jQuery实现鼠标双击Table单元格变成文本框及输入内容后更新到数据库的方法
2015/11/25 Javascript
jQuery实现页面顶部显示的进度条效果完整实例
2015/12/09 Javascript
简单实现js选项卡切换效果
2016/02/03 Javascript
jQuery增加、删除及修改select option的方法
2016/08/19 Javascript
jQuery实现的简单悬浮层功能完整实例
2017/01/23 Javascript
浅谈struts1 & jquery form 文件异步上传
2017/05/25 jQuery
浅谈Angular路由守卫
2017/08/26 Javascript
javaScript手机号码校验工具类PhoneUtils详解
2017/12/08 Javascript
微信公众号H5支付接口调用方法
2019/01/10 Javascript
Vue数字输入框组件的使用方法
2019/10/19 Javascript
请求时token过期自动刷新token操作
2020/09/11 Javascript
如何在 Vue 表单中处理图片
2021/01/26 Vue.js
python实现批量改文件名称的方法
2015/05/25 Python
Django框架搭建的简易图书信息网站案例
2019/05/25 Python
利用python3 的pygame模块实现塔防游戏
2019/12/30 Python
M1芯片安装python3.9.1的实现
2021/02/02 Python
一款css实现的鼠标经过按钮的特效
2014/09/11 HTML / CSS
Pam & Gela官网:美国性感前卫女装品牌
2018/07/19 全球购物
计算机科学与技术应届生求职信
2013/11/07 职场文书
文化活动实施方案
2014/03/28 职场文书
爱晚亭导游词
2015/02/09 职场文书
工厂仓管员岗位职责
2015/04/01 职场文书
教你使用vscode 搭建react-native开发环境
2021/07/07 Javascript
JavaScript流程控制(循环)
2021/12/06 Javascript