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


    原文:https://blog.csdn.net/jason19905/article/details/78628349

    网上的很多PHP微信支付接入教程都颇为复杂,且需要配置和引入较多的文件,本人通过整理后给出一个单文件版的,希望可以给各位想接入微信支付的带来些许帮助和借鉴意义。

    直接运行该文件即可给指定的微信用户退款。

    需要注意的事项: 

    * 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

    代码如下:

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

    Wxpay.php

    <?php
    class WxpayService
    {
        protected $mchid;
        protected $appid;
        protected $apiKey;
        public $data = null;
        public function __construct($mchid, $appid, $key)
        {
            $this->mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号
            $this->appid = $appid; //微信支付申请对应的公众号的APPID
            $this->apiKey = $key;   //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
        }
        /**
         * 退款
         * @param float $totalFee 订单金额 单位元
         * @param float $refundFee 退款金额 单位元
         * @param string $refundNo 退款单号
         * @param string $wxOrderNo 微信订单号
         * @param string $orderNo 商户订单号
         * @return string
         */
        public function doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo='',$orderNo='')
        {
            $config = array(
                'mch_id' => $this->mchid,
                'appid' => $this->appid,
                'key' => $this->apiKey,
            );
            $unified = array(
                'appid' => $config['appid'],
                'mch_id' => $config['mch_id'],
                'nonce_str' => self::createNonceStr(),
                'total_fee' => intval($totalFee * 100),       //订单金额    单位 转为分
                'refund_fee' => intval($refundFee * 100),       //退款金额 单位 转为分
                'sign_type' => 'MD5',           //签名类型 支持HMAC-SHA256和MD5,默认为MD5
                'transaction_id'=>$wxOrderNo,               //微信订单号
                'out_trade_no'=>$orderNo,        //商户订单号
                'out_refund_no'=>$refundNo,        //商户退款单号
                'refund_desc'=>'商品已售完',     //退款原因(选填)
            );
            $unified['sign'] = self::getSign($unified, $config['key']);
            $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/secapi/pay/refund', self::arrayToXml($unified));
            $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
            if ($unifiedOrder === false) {
                die('parse xml error');
            }
            if ($unifiedOrder->return_code != 'SUCCESS') {
                die($unifiedOrder->return_msg);
            }
            if ($unifiedOrder->result_code != 'SUCCESS') {
                die($unifiedOrder->err_code);
            }
            return true;
        }
        public static function curlGet($url = '', $options = array())
        {
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            if (!empty($options)) {
                curl_setopt_array($ch, $options);
            }
            //https请求 不验证证书和host
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $data = curl_exec($ch);
            curl_close($ch);
            return $data;
        }
        public function curlPost($url = '', $postData = '', $options = array())
        {
            if (is_array($postData)) {
                $postData = http_build_query($postData);
            }
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
            curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
            if (!empty($options)) {
                curl_setopt_array($ch, $options);
            }
            //https请求 不验证证书和host
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            //第一种方法,cert 与 key 分别属于两个.pem文件
            //默认格式为PEM,可以注释
            curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/cert/apiclient_cert.pem');
            //默认格式为PEM,可以注释
            curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/cert/apiclient_key.pem');
            //第二种方式,两个文件合成一个.pem文件
    //        curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem');
            $data = curl_exec($ch);
            curl_close($ch);
            return $data;
        }
        public static function createNonceStr($length = 16)
        {
            $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
            $str = '';
            for ($i = 0; $i < $length; $i++) {
                $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
            }
            return $str;
        }
        public static 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;
        }
        public static function getSign($params, $key)
        {
            ksort($params, SORT_STRING);
            $unSignParaString = self::formatQueryParaMap($params, false);
            $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
            return $signStr;
        }
        protected static function formatQueryParaMap($paraMap, $urlEncode = false)
        {
            $buff = "";
            ksort($paraMap);
            foreach ($paraMap as $k => $v) {
                if (null != $v && "null" != $v) {
                    if ($urlEncode) {
                        $v = urlencode($v);
                    }
                    $buff .= $k . "=" . $v . "&";
                }
            }
            $reqPar = '';
            if (strlen($buff) > 0) {
                $reqPar = substr($buff, 0, strlen($buff) - 1);
            }
            return $reqPar;
        }
    }
    
    ?>
  • 相关阅读:
    Golang 函数
    Golang type
    Golang 分支控制和循环
    Golang 字符型
    Golang运算符
    final 和 static 的区别
    Golang标识符命名规则
    Golang值类型与引用类型
    Golang指针
    启动 jvm 参数小总结
  • 原文地址:https://www.cnblogs.com/lxwphp/p/15453786.html
Copyright © 2020-2023  润新知