2021.01.11

关于支付宝退款问题

1 调用支付宝sdk

2 自己写封装

 

1 调用支付宝sdk,网络是比较强大的,只要搜索都能搜索到。

 1 public function refund_and_query($data)
 2     {
 3         import("alipayaop/AopClient", EXTEND_PATH);
 4         import("alipayaop/request/AlipayTradeRefundRequest", EXTEND_PATH);
 5         // include('alipayaop/AopClient.php');
 6         // // include('alipayaop/request/AlipayTradeAppPayRequest.php');
 7         // include('alipayaop/request/AlipayTradeRefundRequest.php');
 8 
 9 
10         $aop = new \AopClient();
11         $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
12         $aop->appId = $this->config['app_id'];
13         $aop->rsaPrivateKey = $this->config['rsaPrivateKey'];
14         $aop->alipayrsaPublicKey=$this->config['alipayPublicKey'];
15         $aop->apiVersion = $this->version;
16         $aop->signType = $this->sign_type;
17         $aop->postCharset=$this->charset;
18         $aop->format='json';
19         $request = new \AlipayTradeRefundRequest();
20         $bizcontent = json_encode([
21             'out_trade_no'=>$data['out_trade_no'],
22             // 'trade_no'=> $wepay_serial,
23             'refund_amount'=> $data['refund_fee'],
24             'refund_reason'=>'正常退款'
25          ]);
26         $request->setBizContent($bizcontent);
27         $result = $aop->execute($request);
28         FLog($result,'refund_and_query_alipay');
29         $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
30 
31         $resultCode = $result->$responseNode->code;
32         
33         if(!empty($resultCode)&&$resultCode == 10000){
34            return ['code'=>1,'msg'=>'退款成功'];
35         } else {
36            $resultMsg  = $result->$responseNode->sub_msg;
37            return ['code'=>0,'msg'=>$resultMsg];
38         }
39     }

对于以上使用sdk 出来的结果以及调用方式来说相当的简单。我这边是直接使用官方提供的sdk 引入后 直接使用 import 调用

无任何需要注意的地方只要能保证输入的值没有问题,就百分百成功,而且不像支付的时候会遇到各种问题,至少我这边没有

 

2 自己写封装

无论关于使用sdk还是自己写 我都是在以前支付封装中加入书写的。下面不给出完整的代码,只针对支付退款。并且由于时间问题,我这里没有给出特别完美的代码,只能说带入没有问题。主要说的是在这个过程中发现的问题

 1 public function refund_and_query1($data)
 2     {
 3         $biz_content=[
 4             'out_trade_no'=>$data['out_trade_no'],
 5             'refund_amount'=>$data['refund_fee'],
 6             'refund_reason'=>'正常退款'
 7         ];
 8         $postdata=[
 9             'app_id'=>$this->config['app_id'],
10             'version'=>$this->version,
11             'format'=>'json',
12             'sign_type'=>$this->sign_type,
13             'method'=>'alipay.trade.refund',
14             'timestamp'=>date('Y-m-d H:i:s'),
15             // 'timestamp'=>'2021-01-11 10:41:23',
16 
17             'charset'=>$this->charset,
18         ];
19 
20         // dump($biz_content);
21 
22 
23         // openssl_sign($str_q, $sign, $rsaPrivateKey, OPENSSL_ALGO_SHA256);
24 
25         $bizcontentD=['biz_content'=>json_encode($biz_content)];
26         $biz_content_res=$this->alisign(array_merge($bizcontentD,$postdata));
27         $postdata['sign']=$biz_content_res['sign'];
28         dump($postdata['sign']);
29         // die;
30 
31 
32 
33         $str=$this->keyandvalStr($postdata,1);
34 
35 
36 
37 
38         //系统参数放入GET请求串
39         $requestUrl = $this->submitUrl . "?".$str;
40 
41         dump($requestUrl);
42 
43       
44 
45 
46         //发起HTTP请求
47         try {
48             $resp = $this->curl($requestUrl, $bizcontentD);
49             dump($resp);
50         } catch (Exception $e) {
51             $this->logCommunicationError($sysParams["method"], $requestUrl, "HTTP_ERROR_" . $e->getCode(), $e->getMessage());
52             return false;
53         }
54     }

 

针对以上代码发现的问题。

1、对于 biz_content 的处理,不同于支付的时候,除了在sign中使用 在生成数据字符串拼接的时候并没有用到,而在http请求的时候却存在着一定的使用

 

 

2、在发起HTTP请求的时候,最好还是使用官方提供的curl,因为自己写的时候总是会报 验签错误 

protected function curl($url, $postFields = null)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $postBodyString = "";
        $encodeArray = Array();
        $postMultipart = false;


        if (is_array($postFields) && 0 < count($postFields)) {

            foreach ($postFields as $k => $v) {
                if ("@" != substr($v, 0, 1)) //判断是不是文件上传
                {

                    $postBodyString .= "$k=" . $v . "&";
                    $encodeArray[$k] = $v;
                } else //文件上传用multipart/form-data,否则用www-form-urlencoded
                {
                    $postMultipart = true;
                    $encodeArray[$k] = new \CURLFile(substr($v, 1));
                }

            }
            unset ($k, $v);
            curl_setopt($ch, CURLOPT_POST, true);
            if ($postMultipart) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, $encodeArray);
            } else {
                curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1));
            }
        }

        if (!$postMultipart) {
            $headers = array('content-type: application/x-www-form-urlencoded;charset=utf-8');
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }

        $reponse = curl_exec($ch);

        if (curl_errno($ch)) {

            throw new Exception(curl_error($ch), 0);
        } else {
            $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if (200 !== $httpStatusCode) {
                throw new Exception($reponse, $httpStatusCode);
            }
        }

        curl_close($ch);
        return $reponse;
    }

 

 

 

重新更新后提示几点注意项

1 生成的公私钥为2048位,上传支付宝公钥自己保留私钥做数据加密

2 上传支付宝公钥后会出现两个公钥  一个是自己提供的 一个是支付宝端生成的,保留支付宝公钥,主要做回调验签使用

3 这次改好后就不需要再为支付宝无法验签发愁了。回调中调用 rsaCheck 将获取的数据原封不动的传入  就行了喔 是不是很方便   看图了解

  

 

 

 

忘记之前有没有做过测试  不过这次的测试是 mobile 直接过 没有问题

 

  1 <?php
  2 namespace pay;
  3 
  4 /**
  5  * 支付宝支付
  6  */
  7 class  Alipay
  8 {
  9     // 公私钥 2048 位
 10     private $config = [
 11         "app_id"=>"*****************",
 12         "rsaPrivateKey"=>"*******************************",
 13         "alipayPublicKey"=>"*********************",//支付宝公钥
 14        
 15     ];
 16 
 17     private $charset = 'utf-8';
 18     private $sign_type = 'RSA2';
 19     private $version = '1.0';
 20 
 21 
 22     private $calltype = 'app';//调用端口 app mobile pc
 23     private $submitUrl = "https://openapi.alipay.com/gateway.do" ;//提交地址
 24     // private $submitUrl = "https://openapi.alipaydev.com/gateway.do";//测试地址
 25     private $submit_auto = true;
 26 
 27     public function __construct($payTypeInfo=[]){
 28         if($payTypeInfo)
 29         {
 30             foreach ($payTypeInfo as $k => $val) {
 31                 if($k=='calltype')
 32                 {
 33                     $this->calltype=$val['val']!=''?$val['val']:$this->calltype;
 34                 }else{
 35                     // $this->config[$k]=$val['val'];
 36                 }
 37             } 
 38         }
 39     }
 40     public function index($data)
 41     {
 42         // 端口检测
 43         $res=$this->calltypeCheck($data);
 44         if($res['code']==0){ return $res; }
 45 
 46         $calltype=$this->calltype;
 47         return $this->$calltype($data);
 48     }
 49 
 50     public function app($data)
 51     {
 52         $str=$this->keyandvalStr($this->postdata($data),1);
 53           Flog($str);
 54         // $str=$this->submitUrl.'?'.$str;
 55         // //返回链接不能带官方https链接
 56         return ['code'=>1,'data'=>['type'=>'app','str'=>$str]];
 57     }
 58 
 59     public function pc($data)
 60     {
 61         $html=$this->formsubmithtml($this->submitUrl,$this->postdata($data));
 62         // var_dump($html);
 63         return ['code'=>1,'data'=>['type'=>'pc','html'=>$html]];
 64     }
 65 
 66     public function mobile($data)
 67     {
 68         // dump($data);
 69         $str=$this->keyandvalStr($this->postdata($data),1);
 70         $url="https://openapi.alipay.com/gateway.do?".$str;
 71         return ['code'=>1,'data'=>['type'=>'mobile','url'=>$url]];
 72     }
 73 
 74 
 75     // 获取数据
 76     public function postdata($data)
 77     {
 78         $calltypeD=$this->calltypeD();
 79 
 80         $return_url=$data['return_url'];
 81         $notify_url=$data['notify_url'];
 82 
 83         // 对subiect进行 & 删除
 84         $subject=str_replace("&","",$data['subject']);
 85 
 86         $biz_content=[
 87             'body'=>'',
 88             'subject'=>$subject,
 89             'out_trade_no'=>$data['pay_sn'],
 90             'timeout_express'=>'30m',
 91             'total_amount'=>$data['total'],
 92             'product_code'=>$calltypeD['product_code'],
 93             "goods_type"=>1,//商品类型 0虚拟商品 1实物商品
 94         ];
 95         $post=[
 96             'app_id'=>$this->config['app_id'],
 97             'method'=>$calltypeD['method'],
 98             'format'=>'json',
 99             'charset'=>$this->charset,
100             'sign_type'=>$this->sign_type,
101             'return_url'=>$return_url,
102             'sign'=>'',
103             'timestamp'=>date('Y-m-d H:i:s'),
104             'version'=>$this->version,
105             'notify_url'=>$notify_url,
106             'biz_content'=>json_encode($biz_content),
107         ];
108 
109         $signdata=$this->alisign($post);
110 
111         return $signdata;
112     }
113 
114     // 根据类型获取想要的信息
115     public function calltypeD($field='')
116     {
117         $D=[
118             'app'=>['product_code'=>'QUICK_MSECURITY_PAY','method'=>'alipay.trade.app.pay'],
119             'pc'=>['product_code'=>'FAST_INSTANT_TRADE_PAY','method'=>'alipay.trade.page.pay'],
120             'mobile'=>['product_code'=>'QUICK_WAP_WAY','method'=>'alipay.trade.wap.pay'],
121         ];
122         if($field!='')
123         {
124             return isset($D[$this->calltype])?$D[$this->calltype][$field]:'';
125         }else{
126             return isset($D[$this->calltype])?$D[$this->calltype]:[];
127         }
128 
129 
130     }
131 
132     // 端口检测
133     public function calltypeCheck($data)
134     {
135         // 对配置信息进行检测
136         if($this->config['app_id']=='') { return ['code'=>0,'msg'=>'请先配置 app_id']; }
137         if($this->config['rsaPrivateKey']=='') { return ['code'=>0,'msg'=>'请先配置 rsaPrivateKey']; }
138 
139         return ['code'=>1,'msg'=>'检测通过'];
140     }
141 
142 
143     /************************** 支付宝公用方法 ************************************/
144 
145     public function alisign($data)
146     {
147         $rsaPrivateKey=$this->config['rsaPrivateKey'];
148         $buff = "";
149         ksort($data);
150         $buff=$this->keyandvalStr($data);
151         $str  = chunk_split($rsaPrivateKey, 64, "\n");
152         $res = "-----BEGIN RSA PRIVATE KEY-----\n$str-----END RSA PRIVATE KEY-----\n";
153 
154         $sign=$this->ras2Sign($buff,$res);
155         $data['sign']=$sign;
156         return $data;
157     }
158     public function keyandvalStr($data,$flg=0)
159     {
160         $buff="";
161         foreach ($data as $k => $v) {
162             if($flg!=0 && $v != "" && !is_array($v))
163             {
164                 $v=urlencode($v);
165                 $buff .= $k . "=" . $v . "&";
166             }else{
167                 if($k != "sign" && $v != "" && !is_array($v)){
168                     $buff .= $k . "=" . $v . "&";
169                 }
170             }
171         }
172         $buff = trim($buff, "&");
173         return $buff;
174     }
175 
176     public function ras2Sign($buff,$res)
177     {
178         $sign='';
179         openssl_sign($buff, $sign, $res, OPENSSL_ALGO_SHA256);
180         $sign = base64_encode($sign);
181         return $sign;
182     }
183     public function formsubmithtml($action,$data)
184     {
185         $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$action."' method='POST'>";
186         foreach ($data as $key => $value) {
187             $val = str_replace("'","&apos;",$value);
188             $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
189         }
190         $sHtml = $sHtml."<input type='submit' class='paysubmit' value='ok' ></form>";
191         if($this->submit_auto==true)
192         {
193             $sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
194         }
195 
196         return $sHtml;
197     }
198 
199 
200 
201     /******************************回调验签*************************************/
202     public function rsaCheck($params)
203     {   
204         $gmt_create=$params['gmt_create'];
205         $config=$this->config;
206         $sign = $params['sign'];
207 
208         unset($params['sign']);
209         unset($params['sign_type']);
210         return $this->verify($this->getCheckSignContent($params), $sign, $config['alipayPublicKey'], $this->sign_type);
211     }
212 
213     function getCheckSignContent($params)
214     {
215         ksort($params);
216         $stringToBeSigned = "";
217         $i = 0;
218         foreach ($params as $k => $v) {
219             if ($i == 0) {
220                 $stringToBeSigned .= "$k" . "=" . "$v";
221             } else {
222                 $stringToBeSigned .= "&" . "$k" . "=" . "$v";
223             }
224             $i++;
225         }
226 
227         unset ($k, $v);
228         return $stringToBeSigned;
229     }
230 
231 
232 
233     function verify($data, $sign, $alipayPublicKey, $signType = 'RSA')
234     {
235         $res = "-----BEGIN PUBLIC KEY-----\n" .
236             wordwrap($alipayPublicKey, 64, "\n", true) .
237             "\n-----END PUBLIC KEY-----";
238 
239         ($res) or die('支付宝RSA公钥错误。请检查公钥文件格式是否正确');
240 
241         //调用openssl内置方法验签,返回bool值
242 
243         $result = FALSE;
244         if ("RSA2" == $signType) {
245             $result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1);
246         } else {
247             $result = (openssl_verify($data, base64_decode($sign), $res) === 1);
248         }
249 
250         return $result;
251     }
252 
264 }

 

posted on 2020-08-27 17:23  思君邪  阅读(471)  评论(0编辑  收藏  举报