18910140161

PHP实现微信支付及退款流程实例

顺晟科技

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可不是小事。

我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航