PHP实现小程序微信支付v2版本退款以及对回调数据的解密

PHP实现小程序微信支付v2版本退款以及对回调数据的解密

PS:本篇文章主要是对PHP实现小程序退款以及退款回调数据解密的流程做大概解释,代码请各位按照自身需求进行修改
小程序js代码:

  refundOrder(){
    var refund = this 
    wx.request({
      url: 'http://127.0.0.1:2910/wxPayV2/refundOrder/refundOrderAction.php', //此处写你的PHP文件地址
      method:'POST',
      header:{
        'content-type':'application/x-www-form-urlencoded'
      },
      data:{
        'out_trade_no':'商户订单号',
        'refund_fee':'1' //退款金额,单位为分
      },
      success(res){
        console.log(res)
      }
    })
  },

PHP微信v2退款类文件
refundOrder.php

<?php
require_once dirname(__FILE__,3).'/config/appConfig.php';
require_once dirname(__FILE__,2).'/createOrder/createOrder.php';
class refundOrder
{
    /**
     * 获取相关基本配置
     */
    public function __construct(){
        $this -> APIv2 = APIv2;
        $this -> appid = appid;
        $this -> mch_id = mch_id;
    }
    /**
     * 生成32位随机字符串
     * @return false|string
     */
    public function nonce_str(){
        $getData = new createOrder;
        return $getData -> nonce_str();
    }
    /**
     * 生成商户退款单号
     * @return string
     */
    public function out_refund_no(): string
    {
        $getData = new createOrder;
        $data = $getData -> out_trade_no();
        return 'T'.$data;
    }
    /**
     * 返回参与签名的数据主体
     * @param $nonce_str :32位随机字符串
     * @param $transaction_id :微信支付订单号
     * @param $out_refund_no :商户退款单号
     * @param $total_fee :订单总金额
     * @param $refund_fee :退款金额
     * @return array
     */
    public function signBody($nonce_str,$transaction_id,$out_refund_no,$total_fee,$refund_fee): array
    {
        $data = array(
            'appid' => $this -> appid,
            'mch_id' => $this -> mch_id,
            'nonce_str' => $nonce_str,
            'sign_type' => 'MD5',
            'transaction_id' => $transaction_id,
            'out_refund_no' => $out_refund_no,
            'total_fee' => $total_fee,
            'refund_fee' => $refund_fee,
        );
        ksort($data);
        return $data;
    }
    /**
     * 将参与签名的数据转换字符串并连接APIv2
     * @param $data :参与签名的数据主体(array)
     * @return string
     */
    public function disposeSignBody($data): string
    {
        $signBody = '';
        foreach ($data as $key => $val){
            $signBody .= "$key=$val&";
        }
        $signBody .= "key=$this->APIv2";
        return $signBody;
    }
    /**
     * 返回签名值
     * @param $signBody :经过处理后的待签名数据
     * @return string
     */
    public function sign($signBody): string
    {
        return strtoupper(MD5($signBody));
    }
    /**
     * 生成访问微信退款接口的参数主体
     * @param $sign :签名值
     * @param $signBody :参与签名的数据主体
     * @return string
     */
    public function requestBody($sign,$signBody): string
    {
        $signArray = array(
            'sign' => $sign
        );
        $requestArray = array_merge($signArray,$signBody);
        $requestBody = '<xml>';
        foreach($requestArray as $key => $val){
            $requestBody .= "<$key>".$val."</$key>";
        }
        $requestBody .= '</xml>';
        return $requestBody;
    }
    /**
     * 使用curl对微信退款接口进行访问
     * @param $url :微信退款接口url地址
     * @param $data :访问接口所传输的数据主体
     * @return bool|string
     */
    public function curlRequest($url,$data){
        $action = curl_init();
        curl_setopt($action, CURLOPT_URL,$url);
        curl_setopt($action, CURLOPT_HEADER,0);
        curl_setopt($action, CURLOPT_POST,1);
        curl_setopt($action, CURLOPT_POSTFIELDS,$data);
        curl_setopt($action, CURLOPT_SSL_VERIFYPEER,0);
        curl_setopt($action, CURLOPT_SSLCERTTYPE,'pem');
        curl_setopt($action, CURLOPT_SSLCERT,dirname(__FILE__,3).'/cert/apiclient_cert.pem');
        curl_setopt($action, CURLOPT_SSLKEYTYPE,'pem');
        curl_setopt($action, CURLOPT_SSLKEY,dirname(__FILE__,3).'/cert/apiclient_key.pem');
        curl_setopt($action, CURLOPT_SSL_VERIFYHOST,0);
        curl_setopt($action, CURLOPT_RETURNTRANSFER,1);
        curl_setopt($action, CURLOPT_CONNECTTIMEOUT,60);
        $result = curl_exec($action);
        curl_close($action);
        return $result;
    }
    /**
     * 对微信返回的数据进行验签 (微信回传的数据中包含空数据,在对数据进行处理时空数据被转化成数组,目前难以解决,暂时废弃验签操作)
     * @param $data :已经被转化为数组的微信返回数据
     * @return bool
     */
    public function verifySign($data): bool
    {
        $sign = $data['sign'];
        unset($data['sign']);
        ksort($data);
        $signBody = '';
        foreach ($data as $key => $val){
            $signBody .= "$key=$val&";
        }
        $signBody .= "key=$this->APIv2";
        $verifySign = strtoupper(MD5($signBody));
        if($verifySign == $sign){
            return true;
        }else{
            return false;
        }
    }
}

PHP退款代码
refundOrderAction.php

<?php
require_once dirname(__FILE__).'/refundOrder.php';
require_once dirname(__FILE__,3).'/database/connect.php';
$out_trade_no = $_POST['out_trade_no'];  //接收小程序端发送的商户订单号
$refund_fee = $_POST['refund_fee'];  //接收小程序端发送的退款金额
$buildData = new refundOrder;
$getInDatabase = new Connect;
$selectRefund = 'select * from order_basic_information where out_trade_no = "'.$out_trade_no.'";';  //在数据库中根据商户订单号进行查询
$databaseResult = $getInDatabase -> querySql($selectRefund);
$resultData = mysqli_fetch_assoc($databaseResult);
$status = $resultData['status'];  //从数据库中获取订单状态
if($status == 1){  //判断订单状态:1.待支付不能退款
    die('该订单未支付不能进行退款操作');
}
if($status == 7){  //判断订单状态:7.已退款不能退款
    die('该订单已经退款,不能再次进行退款');
}
$transaction_id = $resultData['transaction_id']; //从数据库中获得微信支付单号
$total_fee = $resultData['total_fee'];  //从数据库中获得订单总金额
$out_refund_no = $resultData['out_refund_no'];  //从数据库获取商户退款单号
if(!isset($out_refund_no)){  //判断商户退款单号是否为空
    $out_refund_no = $buildData -> out_refund_no();  //生成商户退款单号
}
$nonce_str = $buildData -> nonce_str();  //获取32位随机字符串
$signBody = $buildData -> signBody($nonce_str,$transaction_id,$out_refund_no,$total_fee,$refund_fee); //获取参与签名的数据主体
$disposeSignBody = $buildData -> disposeSignBody($signBody);//对参与签名的数据主体转换为字符串格式
$sign = $buildData -> sign($disposeSignBody);  //获取签名值
$requestData = $buildData -> requestBody($sign,$signBody); //获取请求接口的主体数据
file_put_contents('./requestAndResultData.xml',$requestData."\n\r",FILE_APPEND); //将请求接口的主体数据存入相关的文件中
$url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';  //微信退款接口url地址
$wxResult = $buildData -> curlRequest($url,$requestData);  //使用curl对微信退款接口进行访问并获得返回数据
if(!isset($wxResult)){
    die('未接收到微信的返回数据');
}
file_put_contents('./requestAndResultData.xml',$wxResult."\n\r",FILE_APPEND); //将访问微信接口返回的数据存入相关文件中
$disposeWxData = simplexml_load_string($wxResult,null,LIBXML_NOCDATA); //对微信返回的数据进行解析
$json_data = json_encode($disposeWxData);  //将数据转换为json格式
$array_data = json_decode($json_data,true);  //将json格式数据转换为数组格式
$refund_id = $array_data['refund_id'];  //从微信返回的数据中获取微信退款单号
//将相关数据存入数据库中
$updateSql = 'update order_basic_information set status = 7,refund_id = "'.$refund_id.'",refund_fee = "'.$refund_fee.'",out_refund_no = "'.$out_refund_no.'",refund_time = "'.date('Y-m-d H:i:s').'" where out_trade_no = "'.$out_trade_no.'";';
$getInDatabase -> querySql($updateSql);

curl返回的数据如下:

<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[appid]]></appid>
<mch_id><![CDATA[商户号]]></mch_id>
<nonce_str><![CDATA[HF1WsvV3kQxqr26R]]></nonce_str>
<sign><![CDATA[签名]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<transaction_id><![CDATA[微信支付单号]]></transaction_id>
<out_trade_no><![CDATA[商户订单号]]></out_trade_no>
<out_refund_no><![CDATA[商户退款单号]]></out_refund_no>
<refund_id><![CDATA[微信退款单号]]></refund_id>
<refund_channel><![CDATA[]]></refund_channel>
<refund_fee>1</refund_fee>
<coupon_refund_fee>0</coupon_refund_fee>
<total_fee>1</total_fee>
<cash_fee>1</cash_fee>
<coupon_refund_count>0</coupon_refund_count>
<cash_refund_fee>1</cash_refund_fee>
</xml>

PS:由于隐私原因,商法的部分数据我已经隐去,不影响各位观看数据结构

以下是对退款回调数据的处理
PHP类代码
refundCallBack.php

<?php
require_once dirname(__FILE__,4).'/config/appConfig.php';
class refundCallBack
{
    /**
     * 将xml格式数据转换为数组
     * @param $data :需要转换成为数组的xml格式数据
     * @return mixed
     */
    public function xmlToArray($data){
        $disposeData = simplexml_load_string($data,null,LIBXML_NOCDATA);
        $json_data = json_encode($disposeData);
        return json_decode($json_data,true);
    }
    /**
     * 对微信返回的加密回调数据进行解密并将其由xml格式转换为数组格式
     * @param $req_info :微信会掉返回的加密数据
     * @return false|string
     */
    public function decrypt($req_info){
        $APIv2 = APIv2;
        $disposeAPI = strtolower(MD5($APIv2));
        $disposeReq = base64_decode($req_info);
        $decrypt = openssl_decrypt($disposeReq,'AES-256-ECB',$disposeAPI,OPENSSL_RAW_DATA);
        file_put_contents('./returnData.xml',$decrypt."\n\r",FILE_APPEND);
        return $decrypt;
    }
}

PHP调用方法的代码
refundCallBackAction.php

<?php
require_once dirname(__FILE__).'/refundCallBack.php';
require_once dirname(__FILE__,4).'/database/connect.php';
$returnData = file_get_contents('php://input');  //接收微信的退款回调数据
$content = $returnData;
file_put_contents('./returnData.xml',$content."\n\r",FILE_APPEND);
$getData = new refundCallBack;
$disposeContent = $getData -> xmlToArray($content);  //将微信回调的xml数据转换为数组数据
$req_info = $disposeContent['req_info'];  //从数组中获取微信的加密数据
$getDecryptData = $getData -> decrypt($req_info);  //对加密数据进行解密
$decryptData = $getData -> xmlToArray($getDecryptData); //将解密的数据由xml数据转化为数据格式
$refund_id = $decryptData['refund_id'];  //从数组中获取微信退款单号
$out_refund_no = $decryptData['out_refund_no'];  //从数组中获取商户退款单号
$refund_fee = $decryptData['refund_fee'];  //从数组中获取退款金额
$out_trade_no = $decryptData['out_trade_no'];  //从数组中获取商户订单号
$refund_time = $decryptData['success_time'];  //从数组中获取退款完成时间
$refund_state = $decryptData['refund_state'];  //从数组中获取退款状态
$getInDatabase = new Connect;
$selectSql = 'select * from order_basic_information where out_trade_no = "'.$out_trade_no.'";'; //从数据库中根据商户订单号获取相关数据
$resultInDatabase = $getInDatabase -> querySql($selectSql);
$dataInDatabase = mysqli_fetch_assoc($resultInDatabase);
$status = $dataInDatabase['status'];  //从数据库返回的数组中获取当前订单状态
if($status == 7){  //判断当前订单是否已经完成退款且已经修改相关数据
    die('订单已经退款,状态已经修改,无需继续修改');
}
if($refund_state != 'SUCCESS'){  //判断微信返回数据的订单状态
    die('解密数据订单状态错误,请排查后重试');
}
//将微信返回的解密数据存入数据库中
$updateSql = 'update order_basic_information set status = 7,out_refund_no = "'.$out_refund_no.'",refund_id = "'.$refund_id.'",refund_time = "'.$refund_time.'",refund_fee = "'.$refund_fee.'" where out_trade_no = "'.$out_trade_no.'";';
$databaseResult = $getInDatabase -> querySql($updateSql);
if($databaseResult == 1){  //判断插入数据结果,向微信返回信息
    $getReturnWxData = file_get_contents('./returnWxData.xml');
    echo $getReturnWxData;
}

微信回调数据的展示

<xml>
<return_code>SUCCESS</return_code>
<appid><![CDATA[appid]]></appid>
<mch_id><![CDATA[商户号]]></mch_id>
<nonce_str><![CDATA[28cf5d99e569ef56178d8e49ed3213ec]]></nonce_str>
<req_info><!微信加密后的数据</req_info>
</xml>
<root>
<cash_refund_fee><![CDATA[1]]></cash_refund_fee>
<out_refund_no><![CDATA[商户退款单号]]></out_refund_no>
<out_trade_no><![CDATA[商户订单号]]></out_trade_no>
<refund_account><![CDATA[REFUND_SOURCE_RECHARGE_FUNDS]]></refund_account>
<refund_fee><![CDATA[1]]></refund_fee>
<refund_id><![CDATA[微信退款单号]]></refund_id>
<refund_recv_accout><![CDATA[支付用户零钱]]></refund_recv_accout>
<refund_request_source><![CDATA[API]]></refund_request_source>
<refund_status><![CDATA[SUCCESS]]></refund_status>
<settlement_refund_fee><![CDATA[1]]></settlement_refund_fee>
<settlement_total_fee><![CDATA[1]]></settlement_total_fee>
<success_time><![CDATA[2022-08-12 14:24:12]]></success_time>
<total_fee><![CDATA[1]]></total_fee>
<transaction_id><![CDATA[微信支付单号]]></transaction_id>
</root>

PS:由于隐私原因,上方部分数据已经隐去,但不影响关看数据结构

下面附上返回给微信通知的数据

<xml>
    <return_code><![CDATA[SUCCESS]]></return_code>
    <return_msg><![CDATA[OK]]></return_msg>
</xml>

注意使用这个代码返回给微信后,微信将停止发送回调数据,所以在完成自己的逻辑之后再将此xml数据返回给微信


本文由CSDN用户: 缱绻淡蓝海 原创,代码具有时效性,作者会不定期对往期文章进行更新

                       

点击阅读全文

上一篇 2023年 6月 12日 am10:22
下一篇 2023年 6月 12日 am10:24