PHP实现微信退款的分析与源码实现

后端开发   发布日期:2025年05月14日   浏览次数:185

* 1.微信退款到零钱要求必传证书,需要到https://pay.weixin.qq.com 账户中心->账户设置->API安全->下载证书,然后修改代码中的证书路径 

* 2.该文件需放到支付授权目录下,可以在微信支付商户平台->产品中心->开发配置中设置。 

* 3.如提示签名错误可以通过微信支付签名验证工具进行验证:https://pay.weixin.qq.com/wiki/tools/signverify/ 

* 4.错误码参照 :https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2

 

代码如下:

  1. <?php
  2.   /**
  3.   * 关于微信退款的说明
  4.   * 1.微信退款要求必传证书,需要到https://pay.weixin.qq.com 账户中心->账户设置->API安全->下载证书,证书路径在第119行和122行修改
  5.   * 2.错误码参照 :https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
  6.   */
  7.   header('Content-type:text/html; Charset=utf-8');
  8.   $mchid = 'xxxxx'; //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
  9.   $appid = 'xxxxx'; //微信支付申请对应的公众号的APPID
  10.   $apiKey = 'xxxxx'; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
  11.   $orderNo = ''; //商户订单号(商户订单号与微信订单号二选一,至少填一个)
  12.   $wxOrderNo = ''; //微信订单号(商户订单号与微信订单号二选一,至少填一个)
  13.   $totalFee = 0.01; //订单金额,单位:元
  14.   $refundFee = 0.01; //退款金额,单位:元
  15.   $refundNo = 'refund_'.uniqid(); //退款订单号(可随机生成)
  16.   $wxPay = new WxpayService($mchid,$appid,$apiKey);
  17.   $result = $wxPay->doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo,$orderNo);
  18.   if($result===true){
  19.   echo 'refund success';exit();
  20.   }
  21.   echo 'refund fail';
  22.   ?>

 

Wxpay.php

  1. <?php
  2. class WxpayService
  3. {
  4. protected $mchid;
  5. protected $appid;
  6. protected $apiKey;
  7. public $data = null;
  8. public function __construct($mchid, $appid, $key)
  9. {
  10. $this->mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号
  11. $this->appid = $appid; //微信支付申请对应的公众号的APPID
  12. $this->apiKey = $key; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
  13. }
  14. /**
  15. * 退款
  16. * @param float $totalFee 订单金额 单位元
  17. * @param float $refundFee 退款金额 单位元
  18. * @param string $refundNo 退款单号
  19. * @param string $wxOrderNo 微信订单号
  20. * @param string $orderNo 商户订单号
  21. * @return string
  22. */
  23. public function doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo='',$orderNo='')
  24. {
  25. $config = array(
  26. 'mch_id' => $this->mchid,
  27. 'appid' => $this->appid,
  28. 'key' => $this->apiKey,
  29. );
  30. $unified = array(
  31. 'appid' => $config['appid'],
  32. 'mch_id' => $config['mch_id'],
  33. 'nonce_str' => self::createNonceStr(),
  34. 'total_fee' => intval($totalFee * 100), //订单金额 单位 转为分
  35. 'refund_fee' => intval($refundFee * 100), //退款金额 单位 转为分
  36. 'sign_type' => 'MD5', //签名类型 支持HMAC-SHA256和MD5,默认为MD5
  37. 'transaction_id'=>$wxOrderNo, //微信订单号
  38. 'out_trade_no'=>$orderNo, //商户订单号
  39. 'out_refund_no'=>$refundNo, //商户退款单号
  40. 'refund_desc'=>'商品已售完', //退款原因(选填)
  41. );
  42. $unified['sign'] = self::getSign($unified, $config['key']);
  43. $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/secapi/pay/refund', self::arrayToXml($unified));
  44. $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
  45. if ($unifiedOrder === false) {
  46. die('parse xml error');
  47. }
  48. if ($unifiedOrder->return_code != 'SUCCESS') {
  49. die($unifiedOrder->return_msg);
  50. }
  51. if ($unifiedOrder->result_code != 'SUCCESS') {
  52. die($unifiedOrder->err_code);
  53. }
  54. return true;
  55. }
  56. public static function curlGet($url = '', $options = array())
  57. {
  58. $ch = curl_init($url);
  59. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  60. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  61. if (!empty($options)) {
  62. curl_setopt_array($ch, $options);
  63. }
  64. //https请求 不验证证书和host
  65. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  66. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  67. $data = curl_exec($ch);
  68. curl_close($ch);
  69. return $data;
  70. }
  71. public function curlPost($url = '', $postData = '', $options = array())
  72. {
  73. if (is_array($postData)) {
  74. $postData = http_build_query($postData);
  75. }
  76. $ch = curl_init();
  77. curl_setopt($ch, CURLOPT_URL, $url);
  78. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  79. curl_setopt($ch, CURLOPT_POST, 1);
  80. curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
  81. curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
  82. if (!empty($options)) {
  83. curl_setopt_array($ch, $options);
  84. }
  85. //https请求 不验证证书和host
  86. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  87. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  88. //第一种方法,cert 与 key 分别属于两个.pem文件
  89. //默认格式为PEM,可以注释
  90. curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
  91. curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/cert/apiclient_cert.pem');
  92. //默认格式为PEM,可以注释
  93. curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
  94. curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/cert/apiclient_key.pem');
  95. //第二种方式,两个文件合成一个.pem文件
  96. // curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem');
  97. $data = curl_exec($ch);
  98. curl_close($ch);
  99. return $data;
  100. }
  101. public static function createNonceStr($length = 16)
  102. {
  103. $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  104. $str = '';
  105. for ($i = 0; $i < $length; $i++) {
  106. $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
  107. }
  108. return $str;
  109. }
  110. public static function arrayToXml($arr)
  111. {
  112. $xml = "<xml>";
  113. foreach ($arr as $key => $val) {
  114. if (is_numeric($val)) {
  115. $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
  116. } else
  117. $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
  118. }
  119. $xml .= "</xml>";
  120. return $xml;
  121. }
  122. public static function getSign($params, $key)
  123. {
  124. ksort($params, SORT_STRING);
  125. $unSignParaString = self::formatQueryParaMap($params, false);
  126. $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
  127. return $signStr;
  128. }
  129. protected static function formatQueryParaMap($paraMap, $urlEncode = false)
  130. {
  131. $buff = "";
  132. ksort($paraMap);
  133. foreach ($paraMap as $k => $v) {
  134. if (null != $v && "null" != $v) {
  135. if ($urlEncode) {
  136. $v = urlencode($v);
  137. }
  138. $buff .= $k . "=" . $v . "&";
  139. }
  140. }
  141. $reqPar = '';
  142. if (strlen($buff) > 0) {
  143. $reqPar = substr($buff, 0, strlen($buff) - 1);
  144. }
  145. return $reqPar;
  146. }
  147. }
  148. ?>

 

  

以上就是PHP实现微信退款的分析与源码实现的详细内容,更多关于PHP实现微信退款的分析与源码实现的资料请关注九品源码其它相关文章!