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


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 相关文章推荐
整理8个很棒的 jQuery 倒计时插件和教程
Dec 12 Javascript
js正则表达式中test,exec,match方法的区别说明
Jan 29 Javascript
jquery mobile页面跳转后样式丢失js失效的解决方法
Sep 06 Javascript
JavaScript插件化开发教程 (一)
Jan 27 Javascript
深入理解JavaScript系列(31):设计模式之代理模式详解
Mar 03 Javascript
基于Bootstrap里面的Button dropdown打造自定义select
May 30 Javascript
canvas实现图像布局填充功能
Feb 06 Javascript
Vue props 单向数据流的实现
Nov 06 Javascript
JavaScript迭代器的含义及用法
Jun 21 Javascript
layer ui 导入文件之前传入数据的实例
Sep 23 Javascript
vue和iview实现Scroll 数据无限滚动功能
Oct 31 Javascript
nuxt 实现在其它js文件中使用store的方式
Nov 05 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编写发红包程序
2015/07/22 PHP
php版微信开发之接收消息,自动判断及回复相应消息的方法
2016/09/23 PHP
ThinkPhP+Apache+PHPstorm整合框架流程图解
2020/11/23 PHP
URI、URL和URN之间的区别与联系
2006/12/20 Javascript
javascript 对象的定义方法
2007/01/10 Javascript
javascript eval函数深入认识
2009/02/21 Javascript
Prototype PeriodicalExecuter对象 学习
2009/07/19 Javascript
ext jquery 简单比较
2010/04/07 Javascript
javascript检测页面是否缩放的小例子
2013/05/16 Javascript
angular简介和其特点介绍
2015/01/29 Javascript
将页面table内容与样式另存成excel文件的方法
2015/08/05 Javascript
js友好的时间返回函数
2016/08/24 Javascript
js防阻塞加载的实现方法
2016/09/09 Javascript
AngularJS监听ng-repeat渲染完成的两种方法
2018/01/16 Javascript
vue.js层叠轮播效果的实例代码
2018/11/08 Javascript
js中比较两个对象是否相同的方法示例
2019/09/02 Javascript
[01:18:43]2014 DOTA2华西杯精英邀请赛5 24 iG VS DK
2014/05/25 DOTA
[57:18]DOTA2上海特级锦标赛主赛事日 - 1 败者组第一轮#3VP VS VG
2016/03/03 DOTA
python使用标准库根据进程名如何获取进程的pid详解
2017/10/31 Python
python MysqlDb模块安装及其使用详解
2018/02/23 Python
分享一下Python数据分析常用的8款工具
2018/04/29 Python
python 实现查询Neo4j多节点的多层关系
2019/12/23 Python
利用Python脚本批量生成SQL语句
2020/03/04 Python
HTML5新控件之日期和时间选择输入的实现代码
2018/09/13 HTML / CSS
英国最大的婴儿监视器网上商店:Baby Monitors Direct
2018/04/24 全球购物
卖房协议书
2014/04/11 职场文书
贸易跟单员英文求职信
2014/04/19 职场文书
读书活动总结
2014/04/28 职场文书
小学爱国卫生月活动总结
2014/06/30 职场文书
爱心捐书活动总结
2014/07/05 职场文书
商业企业管理专业求职信
2014/07/10 职场文书
社区母亲节活动总结
2015/02/10 职场文书
机关保密工作承诺书
2015/05/04 职场文书
redis requires ruby version2.2.2的解决方案
2021/07/15 Redis
Python Matplotlib绘制等高线图与渐变色扇形图
2022/04/14 Python
Python如何利用pandas读取csv数据并绘图
2022/07/07 Python