Home  >  Article  >  WeChat Applet  >  WeChat mini program payment and refund process example sharing

WeChat mini program payment and refund process example sharing

小云云
小云云Original
2018-01-05 13:15:353214browse

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. So 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. Into the title.

1. Payment

Payment is mainly divided into several steps:

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

  • #After receiving the payment request, the backend processes the payment data, and then carries the processed data to request the unified payment order interface of the WeChat server

  • The backend receives the return data from the previous request to the WeChat server, processes it again, and then returns to the frontend so that the frontend 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 (also That is, WeChat wants to tell you that the customer has 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 how much to buy you such and such. The store owner told the cashier, "How much does so-and-so have to pay? You should be prepared to collect the money." After the cashier received the money, he went to tell the store, "I have received the money, please 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 to the data required to request the WeChat server payment unified ordering interface in the next step**, use wx.request() of the WeChat applet to request the back-end payment interface.

2. The back-end requests the WeChat server

After the back-end receives the payment request sent by the front-end, it can perform related verifications, such as determining whether the user has any problems and whether the payment amount is correct. No, wait.

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 the format of "Tencent-Game"

  • 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 WeChat payment unified ordering interface https://api.mch.weixin.qq.com/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. The most important one is the data field named prepay_id. This data needs to be returned to the front end before 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 receiving the front-end payment request and returned the data required for front-end payment. function.

4. Front-end initiates payment

After receiving the return data, the front-end uses wx.requestPayment() to request payment. 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, 'SimpleXMLElement', 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 WeChat mini program refund

2. Refund

The process of mini program refund is similar to payment, 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, you can directly determine whether the refund has been completed based on the response received. 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 because WeChat refunds For requests that require a certificate, 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 specific steps for refund are explained below

1. The user initiates a refund request

The user initiates a refund request on the front end, and the backend receives the refund request and The corresponding order is marked as applying for refund and is displayed in the background. After the merchant checks it, if it agrees to the refund, it will proceed with the corresponding operation. Only then will the real refund process enter.

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.

It is the same as requesting the WeChat payment API. The refund request also needs to be signed with the required parameters. Refund API sent to WeChat in XML [https://api.mch.weixin.qq.com/pay/refund](https://api.mch.weixin.qq.com/pay/refund)
The parameters required for a refund request are as follows (multiple parameters are also used in payment API requests):

  • mini program appid.

  • 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 when the refund order is paid

  • Refund order number out_refund_no. The refund order number generated by the backend needs to be unique, because multiple identical refund order numbers will only refund once.

  • 总金额 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(
    'appid'=> $this->APPID,
    'mch_id'=> $this->MCHID,
    'nonce_str'=> $this->createNoncestr(),
    'out_refund_no'=> $this->outRefundNo,
    'out_trade_no'=> $this->outTradeNo,
    'total_fee'=> $this->totalFee,
    'refund_fee'=> $this->refundFee,
    'op_user_id' => $this->opUserId,
  );
  $parma['sign'] = $this->getSign($parma);
  $xmldata = $this->arrayToXml($parma);
  $xmlresult = $this->postXmlSSLCurl($xmldata,'https://api.mch.weixin.qq.com/secapi/pay/refund');
  $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, '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);
  //设置header
  curl_setopt($ch,CURLOPT_HEADER,FALSE);
  //要求结果为字符串且输出到屏幕上
  curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
  //设置证书
  //使用证书:cert 与 key 分别属于两个.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);
  //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可不是小事。
最后扯点闲话吧。这篇博客本来应该在三个月前就发表的,也算当时我从一无所知到独立完成微信小程序商城前后端的总结系列的第一篇。但是公司突然出现人员和项目的变动,导致管理和项目上都混乱不堪,再加上个人的惰性,导致此篇博客一直拖到三个月后的今天才断断续续写完。这三个月我的心态因为各种事起起伏伏,也颇有一番风味。

相关推荐:

php如何实现微信小程序支付及退款

微信小程序支付接口的实例详解

详解微信小程序支付功能开发错误总结

The above is the detailed content of WeChat mini program payment and refund process example sharing. 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