顺晟科技
2021-06-16 10:57:09
305
微信小程序支付的主要逻辑集中在后端。前端只需要携带支付所需的数据并请求后端接口,然后根据返回的结果处理相应的成功和失败。本文的后端使用了Php,重点介绍了整个支付过程和一些细节。所以用其他后端语言的朋友如果需要可以看看。很多时候,开发的需要和相应问题的解决,确实需要跳出语言语法层面,从系统和流程的角度考虑。
一、微信支付
支付主要分为几个步骤:
前端携带支付所需的数据(商品id、购买数量等)。)来发起支付请求
后端收到支付请求后,对支付数据进行处理,然后用处理后的数据请求微信服务器的统一支付指令接口
后端接收上一步请求的微信服务器返回的数据,重新处理,然后返回前端,让前端开始支付。
前端执行支付动作
前端支付完成后,微信服务器会向后端发送支付通知(即微信会告诉你客户已经支付了款项),后端会根据此通知确定支付完成,然后在支付完成后采取相应的动作,如修改订单状态、添加交易日志等。
从这些步骤可以看出,后端的主要功能是将支付所需的数据发送到微信服务器,然后根据微信服务器的响应确定支付是否完成。
这个过程挺好理解的。图片中,前端是客户,后端是店家,微信服务器的统一点餐界面就像收银员。顾客告诉店主我是谁,我现在要为你支付什么。店家告诉收银员:“某某,你该付多少钱?你准备好收钱了。收银员收到钱后,会告诉店主我收到钱了,给他点东西。
下面详细描述每一步的具体实现。
1.前端请求付款
前端对支付的请求意味着简单地携带支付所需的数据,如用户ID、支付金额、支付指令ID等。它们与您的业务逻辑* *有关,或者与下一步微信服务器支付统一订单界面所需的数据有关。使用微信小程序的wx.request()请求后端支付接口。
2.后端请求微信服务器
后端收到前端发送的支付请求后,可以进行相关验证,如判断用户是否有问题,支付金额是否正确。
后端在验证支付没有问题后,需要使用微信指定的数据格式请求微信统一支付指令接口。
请求微信:指定的数据
这需要更多的代码实现。因为需要的数据量很大,也需要加密,以XML格式发送。
首先以下数据是使用小程序支付时必须提供给微信服务器的参数。
applet appid。大概写小程序的都不会不知道这个。
用户id openid。也就是用户的applet id,我在上一篇博客里解释过如何获取。
商户编号mch_id。开通微信支付商户认证申请成功后,微信发送给您的邮件包括
商户订单号由商家为此付款生成的订单编号
总金额合计_费用。对于订单总额来说,单位是分很重要,要特别注意。
微信服务器回调通知接口地址notify_url。微信确认钱到了之后,会多次给这个地址发消息,告诉你客户已经交钱了,你需要给微信回一条消息,表示你收到通知了。该地址不能有端口号,它必须能够直接接受开机自检方法请求。
交易类型贸易类型。微信小程序作为JSAPI支付这个值
商品信息正文。类似于‘腾讯-游戏’的格式
终端IP地址spbill_create_ip。终端地址IP,即请求支付的IP地址。
随机字符串nonce_str。需要后端随机生成的字符串来确保数据安全。微信要求不超过32位数字。
签字。使用上述所有参数进行相应的处理和加密,以生成签名。(具体处理方法见下面的代码,可以直接重用。)
处理完以上所有数据后,以XML格式组织数据,通过POST方式发送到微信支付统一订单界面
3.后端接受微信服务器返回的数据
微信服务器收到支付数据后,如果数据没有问题,会返回相应的数据进行支付,尤其是名为predated _ id的数据字段,需要返回前端,前端才能继续支付。
因此,后端收到微信服务器返回的数据后,需要进行相应的处理,最后将以下数据返回给前端:
Appid不用多说
时间戳当前时间戳
非随机字符串
套餐是前面提到的预付_id,但是记住格式是“预付_id=预付_id_item”。否则会导致错误。
SignType加密方法,一般应该是MD5
PaySign会对上述数据进行相应的处理和加密。
此时,后端的支付接口已经完成接收前端支付请求,并返回前端支付所需的数据。
4.前端发起支付
前端收到返回的数据后,使用wx.requestPayment()请求发起支付。这个API需要的对象参数的值就是我们上一步返回的数据。
5.后端接受微信服务器的回拨
前端完成支付后,微信服务器确认支付已经完成。通知会发送到步中设置的回拨地址。后端接收回调接口收到通知后,可以判断支付是否完成,然后决定后续动作。
需要注意的是,收到微信服务器的回拨通知后,根据通知的result_code字段判断支付是否成功。后端收到成功通知后,需要返回成功数据,通知微信服务器收到回拨通知。否则微信服务器会一直给后端发消息。另外微信的通知是以XML格式发送的,处理时要注意。
这是微信大概的支付流程。下面是PHP语法的微信支付类,可以和上面的步骤对比,加深理解。当需要支付时,直接传入参数实例化这个类,然后调用该类的pay方法。
//微信支付类
班级微信{
//======[基本信息设置]===============================。
//微信微信官方账号身份的标识
protected $ APPID=appid//填写你的appid。在微信公众平台
protected $ APPSECRET=secret
//接受者标识,标识
受保护$ MCHID=' 11111111//商户id
//商户支付密钥
protected $ KEY=' 192006250 b4c 09247 EC 02 edce 69 F6 a 2d ';
//回调通知接口
protected $ APPURL=' https://smart . afei.com/receive suc ';
//交易类型
protected $ TRADETYPE=' JSAPI
//商品类型信息
受保护的$ BOdy=' wx/book ';
//微信支付类的构造函数
function __construct($openid,$ outTradeNo,$totalFee){
$ this-open id=$ open id;//用户的id
$ this-outradeno=$ outradeno;//商品编号
$ this-TotalFee=$ TotalFee;//总价
}
//微信支付类暴露支付界面
公共职能工资(){
$ result=$ this-weixinapp();
返回$ result
}
//处理微信统一订购界面返回的支付相关数据
私有函数weixinapp(){
$ unified order=$ this-unified order();
$parameters=array(
AppId'=$this-APPId,//小程序ID
TimeStamp'='。time(). ',//时间戳
non crest '=$ this-create non crest(),//随机字符串
包'='预付_ id='。$统一订单['预付款_ id'],//数据包
签名类型='MD5'//签名方法
);
$ parameters[' paySign ']=$ this-getSign($ parameters);
return $参数;
}
/*
*请求微信统一订单界面
*/
私有函数unifiedorder(){
$parameters=array(
Appid'=$this-APPID,//applet id
Mch_id'=$this-MCHID,//商户id
sp bill _ create _ ip '=$ _ server[' remote _ addr '],//终端IP
Notify_url'=$this-APPURL,//通知地址
nonce _ str '=$ this-create nonce str(),//随机字符串
Out _ trade _ no'=$ this-outradeno,//商户订单号
total _ fee '=float val($ this-TotalFee),//总金额
open_id'=$this-openid,//用户信息
trade_type'=$this-TRADETYPE,//交易类型
body'=$this-BODY,//商品信息
);
$ parameters[' sign ']=$ this-GetSign($ parameters);
$ XMldata=$ this-Arraytomxml($ parameters);
$ XML _ result=$ this-PostXMlcURl($ XMldata,' https://API。mch。微信。QQ。com/pay/unified order ',60);
$ result=$ this-XMLToarray($ XML _ result);
返回$结果
}
//数组转字符串方法
受保护的函数Arraytomxml($ arr){
$ xml=' xml
foreach ($arr as $key=$val)
{
if (is_numeric($val)){
$xml .=''.$key .''.$瓦尔。'/'.$key .'';
}else{
$xml .=''.$key .[CData[1 .$瓦尔。']]/'.$key。
}
}
$xml .='/XML ';
返回$ xml
}
受保护的函数xmlToArray($xml){
$ array _ data=JSON _ decode(JSON _ encode(simple XML _ load _ string($ XML,' SimpleXMLElement ',LIBXML_NOCDATA)),true);
返回$ array _ data
}
//发送可扩展标记语言请求方法
私有静态函数postXmlCurl($xml,$url,$second=30)
{
$ ch=curl _ init();
//设置超时
curl_setopt($ch,CURLOPT_TIMEOUT,$ second);
curl_setopt($ch,CURLOPT_URL,$ URL);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验
//设置页眉
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//post提交方式
curl_setopt($ch,CURLOPT_POST,TRUE);
curl_setopt($ch,CURLOPT _ POSTFIELDS,$ XML);
curl_setopt($ch,CURLOPT _ CONNECTTIMEOUT,20);
curl_setopt($ch,CURLOPT_TIMEOUT,40);
set _ time _ limit(0);
//运行卷曲
$ data=curl _ exec($ ch);
//返回结果
if ($data) {
curl _ close($ ch);
返回$数据
} else {
$ error=curl _ errno($ ch);
curl _ close($ ch);
抛出新的WxPayException('curl出错,错误码: $ error ');
}
}
/*
* 对要发送到微信统一下单接口的数据进行签名
*/
受保护的函数getSign($Obj){
foreach ($Obj as $k=$v){
$ Parameters[$ k]=$ v;
}
//签名步骤一:按字典序排序参数
ksort($ Parameters);
$ String=$ this-formatBizQueryParaMap($ Parameters,false);
//签名步骤二:在线后加入键
$字符串=$字符串.key=' .$ this-KEY;
//签名步骤三:MD5加密
$ String=MD5($ String);
//签名步骤四:所有字符转为大写
$ result _=strtopher($ String);
返回$ result _;
}
/*
*排序并格式化参数方法,签名时需要使用
*/
受保护函数formatBizQueryParaMap($paraMap,$urlencode)
{
$ buff=
k sort($ ParAmap);
foreach ($paraMap as $k=$v)
{
if($urlencode)
{
$ v=URL编码($ v);
}
//$buff .=strtolow($ k).'=' .$ v . ';
$buff .=$ k .'='。$ v . ';
}
$ reqPar
if (strlen($buff) 0)
{
$reqPar=substr($buff,0,strlen($ buff)-1);
}
返回$ reqPar
}
/*
* 生成随机字符串方法
*/
受保护的函数create non ert($ length=32){
$ chars=' abcdefghijklmnopqrstuvwxyz 0123456789 ';
$ str=
for($ I=0;$ i $长度;$i ) {
$str .=substr($chars,mt_rand(0,strlen($chars)-1),1);
}
返回$ str
}
}
以上就是微信支付的相关流程。在理清思路后,流程还是比较清晰和简单的。重点在于需要注意一些细节问题,例如数据格式,加密方法等。
下面说一下微信小程序退款的具体实现
二、微信退款
小程序退款的流程和付款相似,但有一些细节上的不同。
首先退款的步骤通常如下:
用户前端点击退款按钮后,后端接收到用户的退款请求通过商城后台呈现给商户,商户确定允许退款后,后端再发起向微信退款接口的请求来请求退款。
后端向微信退款接口发送请求后,得到响应信息,确定退款是否完成,根据退款是否完成再去进行改变订单状态等业务逻辑。
退款的步骤相对微信支付来说比较简单。
值得注意的有以下两点:
1.在微信退款界面请求退款后,可以根据得到的回复直接确定退款是否完成。不需要设置专门的回拨接口来等待微信通知。当然,如果需要,可以在微信商户平台上设置回拨接口,接受微信回拨,但不是必须的。
2.退款请求需要在请求服务器上安装微信提供的安全证书,也就是说,相比支付请求,发起退款请求时请求方式不能重复使用,因为微信退款需要携带证书请求,成功申请微信商户号后可以从微信商户平台下载,Linux下PHP开发环境的证书只需要放在网站根目录的cert文件夹中即可。其他开发环境可能需要导入操作。
下面来说明退款的具体步骤
1.用户发起退款请求
用户在前端发起退款请求,后端接收退款请求,并将相应订单标记为申请退款,显示在后台。商家核对后,如果同意退款,就会进行相应的操作,然后进入真正的退款流程。
2.商家发起退款请求
商户同意退款后,后端向微信提供的退款API发起请求。
就像请求微信支付API一样,退款请求也需要签署所需参数,并以XML形式发送到微信退款API
退款请求所需的参数如下(支付API请求时也使用多个参数):
参数:
应用编号
Mch_id商户号。开通微信支付商户认证申请成功后,微信发送给您的邮件包括
Out _ trade _无商户订单号。支付退款单时生成的订单号
Out _退款_无退款订单号。后端生成的退款单编号需要是的,因为多个相同的退款单只会被退款一次。
Total_fee合计金额。订单总额,以分钟为单位。
退款_费用退款金额。退款金额也以美分为单位
Op_user_id运算符。与商户编号相同
随机数_字符串随机字符串。与付款请求相同
签字。使用上述所有参数进行相应的处理和加密,以生成签名。(具体处理方式和付款一样,可以直接重复使用。)
注意:我总是报告错误的签名,因为我在实例化期间没有传入关键参数。
三.退款完成
发起退款请求后,可以根据请求的响应XML中的result_code字段直接判断退款是否成功,从而对订单状态进行处理和跟进。不需要等待来自另一个界面(如支付)的通知来确定请求状态。当然,如上所述,如果我们需要微信服务器向后端发送通知,我们可以在微信商户平台上进行设置。
因为退款的过程和付款类似,所以我选择了退款的PHP类直接继承付款类。
代码如下。注意退款请求方式postXmlSSLCurl和付款请求方式postXmlCurl的区别,后者是使用上述退款所需的双向凭证。
winxindury类扩展了WeiXinPay{
protected \ $ SSLCERT _ PATH=' cert/API client _ cert . PEM ';//证书路径
protected \ $ SSLKEY _ PATH=' cert/API client _ key . PEM ';//证书路径
受保护\ $ opUserId=' 1234567899//商户号
function __construct($openid,$ outTradeNo,$totalFee,$ outRefundNo,$ RefrendFee){
//初始化退款类所需的变量
$ this-open id=$ open id;
$ this-outradeno=$ outradeno;
$ this-TotalFee=$ TotalFee;
$ this-OutRefund no=$ OutRefund no;
$ this-RefrendFee=$ RefrendFee;
}
公共功能退款(){
//对外曝光退款界面
$ result=$ this-wxrefundapi();
返回$ result
}
私有函数wxrefundapi(){
//通过微信api退款流程
$parma=array(
appid'=$this-APPID,
mch_id'=$this-MCHID,
nonce _ str '=$ this-create non estr(),
out _ return _ no '=$ this-out ref und no,
out _ trade _ no '=$ this-outrade no,
total_fee'=$this-totalFee,
退款_费用'=$ this-退款,
op_user_id'=$this-opUserId,
);
$ parma[' sign ']=$ this-GetSign($ parma);
$ xmldata=$ this-Arraytomxml($ parma);
$ xmlresult=$ this-PostXMLSslcurl($ xmldata,' https://API . mch . weixin . QQ.com/sec API/pay/rebuff ');
$ result=$ this-XMLToarray($ xmlresult);
返回$ result
}
//请求使用证书
函数PostXMlssColl($ XML,$url,$second=30)
{
$ ch=curl _ init();
//超时
curl_setopt($ch,CURLOPT_TIMEOUT,$ second);
//如果有的话,在这里设置代理
//curl_setopt($ch,CURLOPT_PROXY,' 8 . 8 . 8 . 8 ');
//curl_setopt($ch,CURLOPT_PROXYPORT,8080);
curl_setopt($ch,CURLOPT_URL,$ URL);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置标题
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//结果必须是字符串并输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:证书和密钥属于两个。pem文件
//默认格式是PEM,可以注释
curl_setopt($ch,CURLOPT _ SSLCERTTYPE,' PEM ');
curl_setopt($ch,CURLOPT_SSLCERT,$ this-SSLCERT _ PATH);
//默认格式是PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,' PEM ');
curl_setopt($ch,CURLOPT_SSLKEY,$ this-SSLKEY _ PATH);
//后期提交方式
curl_setopt($ch,CURLOPT_POST,true);
curl_setopt($ch,CURLOPT _ POSTFIELDS,$ XML);
$ data=curl _ exec($ ch);
//返回结果
if($data){
curl _ close($ ch);
返回$ data
}
else {
$ error=curl _ errno($ ch);
Echo 'curl错误,错误代码:$error '。br ';
curl _ close($ ch);
返回false
}
}}
三.摘要
以上是微信支付和退款流程及相关知识的介绍。本文中的所有PHP类都是封装的,可以直接使用。
因为微信支付和退款涉及的事情比较复杂,很多人直接看官方文件可能会比较混乱,所以看这篇文章了解一下流程和要点,再看看微信的官方文件。一方面,我们可以更清楚地了解小程序的支付和退款过程。另一方面,由于篇幅有限,作者能力有限,必然有不足之处。为了安全起见,您仍然需要查看官方开发文档。毕竟,说到支付,做一个BUG可不是小事。
11
2022-12
08
1972-02
17
2022-03
15
2022-03
03
2021-08
16
2021-06