Home  >  Article  >  Web Front-end  >  How to implement payment and refund in WeChat mini program

How to implement payment and refund in WeChat mini program

亚连
亚连Original
2018-06-23 15:16:121644browse

When I was working on a WeChat mini program recently, it involved the payment and refund process of the mini program, so I also roughly read this aspect over and over, so I will summarize it in this article

First of all, let me explain that the main logic of WeChat applet payment is concentrated on the backend. The frontend only needs to carry the data required for payment to request the backend interface and then handle the corresponding success or failure based on the returned results. I use PHP on the backend. Of course, in this blog I am not going to post a bunch of code to explain the specific implementation of payment, but will mainly focus on the entire payment process and some details. Therefore, friends who use other back-end languages ​​​​can take a look if necessary. Many times, development needs and corresponding problem solutions really need to go beyond the language syntax level and be considered from the perspective of systems and processes. Okay, no more nonsense. Into the title.

1. Payment

Payment is mainly divided into several steps:

  • The front-end carries the data required for payment (commodity id, purchase quantity, etc.) to initiate a payment request

  • After receiving the payment request, the back-end processes the payment data and then carries the processed Data request WeChat server's payment unified order interface

  • The backend receives the return data from the previous request to the WeChat server, processes it again, and then returns it to the front end so that the front end can start payment.

  • Front-end performs payment action

  • After the front-end payment is completed, the WeChat server will send a payment notification to the back-end (that is, WeChat will tell you that the customer The money has been paid), the backend determines that the payment is completed based on this notification, and then takes the corresponding actions after the payment is completed, such as modifying the order status, adding transaction logs, etc.

It can be seen from these steps that the main function of the backend is to transmit the data required for payment to the WeChat server, and then determine whether the payment is completed based on the response of the WeChat server.

This process is quite easy to understand. Figuratively speaking, the front-end is a customer, the back-end is the store, and the unified ordering interface of the WeChat server is like a cashier. The customer tells the store, who am I, and now I have to pay as much as I want to buy you such and such. The store owner told the cashier, "How much does so-and-so have to pay? Be prepared to collect the money." After the cashier received the money, he went to tell the store that I had received the money and you could give him something.
The following is a detailed explanation of the specific implementation of each step.

1. Front-end request for payment

The front-end request for payment simply carries the data required for payment, such as user ID, payment amount, payment order ID, etc. It is related to your business logic ** Or related data related to ** the next step of requesting the WeChat server payment unified ordering interface**, use wx.request() of the WeChat applet to request the back-end payment interface.

2. The backend requests the WeChat server

After the backend receives the payment request sent by the frontend, it can perform related verifications, such as determining whether the user has any problems, whether the payment amount is correct, etc. .

After verifying that there is no problem and you can apply for payment to the WeChat server, the backend needs to use the data format specified by WeChat to request WeChat’s unified payment order interface.

Request data specified by WeChat:

This requires more code to implement. Because the amount of data required is large, it also needs to be encrypted and sent in XML format.
First of all, the following data are parameters that must be provided to the WeChat server when using mini program payment.

  • Mini program appid. There is probably no one who writes small programs who doesn’t know this. . .

  • User ID openid. That is the user's mini program ID. I explained how to obtain it in my last blog.

  • Merchant number mch_id. After successfully applying for WeChat payment merchant certification, the email sent to you by WeChat will contain

  • merchant order number out_trade_no. The order number

  • generated by the merchant for this payment is the total amount total_fee. The total amount of the order, a very important point is that the unit is cents, so pay special attention to it.

  • WeChat server callback notification interface address notify_url. After WeChat confirms that the money has arrived, it will send multiple messages to this address to tell you that the customer has paid. You need to return a message to WeChat to indicate that you have received the notification. . This address cannot have a port number, and must be able to directly accept POST method requests.

  • Transaction type trade_type. The WeChat applet payment value is unified as JSAPI

  • Product Information Body. Similar to "Tencent-Game" format

  • Terminal IP address spbill_create_ip. Terminal address IP, which is the IP address requesting payment.

  • Random string nonce_str. A string randomly generated by the backend is required to ensure data security. WeChat requires no longer than 32 bits.

  • signature sign. Use all the above parameters to process the encryption and generate the signature accordingly. (The specific processing method can be seen in the code below, which can be reused directly.)

After processing all the above data, organize the data in XML format and send it to the WeChat payment unified ordering interface https://api.mch.weixin.qq.com/ using the POST method. pay/unifiedorder.

3. The backend accepts the data returned by the WeChat server

After the WeChat server receives the payment data, if there is no problem with the data, it will return the corresponding data for payment. , a very important one is the data field named prepay_id. This data needs to be returned to the front end so that the front end can continue to pay.

Therefore, after the backend receives the return data from the WeChat server, it needs to perform corresponding processing, and finally return the following data to the frontend:

  1. appid Needless to say.

  2. timeStamp current timestamp

  3. nonceStr random string

  4. package is the one mentioned above prepay_id, but remember the format is "prepay_id= prepay_id_item". Otherwise an error will result.

  5. signType encryption method, generally should be MD5

  6. paySign processes and encrypts the above data accordingly.

At this point, the back-end payment interface has completed the function of receiving the front-end payment request and returning the data required for front-end payment.

4. Front-end initiates payment

After receiving the return data, the front-end uses wx.requestPayment() to request payment initiation. The values ​​of the object parameters required by this API are the data returned in the previous step.

5. The backend accepts the WeChat server callback

After the frontend completes the payment, the WeChat server confirms that the payment has been completed. A notification will be sent to the callback address set in the first step. After receiving the notification, the back-end receiving callback interface can determine whether the payment is completed and determine subsequent actions.

It should be noted that after receiving the callback notification from the WeChat server, it is judged whether the payment is successful based on the result_code field of the notification. After receiving the successful notification, the backend needs to return success data to the WeChat server to inform the WeChat server that the callback notification has been received. Otherwise, the WeChat server will keep sending messages to the backend. In addition, WeChat notifications are sent in XML format, so you need to pay attention when accepting and processing them.

The general payment process of WeChat is like this. The following is the WeChat payment class of PHP syntax. You can refer to the above steps to deepen your understanding. When you need to pay, you can directly pass in the parameters to instantiate this class and then call the pay method of the class.

//微信支付类
class WeiXinPay{
  //=======【基本信息设置】=====================================
  //微信公众号身份的唯一标识
  protected $APPID = appid;//填写您的appid。微信公众平台里的
  protected $APPSECRET = secret;
  //受理商ID,身份标识
  protected $MCHID = '11111111';//商户id
  //商户支付密钥Key
  protected $KEY = '192006250b4c09247ec02edce69f6a2d';
  //回调通知接口
  protected $APPURL =   'https://smart.afei.com/receivesuc';
  //交易类型
  protected $TRADETYPE = 'JSAPI';
  //商品类型信息
  protected $BODY = 'wx/book';
  //微信支付类的构造函数
  function __construct($openid,$outTradeNo,$totalFee){
    $this->openid = $openid; //用户唯一标识
    $this->outTradeNo = $outTradeNo; //商品编号
    $this->totalFee = $totalFee; //总价
  }
  //微信支付类向外暴露的支付接口
  public function pay(){
    $result = $this->weixinapp();
    return $result;
  }
   //对微信统一下单接口返回的支付相关数据进行处理
   private function weixinapp(){
     $unifiedorder=$this->unifiedorder();
     $parameters=array(
     'appId'=>$this->APPID,//小程序ID
     'timeStamp'=>''.time().'',//时间戳
     'nonceStr'=>$this->createNoncestr(),//随机串
     'package'=>'prepay_id='.$unifiedorder['prepay_id'],//数据包
     'signType'=>'MD5'//签名方式
       );
     $parameters['paySign']=$this->getSign($parameters);
     return $parameters;
   }
  /*
   *请求微信统一下单接口
   */
  private function unifiedorder(){
    $parameters = array(
      'appid' => $this->APPID,//小程序id
      'mch_id'=> $this->MCHID,//商户id
      'spbill_create_ip'=>$_SERVER['REMOTE_ADDR'],//终端ip
      'notify_url'=>$this->APPURL, //通知地址
      'nonce_str'=> $this->createNoncestr(),//随机字符串
      'out_trade_no'=>$this->outTradeNo,//商户订单编号
      'total_fee'=>floatval($this->totalFee), //总金额
      'open_id'=>$this->openid,//用户openid
      'trade_type'=>$this->TRADETYPE,//交易类型
      'body' =>$this->BODY, //商品信息
    );
    $parameters['sign'] = $this->getSign($parameters);
    $xmlData = $this->arrayToXml($parameters);
    $xml_result = $this->postXmlCurl($xmlData,'https://api.mch.weixin.qq.com/pay/unifiedorder',60);
    $result = $this->xmlToArray($xml_result);
    return $result;
  }
  //数组转字符串方法
  protected function arrayToXml($arr){
    $xml = "<xml>";
    foreach ($arr as $key=>$val)
    {
      if (is_numeric($val)){
        $xml.="<".$key.">".$val."</".$key.">";
      }else{
         $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
      }
    }
    $xml.="</xml>";
    return $xml;
  }
  protected function xmlToArray($xml){
    $array_data = json_decode(json_encode(simplexml_load_string($xml, &#39;SimpleXMLElement&#39;, LIBXML_NOCDATA)), true);
    return $array_data;
  }
  //发送xml请求方法
  private static function 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); //严格校验
    //设置header
    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);
    //运行curl
    $data = curl_exec($ch);
    //返回结果
    if ($data) {
      curl_close($ch);
      return $data;
    } else {
      $error = curl_errno($ch);
      curl_close($ch);
      throw new WxPayException("curl出错,错误码:$error");
    }
  }
  /*
   * 对要发送到微信统一下单接口的数据进行签名
   */
  protected function getSign($Obj){
     foreach ($Obj as $k => $v){
     $Parameters[$k] = $v;
     }
     //签名步骤一:按字典序排序参数
     ksort($Parameters);
     $String = $this->formatBizQueryParaMap($Parameters, false);
     //签名步骤二:在string后加入KEY
     $String = $String."&key=".$this->KEY;
     //签名步骤三:MD5加密
     $String = md5($String);
     //签名步骤四:所有字符转为大写
     $result_ = strtoupper($String);
     return $result_;
   }
  /*
   *排序并格式化参数方法,签名时需要使用
   */
  protected function formatBizQueryParaMap($paraMap, $urlencode)
  {
    $buff = "";
    ksort($paraMap);
    foreach ($paraMap as $k => $v)
    {
      if($urlencode)
      {
        $v = urlencode($v);
      }
      //$buff .= strtolower($k) . "=" . $v . "&";
      $buff .= $k . "=" . $v . "&";
    }
    $reqPar;
    if (strlen($buff) > 0)
    {
      $reqPar = substr($buff, 0, strlen($buff)-1);
    }
    return $reqPar;
  }
  /*
   * 生成随机字符串方法
   */
  protected function createNoncestr($length = 32 ){
     $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
     $str ="";
     for ( $i = 0; $i < $length; $i++ ) {
     $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
     }
     return $str;
     }
}

The above is the relevant process of WeChat payment. After clarifying the ideas, the process is relatively clear and simple. The point is that you need to pay attention to some details, such as data format, encryption method, etc.

Let’s talk about the specific implementation of refund for WeChat mini program

2. Refund

Refund for mini program The process and payment are similar, but there are some differences in details.

The first step of refunding is usually as follows:

  • After the user clicks the refund button on the front end, the back end receives the user's refund request and presents it to the merchant through the mall backend. After the merchant determines that the refund is allowed, the backend initiates a request to the WeChat refund interface to request a refund.

  • After the backend sends a request to the WeChat refund interface, it gets the response information, determines whether the refund is completed, and then changes the order status and other business logic based on whether the refund is completed.

The steps for refunding are relatively simple compared to WeChat payment.

It is worth noting the following two points:

1. After requesting a refund from the WeChat refund interface, it is OK according to the response received. Directly determine whether the refund is completed. There is no need to set up a special callback interface to wait for WeChat notifications. Of course, if necessary, you can also set up a callback interface on the WeChat merchant platform to accept WeChat callbacks, but it is not necessary.

2. The refund request requires the security certificate provided by WeChat to be installed on the requesting server. In other words, when initiating a refund request compared to a payment request, the request method cannot be reused at the time of request, because WeChat refunds need to carry the certificate. For requests, this certificate can be downloaded from the WeChat merchant platform after successfully applying for a WeChat merchant account. The certificate for the PHP development environment under Linux only needs to be placed in the cert folder in the root directory of the website. Other development environments may require import operations.

The following will explain the specific steps for refund

1. The user initiates a refund request

The user initiates a refund request on the front-end, and the back-end receives the refund request, marks the corresponding order as applying for refund, and displays it in the background. After the merchant checks it, if it agrees to the refund, it will perform the corresponding operation. Only then will the actual refund be entered. Process.

2. The merchant initiates a refund request

After the merchant agrees to the refund, the backend will initiate a request to the refund API provided by WeChat.

    同请求微信支付API一样.退款请求也需要将需要的参数进行签名后以XML发送到微信的退款API [https://api.mch.weixin.qq.com/pay/refund](https://api.mch.weixin.qq.com/pay/refund)
退款请求需要的参数如下(多个参数在支付API请求时也有使用):

  • 小程序 appid。

  • 商户号 mch_id 。申请开通微信支付商户认证成功后微信发给你的邮件里有

  • 商户订单号 out_trade_no 。退款订单在支付时生成的订单号

  • 退款订单号 out_refund_no 。由后端生成的退款单号,需要保证唯一,因为多个同样的退款单号只会退款一次。

  • 总金额 total_fee 。订单总金额,单位为分。

  • 退款金额 refund_fee 需要退款的金额,单位同样为分

  • 操作员 op_user_id .与商户号相同即可

  • 随机字符串 nonce_str 。同支付请求

  • 签名 sign 。使用上面的所有参数进行相应处理加密生成签名。(具体处理方式与支付相同,可直接复用。)

三. 退款完成

    在发起退款请求后,就可以直接根据请求的响应XML中的  result_code字段来判断退款是否成功,从而对订单状态进行处理和后续操作。不需要像支付那样等待另一个接口的通知来确定请求状态。当然如上文所说,如果需要微信服务器发送通知到后端的话,可以到微信商户平台进行设置。

退款因为流程与支付大同小异,因此退款的PHP类我选择了直接继承支付类,

代码如下,注意区分退款请求方法postXmlSSLCurl和支付请求方法postXmlCurl的区别,这也就是上文提到的退款需要的双向证书的使用。

"`
 class WinXinRefund extends WeiXinPay{
  protected \$SSLCERT_PATH = &#39;cert/apiclient_cert.pem&#39;;//证书路径
  protected \$SSLKEY_PATH = &#39;cert/apiclient_key.pem&#39;;//证书路径
  protected \$opUserId = &#39;1234567899&#39;;//商户号
function __construct($openid,$outTradeNo,$totalFee,$outRefundNo,$refundFee){
  //初始化退款类需要的变量
  $this->openid = $openid;
  $this->outTradeNo = $outTradeNo;
  $this->totalFee = $totalFee;
  $this->outRefundNo = $outRefundNo;
  $this->refundFee = $refundFee;
} 
public function refund(){
  //对外暴露的退款接口
  $result = $this->wxrefundapi();
  return $result;
}
private function wxrefundapi(){
  //通过微信api进行退款流程
  $parma = array(
    &#39;appid&#39;=> $this->APPID,
    &#39;mch_id&#39;=> $this->MCHID,
    &#39;nonce_str&#39;=> $this->createNoncestr(),
    &#39;out_refund_no&#39;=> $this->outRefundNo,
    &#39;out_trade_no&#39;=> $this->outTradeNo,
    &#39;total_fee&#39;=> $this->totalFee,
    &#39;refund_fee&#39;=> $this->refundFee,
    &#39;op_user_id&#39; => $this->opUserId,
  );
  $parma[&#39;sign&#39;] = $this->getSign($parma);
  $xmldata = $this->arrayToXml($parma);
  $xmlresult = $this->postXmlSSLCurl($xmldata,&#39;https://api.mch.weixin.qq.com/secapi/pay/refund&#39;);
  $result = $this->xmlToArray($xmlresult);
  return $result;
}
//需要使用证书的请求
function postXmlSSLCurl($xml,$url,$second=30)
{
  $ch = curl_init();
  //超时时间
  curl_setopt($ch,CURLOPT_TIMEOUT,$second);
  //这里设置代理,如果有的话
  //curl_setopt($ch,CURLOPT_PROXY, &#39;8.8.8.8&#39;);
  //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);
  //设置header
  curl_setopt($ch,CURLOPT_HEADER,FALSE);
  //要求结果为字符串且输出到屏幕上
  curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
  //设置证书
  //使用证书:cert 与 key 分别属于两个.pem文件
  //默认格式为PEM,可以注释
  curl_setopt($ch,CURLOPT_SSLCERTTYPE,&#39;PEM&#39;);
  curl_setopt($ch,CURLOPT_SSLCERT, $this->SSLCERT_PATH);
  //默认格式为PEM,可以注释
  curl_setopt($ch,CURLOPT_SSLKEYTYPE,&#39;PEM&#39;);
  curl_setopt($ch,CURLOPT_SSLKEY, $this->SSLKEY_PATH);
  //post提交方式
  curl_setopt($ch,CURLOPT_POST, true);
  curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
  $data = curl_exec($ch);
  //返回结果
  if($data){
    curl_close($ch);
    return $data;
  }
  else {
    $error = curl_errno($ch);
    echo "curl出错,错误码:$error"."<br>";
    curl_close($ch);
    return false;
  }
}}

三. 总结

以上就是关于微信支付和退款的流程及相关知识的介绍。文中的 PHP类 均封装直接可用。

因为微信支付和退款涉及的东西较为繁杂,很多人直接看官方文档可能会一头雾水,所以看过此文了解流程和要点后,再去看微信官方文档。一方面可以更清晰的了解小程序的支付和退款流程。另一方面,本文因为篇幅有限及作者能力有限,肯定有无暇顾及或有所纰漏之处。为求稳妥,还是需要多看看官方开发文档。毕竟事涉支付,出个BUG可不是小事。
最后扯点闲话吧。这篇博客本来应该在三个月前就发表的,也算当时我从一无所知到独立完成微信小程序商城前后端的总结系列的第一篇。但是公司突然出现人员和项目的变动,导致管理和项目上都混乱不堪,再加上个人的惰性,导致此篇博客一直拖到三个月后的今天才断断续续写完。这三个月我的心态因为各种事起起伏伏,也颇有一番风味。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

使用js如何实现时间戳与日期格式之间转换

在JavaScript中详细解读性能优化

在微信小程序中如何使用input组件实现密码框

The above is the detailed content of How to implement payment and refund in WeChat mini program. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn