Thinkphp5,微信支付宝退款

发布时间:2021-04-30作者:小灵龙点击:120

支付宝app


第一步:整理支付宝 参数,appid,密码等。在extra文件夹下建立一个文件ali.php,把参数都放进去。

1,支付宝app支付三大基本参数:  APPID; 秘钥,应用公钥;
2,支付宝退款三大基本参数: APPID; 秘钥,支付宝公钥;
3,支付宝需要自己生成唯一订单号,这个和支付宝商户中心商户订单号不一样,但成功后回调也会把我们这个发过去的订单号回调发过来,以此订单号为条件,查询订单表数据,更改订单状态;可以顺便把支付宝交易号回调过来保存数据库,后面退款需要用到.
4,支付宝回调地址notify_url必须公网可以访问

<?php
//支付宝配置文件,放在应用配置文件或者模块配置文件都可以。如果多个模块都需要就放置在应用模块config.php中。
return [   
   
    'zhn' => [
        //应用ID APPID)
        'app_id' =>'25555644',
        // ֧异步通知地址,异步是服务器和服务器之间的交互,没有cookieId,没有session值,写业务逻辑。比如接收值,存入数据库
        'notify_url' => 'http://localhost/admin/payment/notify',
        // ֧//同步通知页面跳转,支付宝支付成功要跳转的地址
        'return_url' => '',
        //支付宝网关
        'gatewayUrl'=>'https://openapi.alipay.com/gateway.do',
        //支付宝公钥
        'ali_public_key' => '9ulOzQH63idl05qQIDAQAB',
        // 商户私钥
        'private_key' => '
9ulOzQH63idl05qQIDAQAB',
        'log' => [ // optional
            'file' => '../storage/logs/alipay.log',
            'level' => 'info', //
            'type' => 'single', //
            'max_file' => 30, //
        ],
        'http' => [
            'timeout' => 5.0,
            'connect_timeout' => 5.0,
        ],
    ]
]

?>


第二步:去支付宝开放平台下载 App支付服务端 DEMO & SDK
解压后放入vendor文件夹
jinghuajuanke.cn

第三步:建立一个控制器,支付类。

<?php
namespace app\admin\Controller;
use think\Controller;
use think\Loader;
use think\Request;
class Payment extends Base{   

    private $appId = '应用id';
    private $rsaPrivateKey = "商户秘钥";
    private $alipayrsaPublicKey = "商户公钥";
    private $aop;
    private $notify_url = "公网服务器域名/index/alipay/notify";
    private $alipayrsaPublicKey_zhifubao = "支护宝公钥";

     //生成唯一订单号退款用
    public  function build_order_no(){
      return date('Ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
    }
    public function __construct()
    {
        $config=config('alipay.zhiun');
       $this->appId=$config['app_id'];
       $this->rsaPrivateKey=$config['private_key'];
       $this->alipayrsaPublicKey_zhifubao=$config['ali_public_key'];

       $this->notify_url=$config['notify_url'];
        Loader::import('alipay.AopSdk');
    } 

   //支付宝app退款  退款用支付宝公钥
    public function exitPay(Request $request)

    {      
         //引入sdk
        vendor('aop.AopClient');
        vendor('aop.request.AlipayTradeRefundRequest');
         $aop = new \AopClient ();
         $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
         $aop->appId = $this->appId;
         $aop->rsaPrivateKey = $this->rsaPrivateKey;
         $aop->alipayrsaPublicKey = $this->alipayrsaPublicKey_zhifubao;
         $aop->apiVersion = '1.0';
         $aop->signType = "RSA2";
         $aop->postCharset = 'UTF-8';
         $aop->format = "json";
         $request = new \AlipayTradeRefundRequest (); 

          //需要传入的参数变量
          $trade_no  = ""; //支付宝交易号,和商户订单号不能同时为空
          $refund_fee = "10";//退款金额
          $out_trade_no = "87177359170";//订单支付时传入的商户订单号,不能和 trade_no同时为空。 

          $request->setBizContent("{" .

            "\"trade_no\":\"{$trade_no}\"," . //支付宝交易号,和商户订单号不能同时为空 特殊可选

            "\"out_trade_no\":\"{$out_trade_no}\"," .  //订单支付时传入的商户订单号,不能和 trade_no同时为空。 特殊可选

            "\"refund_amount\":\"{$refund_fee}\"," . //需要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数     必选

            "\"refund_reason\":\"退款\"" . //退款的原因说明    正常退款 可选 最后的“,”逗号去掉

            "}"); 

          $result = $aop->execute($request);
          $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
          $resultCode = $result->$responseNode->code;
          if(!empty($resultCode) && $resultCode == 10000){
              //退款成功,处理订单状态以及更新退款等信息 
              return json(['code'=>200,'msg'=>'退款成功']);

          } else {
              return json(['code'=>400,'msg'=>'退款失败']);

          }
    }   

    //支付宝app支付付款  支付用的是应用公钥.,下面这个没测试
    public function payOrder(Request $request)
    {      
        //引入sdk
        vendor('aop.AopClient');
        vendor('aop.request.AlipayTradeAppPayRequest');

        $subject="测试公益付款";//商品标题
        $body = '公益事业';
        $out_trade_no = $this->build_order_no();
        $total_amount = 0.01;//付款金额
        $aop = new \AopClient ();
        $aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
        $aop->appId = $this->appId;
        $aop->rsaPrivateKey = $this->rsaPrivateKey;
        $aop->format = "json";
        $aop->charset = "UTF-8";
        $aop->signType = "RSA2";
        $request = new \AlipayTradeAppPayRequest();
        $bizcontent = "{\"body\":\"".$body."\","

                . "\"subject\": \"".$subject."\","

                . "\"out_trade_no\": \"".$out_trade_no."\","

                . "\"timeout_express\": \"30m\","

                . "\"total_amount\": \"".$total_amount."\","

                . "\"product_code\":\"QUICK_MSECURITY_PAY\""

                . "}";      

        $request->setNotifyUrl($this->notify_url);

        $request->setBizContent($bizcontent);

       //这里和普通的接口调用不同,使用的是sdkExecute

        $response = $aop->sdkExecute($request); 

        if($response){

            echo json_encode(['status'=>1,'msg'=>'success','data'=>$response]);

        }else{
            echo json_encode(['status'=>0,'msg'=>'false','orderid'=>$orderid]);

        }   

    }

    //支付宝支付成功之后异步回调处理
    public function notify()
    {     
         if ($_POST['trade_status'] == 'TRADE_SUCCESS')
          {//如果支付成功
             //===============修改订单状态===========================//        

              $orderSn = $_POST['out_trade_no'];//获取订单号            

              $data1['batch'] = $_POST['trade_no']; //交易流水号

              //根据订单号,以此为条件,修改订单状态以及存入交易流水号

              echo 'success'; //必须success结尾,否则支付宝会多次回调

              exit;

          }
    } 
 // --------------------------------微信-----------------------------------------------------------
    注意:
1,证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载,API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert,下载之前需要安装商户操作证书)   
    /**
     * 退款
     * @param float $totalFee 订单金额 单位元
     * @param float $refundFee 退款金额 单位元
     * @param string $refundNo 退款单号
     * @param string $wxOrderNo 微信订单号
     * @param string $orderNo 商户订单号
     * @return string
     */
    public function doRefund($totalFee='10', $refundFee='10', $refundNo='', $wxOrderNo = '', $orderNo = '87371826215') {
    	$config=config('weconfig.zhun');      
        $unified = array(
            'appid' => $config['app_id'],
            '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' => uniqid(), //商户退款单号
            '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);
        // { ["return_code"]=>  "SUCCESS" ["return_msg"]=>  "OK" ["appid"]=>  "w90065" ["mch_id"]=>  "1001" ["nonce_str"]=> "S4ZqlV36q" ["sign"]=>  "CD37B81C02D937335D89" ["result_code"]=>  "FAIL" ["err_code"]=>  "ERROR" ["err_code_des"]=> string(21) "订单已全额退款" } 

        if ($unifiedOrder === false) {
            die('parse xml error');
        }
        if ($unifiedOrder->return_code != 'SUCCESS') {
            die($unifiedOrder->return_msg);
        }
        if(!empty($unifiedOrder) && $unifiedOrder->return_code == 'SUCCESS'){
              //退款成功,处理订单状态以及更新退款等信息
            return json_encode(['code'=>100,'msg'=>'退款成功']); 
        } else {
            return json_encode(['code'=>200,'msg'=>$result->$unifiedOrder->err_code_des]); 
        }
       
    }

    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, "cert/wechat/apiclient_cert.pem");//绝对路径 必须有证书       
       //默认格式为PEM,可以注释
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, "cert/wechat/apiclient_key.pem");//绝对路径,必须有证书
        //第二种方式,两个文件合成一个.pem文件
		//curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem');
        $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;
        }
    }

    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 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 xmlToArray($xml) {
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }

    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;
    }

}

?>
标签: