Alipay.php 14.0 KB
Newer Older
D
v1.2.0  
devil_gong 已提交
1 2 3 4
<?php
// +----------------------------------------------------------------------
// | ShopXO 国内领先企业级B2C免费开源电商系统
// +----------------------------------------------------------------------
D
devil_gong 已提交
5
// | Copyright (c) 2011~2019 http://shopxo.net All rights reserved.
D
v1.2.0  
devil_gong 已提交
6 7 8 9 10 11 12 13
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Devil
// +----------------------------------------------------------------------
namespace payment;

/**
14
 * 支付宝支付 - 新版本接口
D
v1.2.0  
devil_gong 已提交
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
 * @author   Devil
 * @blog    http://gong.gg/
 * @version 1.0.0
 * @date    2018-09-19
 * @desc    description
 */
class Alipay
{
    // 插件配置参数
    private $config;

    /**
     * 构造方法
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-09-17
     * @desc    description
     * @param   [array]           $params [输入参数(支付配置参数)]
     */
    public function __construct($params = [])
    {
        $this->config = $params;
    }

    /**
     * 配置信息
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-09-19
     * @desc    description
     */
    public function Config()
    {
        // 基础信息
        $base = [
            'name'          => '支付宝',  // 插件名称
53
            'version'       => '0.0.2',  // 插件版本
D
v1.2.0  
devil_gong 已提交
54
            'apply_version' => '不限',  // 适用系统版本描述
D
devil_gong 已提交
55
            'apply_terminal'=> ['pc','h5'], // 适用终端 默认全部 ['pc', 'h5', 'app', 'alipay', 'weixin', 'baidu']
56
            'desc'          => '***新版本接口*** 适用PC+H5,即时到帐支付方式,买家的交易资金直接打入卖家支付宝账户,快速回笼交易资金。 <a href="http://www.alipay.com/" target="_blank">立即申请</a>',  // 插件描述(支持html)
D
v1.2.0  
devil_gong 已提交
57 58 59 60 61 62 63 64 65 66
            'author'        => 'Devil',  // 开发者
            'author_url'    => 'http://shopxo.net/',  // 开发者主页
        ];

        // 配置信息
        $element = [
            [
                'element'       => 'input',
                'type'          => 'text',
                'default'       => '',
67 68 69
                'name'          => 'appid',
                'placeholder'   => '应用ID',
                'title'         => '应用ID',
D
v1.2.0  
devil_gong 已提交
70
                'is_required'   => 0,
71
                'message'       => '请填写应用ID',
D
v1.2.0  
devil_gong 已提交
72 73
            ],
            [
74 75 76 77
                'element'       => 'textarea',
                'name'          => 'rsa_public',
                'placeholder'   => '应用公钥',
                'title'         => '应用公钥',
D
v1.2.0  
devil_gong 已提交
78
                'is_required'   => 0,
79 80
                'rows'          => 6,
                'message'       => '请填写应用公钥',
D
v1.2.0  
devil_gong 已提交
81 82
            ],
            [
83 84 85 86 87 88 89 90 91 92 93 94 95
                'element'       => 'textarea',
                'name'          => 'rsa_private',
                'placeholder'   => '应用私钥',
                'title'         => '应用私钥',
                'is_required'   => 0,
                'rows'          => 6,
                'message'       => '请填写应用私钥',
            ],
            [
                'element'       => 'textarea',
                'name'          => 'out_rsa_public',
                'placeholder'   => '支付宝公钥',
                'title'         => '支付宝公钥',
D
v1.2.0  
devil_gong 已提交
96
                'is_required'   => 0,
97 98
                'rows'          => 6,
                'message'       => '请填写支付宝公钥',
D
v1.2.0  
devil_gong 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
            ],
        ];

        return [
            'base'      => $base,
            'element'   => $element,
        ];
    }

    /**
     * 支付入口
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-09-19
     * @desc    description
     * @param   [array]           $params [输入参数]
     */
    public function Pay($params = [])
    {
119 120 121 122 123 124 125 126 127 128 129 130 131
        // 参数
        if(empty($params))
        {
            return DataReturn('参数不能为空', -1);
        }
        
        // 配置信息
        if(empty($this->config))
        {
            return DataReturn('支付缺少配置', -1);
        }

        // 手机/PC
D
v1.2.0  
devil_gong 已提交
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
        if(IsMobile())
        {
            $ret = $this->PayMobile($params);
        } else {
            $ret = $this->PayWeb($params);
        }
        return $ret;
    }

    /**
     * [PayMobile wap手机支付]
     * @author   Devil
     * @blog     http://gong.gg/
     * @version  1.0.0
     * @datetime 2018-09-28T00:41:09+0800
     * @param   [array]           $params [输入参数]
     */
    private function PayMobile($params = [])
    {
151
        // 支付参数
D
v1.2.0  
devil_gong 已提交
152
        $parameter = array(
153 154 155 156 157 158 159 160 161
            'app_id'                =>  $this->config['appid'],
            'method'                =>  'alipay.trade.wap.pay',
            'format'                =>  'JSON',
            'charset'               =>  'utf-8',
            'sign_type'             =>  'RSA2',
            'timestamp'             =>  date('Y-m-d H:i:s'),
            'version'               =>  '1.0',
            'return_url'            =>  $params['call_back_url'],
            'notify_url'            =>  $params['notify_url'],
D
v1.2.0  
devil_gong 已提交
162
        );
163 164 165 166 167 168 169
        $biz_content = array(
            'product_code'          =>  'QUICK_WAP_WAY',
            'subject'               =>  $params['name'],
            'out_trade_no'          =>  $params['order_no'],
            'total_amount'          =>  $params['total_price'],
        );
        $parameter['biz_content'] = json_encode($biz_content, JSON_UNESCAPED_UNICODE);
D
v1.2.0  
devil_gong 已提交
170

171 172 173 174 175
        // 生成签名参数+签名
        $parameter['sign'] = $this->MyRsaSign($this->GetSignContent($parameter));
        
        // 输出执行form表单post提交
        exit($this->BuildRequestForm($parameter));
D
v1.2.0  
devil_gong 已提交
176 177
    }

178

D
v1.2.0  
devil_gong 已提交
179
    /**
180
     * [PayWeb PC支付]
D
v1.2.0  
devil_gong 已提交
181 182 183
     * @author   Devil
     * @blog     http://gong.gg/
     * @version  1.0.0
184
     * @datetime 2018-09-28T00:23:04+0800
D
v1.2.0  
devil_gong 已提交
185 186
     * @param   [array]           $params [输入参数]
     */
187
    private function PayWeb($params = [])
D
v1.2.0  
devil_gong 已提交
188
    {
189
        // 支付参数
D
v1.2.0  
devil_gong 已提交
190
        $parameter = array(
191 192 193 194 195 196 197 198 199 200 201 202
            'app_id'                =>  $this->config['appid'],
            'method'                =>  'alipay.trade.page.pay',
            'format'                =>  'JSON',
            'charset'               =>  'utf-8',
            'sign_type'             =>  'RSA2',
            'timestamp'             =>  date('Y-m-d H:i:s'),
            'version'               =>  '1.0',
            'return_url'            =>  $params['call_back_url'],
            'notify_url'            =>  $params['notify_url'],
        );
        $biz_content = array(
            'product_code'          =>  'FAST_INSTANT_TRADE_PAY',
D
v1.2.0  
devil_gong 已提交
203 204
            'subject'               =>  $params['name'],
            'out_trade_no'          =>  $params['order_no'],
205
            'total_amount'          =>  $params['total_price'],
D
v1.2.0  
devil_gong 已提交
206
        );
207 208 209 210
        $parameter['biz_content'] = json_encode($biz_content, JSON_UNESCAPED_UNICODE);

        // 生成签名参数+签名
        $parameter['sign'] = $this->MyRsaSign($this->GetSignContent($parameter));
D
v1.2.0  
devil_gong 已提交
211
        
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
        // 输出执行form表单post提交
        exit($this->BuildRequestForm($parameter));
    }

    

    /**
     * 支付回调处理
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-09-19
     * @desc    description
     * @param   [array]           $params [输入参数]
     */
    public function Respond($params = [])
    {
        $data = empty($_POST) ? $_GET :  array_merge($_GET, $_POST);
        ksort($data);
D
v1.2.0  
devil_gong 已提交
231

232 233 234
        // 参数字符串
        $prestr = '';
        foreach($data AS $key=>$val)
D
v1.2.0  
devil_gong 已提交
235
        {
236 237 238 239
            if ($key != 'sign' && $key != 'sign_type' && $key != 'code')
            {
                $prestr .= "$key=$val&";
            }
D
v1.2.0  
devil_gong 已提交
240
        }
241 242 243 244
        $prestr = substr($prestr, 0, -1);

        // 签名
        if(!$this->OutRsaVerify($prestr, $data['sign']))
D
devil_gong 已提交
245
        {
246
            return DataReturn('签名校验失败', -1);
D
devil_gong 已提交
247
        }
D
v1.2.0  
devil_gong 已提交
248

249 250
        // 支付状态
        if(!empty($data['trade_no']) || (isset($data['trade_status']) && in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])))
D
v1.2.0  
devil_gong 已提交
251
        {
252
            return DataReturn('支付成功', 0, $this->ReturnData($data));
D
v1.2.0  
devil_gong 已提交
253
        }
254
        return DataReturn('处理异常错误', -100);
D
v1.2.0  
devil_gong 已提交
255 256 257
    }

    /**
258
     * [ReturnData 返回数据统一格式]
D
v1.2.0  
devil_gong 已提交
259 260 261
     * @author   Devil
     * @blog     http://gong.gg/
     * @version  1.0.0
262 263
     * @datetime 2018-10-06T16:54:24+0800
     * @param    [array]                   $data [返回数据]
D
v1.2.0  
devil_gong 已提交
264
     */
265
    private function ReturnData($data)
D
v1.2.0  
devil_gong 已提交
266
    {
267 268 269 270 271 272
        // 返回数据固定基础参数
        $data['trade_no']       = $data['trade_no'];        // 支付平台 - 订单号
        $data['buyer_user']     = $data['seller_id'];       // 支付平台 - 用户
        $data['out_trade_no']   = $data['out_trade_no'];    // 本系统发起支付的 - 订单号
        $data['subject']        = isset($data['subject']) ? $data['subject'] : ''; // 本系统发起支付的 - 商品名称
        $data['pay_price']      = $data['total_amount'];    // 本系统发起支付的 - 总价
D
v1.2.0  
devil_gong 已提交
273

274
        return $data;
D
v1.2.0  
devil_gong 已提交
275 276 277
    }

    /**
278
     * 建立请求,以表单HTML形式构造(默认)
D
v1.2.0  
devil_gong 已提交
279
     * @author   Devil
280 281 282 283 284 285
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2019-03-15
     * @desc    description
     * @param   [array]          $params [请求参数数组]
     * @return  [string]                 [提交表单HTML文本]
D
v1.2.0  
devil_gong 已提交
286
     */
287
    private function BuildRequestForm($params)
D
v1.2.0  
devil_gong 已提交
288
    {
289 290
        $html = "<form id='alipaysubmit' name='alipaysubmit' action='https://openapi.alipay.com/gateway.do?charset=utf-8' method='POST'>";
        while(list ($key, $val) = each($params))
D
v1.2.0  
devil_gong 已提交
291
        {
292 293 294 295 296
            if(!empty($val))
            {
                $val = str_replace("'", "&apos;", $val);
                $html .= "<input type='hidden' name='".$key."' value='".$val."'/>";
            }
D
v1.2.0  
devil_gong 已提交
297 298
        }

299 300 301 302 303 304
        //submit按钮控件请不要含有name属性
        $html .= "<input type='submit' value='ok' style='display:none;''></form>";
        
        $html .= "<script>document.forms['alipaysubmit'].submit();</script>";
        
        return $html;
D
v1.2.0  
devil_gong 已提交
305 306 307
    }

    /**
308
     * 获取签名内容
D
v1.2.0  
devil_gong 已提交
309 310 311
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
312
     * @date    2019-03-15
D
v1.2.0  
devil_gong 已提交
313
     * @desc    description
314
     * @param   [array]          $params [需要签名的参数]
D
v1.2.0  
devil_gong 已提交
315
     */
316
    public function GetSignContent($params)
D
v1.2.0  
devil_gong 已提交
317
    {
318 319 320 321
        ksort($params);
        $string = "";
        $i = 0;
        foreach($params as $k => $v)
D
v1.2.0  
devil_gong 已提交
322
        {
323
            if(!empty($v) && "@" != substr($v, 0, 1))
D
v1.2.0  
devil_gong 已提交
324
            {
325 326 327 328
                if ($i == 0) {
                    $string .= "$k" . "=" . "$v";
                } else {
                    $string .= "&" . "$k" . "=" . "$v";
D
v1.2.0  
devil_gong 已提交
329
                }
330
                $i++;
D
v1.2.0  
devil_gong 已提交
331 332
            }
        }
333 334 335
        unset($k, $v);
        return $string;
    }
D
v1.2.0  
devil_gong 已提交
336

337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
    /**
     * [MyRsaSign 签名字符串]
     * @author   Devil
     * @blog     http://gong.gg/
     * @version  1.0.0
     * @datetime 2017-09-24T08:38:28+0800
     * @param    [string]                   $prestr [需要签名的字符串]
     * @return   [string]                           [签名结果]
     */
    private function MyRsaSign($prestr)
    {
        $res = "-----BEGIN RSA PRIVATE KEY-----\n";
        $res .= wordwrap($this->config['rsa_private'], 64, "\n", true);
        $res .= "\n-----END RSA PRIVATE KEY-----";
        return openssl_sign($prestr, $sign, $res, OPENSSL_ALGO_SHA256) ? base64_encode($sign) : null;
    }

    /**
     * [MyRsaDecrypt RSA解密]
     * @author   Devil
     * @blog     http://gong.gg/
     * @version  1.0.0
     * @datetime 2017-09-24T09:12:06+0800
     * @param    [string]                   $content [需要解密的内容,密文]
     * @return   [string]                            [解密后内容,明文]
     */
    private function MyRsaDecrypt($content)
    {
        $res = "-----BEGIN PUBLIC KEY-----\n";
        $res .= wordwrap($this->config['rsa_public'], 64, "\n", true);
        $res .= "\n-----END PUBLIC KEY-----";
        $res = openssl_get_privatekey($res);
        $content = base64_decode($content);
        $result  = '';
        for($i=0; $i<strlen($content)/128; $i++)
D
v1.2.0  
devil_gong 已提交
372
        {
373 374 375
            $data = substr($content, $i * 128, 128);
            openssl_private_decrypt($data, $decrypt, $res, OPENSSL_ALGO_SHA256);
            $result .= $decrypt;
D
v1.2.0  
devil_gong 已提交
376
        }
377 378 379
        openssl_free_key($res);
        return $result;
    }
D
v1.2.0  
devil_gong 已提交
380

381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
    /**
     * [OutRsaVerify 支付宝验证签名]
     * @author   Devil
     * @blog     http://gong.gg/
     * @version  1.0.0
     * @datetime 2017-09-24T08:39:50+0800
     * @param    [string]                   $prestr [需要签名的字符串]
     * @param    [string]                   $sign   [签名结果]
     * @return   [boolean]                          [正确true, 错误false]
     */
    private function OutRsaVerify($prestr, $sign)
    {
        $res = "-----BEGIN PUBLIC KEY-----\n";
        $res .= wordwrap($this->config['out_rsa_public'], 64, "\n", true);
        $res .= "\n-----END PUBLIC KEY-----";
        $pkeyid = openssl_pkey_get_public($res);
        $sign = base64_decode($sign);
        if($pkeyid)
D
v1.2.0  
devil_gong 已提交
399
        {
400 401
            $verify = openssl_verify($prestr, $sign, $pkeyid, OPENSSL_ALGO_SHA256);
            openssl_free_key($pkeyid);
D
v1.2.0  
devil_gong 已提交
402
        }
403
        return (isset($verify) && $verify == 1) ? true : false;
D
v1.2.0  
devil_gong 已提交
404 405
    }

406 407
     /**
     * [SyncRsaVerify 同步返回签名验证]
D
v1.2.0  
devil_gong 已提交
408 409 410
     * @author   Devil
     * @blog     http://gong.gg/
     * @version  1.0.0
411
     * @datetime 2017-09-25T13:13:39+0800
D
v1.2.0  
devil_gong 已提交
412
     * @param    [array]                   $data [返回数据]
413
     * @param    [boolean]                 $key  [数据key]
D
v1.2.0  
devil_gong 已提交
414
     */
415
    private function SyncRsaVerify($data, $key)
D
v1.2.0  
devil_gong 已提交
416
    {
417 418
        $string = json_encode($data[$key], JSON_UNESCAPED_UNICODE);
        return $this->OutRsaVerify($string, $data['sign']);
D
v1.2.0  
devil_gong 已提交
419 420 421
    }
}
?>