How to implement WeChat applet payment and refund in PHP

小云云
Release: 2018-05-19 16:52:04
Original
9897 people have browsed it

The main logic of WeChat mini-program payment is concentrated on the back-end. The front-end only needs to carry the data required for payment to request the back-end interface and then handle the corresponding success or failure based on the returned results. The backend of this article uses PHP, focusing 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.

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

Backend Receive the return data from the previous request to the WeChat server, process it again, and then return to the front end so that the front end can start payment.

Front-end payment action

After the front-end payment is completed, the WeChat server will send a payment notification to the back-end (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 follow **The next step is to request the WeChat server to pay for the data required by the unified order interface**. 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, and it also needs to be encrypted and stored in XML format to send.
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 the WeChat payment unified order interface using the POST method

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 thing is The name is prepay_id The data field needs to be returned to the front end before the front end can continue to pay.

Therefore, after the back-end receives the return data from the WeChat server, it needs to perform corresponding processing, and finally return the following data to the front-end:

appid No need to say more

timeStamp current timestamp

nonceStr random string

package is the prepay_id mentioned above, but remember the format is like “prepay_id= prepay_id_item”. Otherwise an error will result.

signType encryption method, generally should be MD5

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 returned data, the front-end uses wx.requestPayment() to request payment. This API The required values ​​of the object parameters 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 payment is required, directly pass in the parameters to instantiate this class and then call pay of the class. Just use the method.

//微信支付类
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;
     }
}
Copy after login


#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 refund process of the mini program 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 confirms that the refund is allowed , the backend then 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 refund 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 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 backend. After the merchant checks it, if it agrees to the refund, it will perform the corresponding operation. Only then will the real refund process be entered. .

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.

Same request The WeChat payment API is the same. The refund request also needs to sign the required parameters and send them to WeChat's refund API in XML
The parameters required for the 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 amount total_fee. The total amount of the order, in cents.

Refund amount refund_fee The amount to be refunded, the unit is also in minutes

Operator op_user_id. It can be the same as the merchant number

Random string nonce_str. Same as payment request

signature. Use all the above parameters to process the encryption and generate the signature accordingly. (The specific processing method is the same as payment and can be reused directly.)

3. Refund completed

在发起退款请求后,就可以直接根据请求的响应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;
  }
}}
Copy after login


三. 总结

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

因为微信支付和退款涉及的东西较为繁杂,很多人直接看官方文档可能会一头雾水,所以看过此文了解流程和要点后,再去看微信官方文档。一方面可以更清晰的了解小程序的支付和退款流程。另一方面,本文因为篇幅有限及作者能力有限,肯定有无暇顾及或有所纰漏之处。为求稳妥,还是需要多看看官方开发文档。毕竟事涉支付,出个BUG可不是小事。

以上内容就是关于php如何实现微信小程序支付和退款功能,相信php程序员也有了一定的思路,希望能帮助到大家。

相关推荐:

微信小程序机器人自动客服功能

微信小程序如何实现图片放大预览功能

微信小程序开发入门实例

微信小程序分享时可自定义配图新功能

最全的微信小程序项目实例

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

Related labels:
source:php.cn
Statement of this Website
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
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!