zz 4 роки тому
батько
коміт
e69d799482
2 змінених файлів з 432 додано та 5 видалено
  1. 427 0
      app/Libs/Pay/Merchants/SandPay.php
  2. 5 5
      app/Libs/Pay/SandPay.php

+ 427 - 0
app/Libs/Pay/Merchants/SandPay.php

@@ -0,0 +1,427 @@
+<?php
+
+namespace App\Libs\Pay\Merchants;
+
+use Exception;
+use GuzzleHttp\Client;
+use Log;
+
+class SandPay
+{
+    const API_HOST = 'https://cashier.sandpay.com.cn/gateway/api/';
+    const API_TEST_HOST = 'http://61.129.71.103:8003/gateway/';
+    const NOTIFY_HOST = 'http://47.97.120.133:8094/';
+    const NOTIFY_TEST_HOST = 'http://managepre.aizhuishu.com/';
+
+    private $notify_url;
+    private $front_url;
+    private $pay_url;
+    private $query_order_url;
+    private $private_key;
+    private $public_key;
+    private $client;
+    private $mer_id;
+    private $app_id;
+
+    public function __construct(array $config)
+    {
+        $this->pay_url =  (self::API_HOST) . 'order/pay';
+        $this->query_order_url =  (self::API_HOST) . 'order/query';
+        $this->mer_id = $config['mer_id'];
+        $this->app_id = $config['app_id'];
+        $this->private_key = $this->loadPk12Cert(storage_path($config['private_key_path']), $config['cert_pwd']);
+        $this->public_key = $this->loadX509Cert(storage_path($config['public_key_path']));
+        $this->notify_url =   env('SANDPAY_CALL_BACK_URL');
+        //$this->notify_url =   (env('APP_ENV') == 'online' ? self::NOTIFY_HOST : self::NOTIFY_TEST_HOST) . 'pay/sandpay_back';
+        $this->front_url =   (env('APP_ENV') == 'online' ? self::NOTIFY_HOST : self::NOTIFY_TEST_HOST) . 'pay/sandpay_back';
+        $this->client = new Client();
+    }
+
+    public function notify(array $params)
+    {
+        $sign = $params['sign']; //签名
+        $signType = $params['signType']; //签名方式
+        $data = stripslashes($params['data']); //支付数据
+        $result = json_decode($data, true); //data数据
+
+        if ($this->verify($data, $sign, $this->public_key)) {
+            //签名验证成功
+            return $result;
+        } else {
+            //签名验证失败
+            myLog('sand_pay')->error('sign error: ' . json_encode($params));
+        }
+    }
+
+    public function send(array $data)
+    {
+        $params = [
+            'head' => [
+                'version' => '1.0',
+                'method' => 'sandpay.trade.pay',
+                'productId' => '00002020',
+                'accessType' => '1',
+                'mid' => $this->mer_id,
+                'channelType' => '07',
+                'reqTime' => date('YmdHis', time())
+            ],
+            'body' => [
+                'orderCode' => $data['trade_no'],
+                'totalAmount' => $this->amountFormat($data['price']/100),
+                'subject' => $data['body'],
+                'body' => $data['body'],
+                'payMode' => 'sand_wx',
+                'payExtra' => json_encode([
+                    'subAppid' => $this->app_id,
+                    'userId' => $data['openid'],
+                ]),
+                'clientIp' => _getIp(),
+                'notifyUrl' => $this->notify_url,
+                'frontUrl' => $this->front_url,
+                'extend' => ''
+            ]
+        ];
+        $result = '';
+        try{
+            $result = $this->sendPost($this->pay_url, $params);
+        }catch (\Exception $e){
+            Log::error('SandPay $request param is :');
+            Log::error($params);
+            Log::error('SandPay  error return  '.' pay_url: response '.$result);
+        }
+
+        $data = json_decode($result['data'], true);
+        if ($data['head']['respCode'] == "000000") {
+            $credential = json_decode($data['body']['credential'],true);
+            return json_decode($credential['params'],true);
+        } else {
+            myLog('sand_pay')->error('唤起支付失败: ' );
+            myLog('sand_pay')->error($result );
+            myLog('sand_pay')->error($params );
+            return [];
+        }
+    }
+    public function refund(array $data){
+        $params = [
+            'head'=>[
+                'version'=>'1.0',
+                'method'=>'sandpay.trade.refund',
+                'productId'=>'00002020',
+                'accessType'=>'1',
+                'mid'=>$this->mer_id,
+                'channelType' => '07',
+                'reqTime' => date('YmdHis')
+            ],
+            'body' => [
+                'orderCode' => $data['refund_no'],
+                'refundAmount' => $this->amountFormat($data['price']/100),
+                'oriOrderCode'=>$data['trade_no'],
+                'notifyUrl' => $data['callback'],
+                'refundReason' => '退货',
+                'extend' => isset($data['attach'])?$data['attach']:''
+            ]
+        ];
+        $url = 'https://cashier.sandpay.com.cn/gateway/api/order/refund';
+        $result = '';
+        try{
+            $result = $this->sendPost($url, $params);
+        }catch (\Exception $e){
+            Log::error('SandPay $request param is :');
+            Log::error($params);
+            Log::error('SandPay  error return  '.' pay_url: response '.$result);
+        }
+        return $result;
+    }
+
+    public function query(array $data){
+        $params = [
+            'head'=>[
+                'version'=>'1.0',
+                'method'=>'sandpay.trade.query',
+                'productId'=>'00002020',
+                'accessType'=>'1',
+                'mid'=>$this->mer_id,
+                'channelType' => '07',
+                'reqTime' => date('YmdHis')
+            ],
+            'body' => [
+                'orderCode' => $data['trade_no'],
+                'extend' => ''
+            ]
+        ];
+        $result = '';
+        $url = 'https://cashier.sandpay.com.cn/gateway/api/order/query';
+        try{
+            $result = $this->sendPost($url, $params);
+        }catch (\Exception $e){
+        }
+        return $result;
+    }
+
+    /**
+     * 格式金额
+     */
+    private function amountFormat(float $amount)
+    {
+        $amount = (string) (int) ($amount * 100);
+        $length = strlen($amount);
+        for ($i = 0; $i < 12 - $length; $i++) {
+            $amount = '0' . $amount;
+        }
+        return $amount;
+    }
+
+    /**
+     * 请求接口
+     * @param array $data
+     * @return array
+     */
+    private function sendPost(string $url, array $data)
+    {
+        // step1: 私钥签名
+        $sign = $this->sign($data, $this->private_key);
+
+        // step2: 拼接post数据
+        $post = array(
+            'charset' => 'utf-8',
+            'signType' => '01',
+            'data' => json_encode($data),
+            'sign' => $sign
+        );
+
+        // step3: post请求
+        $result = $this->http_post_json($url, $post);
+        $arr = $this->parse_result($result);
+
+        try {
+            //step4: 公钥验签
+            $this->verify($arr['data'], $arr['sign'], $this->public_key);
+            return $arr;
+        } catch (Exception $e) {
+            echo $e->getMessage();
+            return [];
+        }
+    }
+
+    private function parse_result($result)
+    {
+        $arr = array();
+        $response = urldecode($result);
+        $arrStr = explode('&', $response);
+        foreach ($arrStr as $str) {
+            $p = strpos($str, "=");
+            $key = substr($str, 0, $p);
+            $value = substr($str, $p + 1);
+            $arr[$key] = $value;
+        }
+
+        return $arr;
+    }
+
+    /**
+     * 发送请求
+     * @param $url
+     * @param $param
+     * @return array|null
+     * @throws Exception
+     */
+    private function http_post_json(string $url, array $param)
+    {
+        try {
+            $response = $this->client->post($url, ['form_params' => $param, 'Content-Type' => 'application/x-www-form-urlencoded']);
+            $content = $response->getBody()->getContents();
+            return $content;
+            /*$content = urldecode($content);
+            parse_str($content, $result);
+            return $result;*/
+        } catch (Exception $e) {
+            throw $e;
+        }
+    }
+
+    /**
+     * 获取公钥
+     * @param $path
+     * @return mixed
+     * @throws Exception
+     */
+    private function loadX509Cert($path)
+    {
+        try {
+            $file = file_get_contents($path);
+            if (!$file) {
+                throw new Exception('loadx509Cert::file_get_contents ERROR');
+            }
+
+            $cert = chunk_split(base64_encode($file), 64, "\n");
+            $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
+
+            $res = openssl_pkey_get_public($cert);
+            $detail = openssl_pkey_get_details($res);
+            openssl_free_key($res);
+
+            if (!$detail) {
+                throw new Exception('loadX509Cert::openssl_pkey_get_details ERROR');
+            }
+
+            return $detail['key'];
+        } catch (Exception $e) {
+            throw $e;
+        }
+    }
+
+    /**
+     * 获取私钥
+     * @param $path
+     * @param $pwd
+     * @return mixed
+     * @throws Exception
+     */
+    private function loadPk12Cert($path, $pwd)
+    {
+        try {
+            $file = file_get_contents($path);
+            if (!$file) {
+                throw new Exception('loadPk12Cert::file
+					_get_contents');
+            }
+
+            if (!openssl_pkcs12_read($file, $cert, $pwd)) {
+                throw new Exception('loadPk12Cert::openssl_pkcs12_read ERROR');
+            }
+            return $cert['pkey'];
+        } catch (Exception $e) {
+            throw $e;
+        }
+    }
+
+    /**
+     * 私钥签名
+     * @param $plainText
+     * @param $path
+     * @return string
+     * @throws Exception
+     */
+    function sign($plainText, $path)
+    {
+        $plainText = json_encode($plainText);
+        try {
+            $resource = openssl_pkey_get_private($path);
+            $result = openssl_sign($plainText, $sign, $resource);
+            openssl_free_key($resource);
+
+            if (!$result) {
+                throw new Exception('签名出错' . $plainText);
+            }
+
+            return base64_encode($sign);
+        } catch (Exception $e) {
+            throw $e;
+        }
+    }
+
+    /**
+     * 公钥验签
+     * @param $plainText
+     * @param $sign
+     * @param $path
+     * @return int
+     * @throws Exception
+     */
+    function verify($plainText, $sign, $path)
+    {
+        $resource = openssl_pkey_get_public($path);
+        $result = openssl_verify($plainText, base64_decode($sign), $resource);
+        openssl_free_key($resource);
+
+        if (!$result) {
+            throw new Exception('签名验证未通过,plainText:' . $plainText . '。sign:' . $sign, '02002');
+        }
+
+        return $result;
+    }
+
+    /**
+     * 公钥加密AESKey
+     * @param $plainText
+     * @param $puk
+     * @return string
+     * @throws Exception
+     */
+    function RSAEncryptByPub($plainText, $puk)
+    {
+        if (!openssl_public_encrypt($plainText, $cipherText, $puk, OPENSSL_PKCS1_PADDING)) {
+            throw new Exception('AESKey 加密错误');
+        }
+
+        return base64_encode($cipherText);
+    }
+
+    /**
+     * 私钥解密AESKey
+     * @param $cipherText
+     * @param $prk
+     * @return string
+     * @throws Exception
+     */
+    function RSADecryptByPri($cipherText, $prk)
+    {
+        if (!openssl_private_decrypt(base64_decode($cipherText), $plainText, $prk, OPENSSL_PKCS1_PADDING)) {
+            throw new Exception('AESKey 解密错误');
+        }
+
+        return (string) $plainText;
+    }
+
+    /**
+     * AES加密
+     * @param $plainText
+     * @param $key
+     * @return string
+     * @throws Exception
+     */
+    function AESEncrypt($plainText, $key)
+    {
+        $plainText = json_encode($plainText);
+        $result = openssl_encrypt($plainText, 'AES-128-ECB', $key, 1);
+
+        if (!$result) {
+            throw new Exception('报文加密错误');
+        }
+
+        return base64_encode($result);
+    }
+
+    /**
+     * AES解密
+     * @param $cipherText
+     * @param $key
+     * @return string
+     * @throws Exception
+     */
+    function AESDecrypt($cipherText, $key)
+    {
+        $result = openssl_decrypt(base64_decode($cipherText), 'AES-128-ECB', $key, 1);
+
+        if (!$result) {
+            throw new Exception('报文解密错误', 2003);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 生成AESKey
+     * @param $size
+     * @return string
+     */
+    function aes_generate($size)
+    {
+        $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+        $arr = array();
+        for ($i = 0; $i < $size; $i++) {
+            $arr[] = $str[mt_rand(0, 61)];
+        }
+        return implode('', $arr);
+    }
+}

+ 5 - 5
app/Libs/Pay/SandPay.php

@@ -9,11 +9,11 @@ class SandPay
 {
     const API_HOST = 'https://caspay.sandpay.com.cn/agent-main/openapi/';
     const API_TEST_HOST = 'http://61.129.71.103:7970/agent-main/openapi/';
-    const PUB_KEY_PATH = 'cert/sand.cer'; //公钥文件
-    const PRI_KEY_PATH = 'cert/RxCy1TfYDs8mwcLhH541695XpgOWOBxg.pfx'; //私钥文件
-    const CERT_PWD = 'RxCy1TfYDs8mwcLhH541695XpgOWOBxg'; //私钥证书密码
-    const MER_ID = 'S1631087';
-    const NOTIFY_HOST = 'http://my_domain/';
+    const PUB_KEY_PATH = 'cert/sandpay/sand.cer'; //公钥文件
+    const PRI_KEY_PATH = 'cert/sandpay/vPvWnHjBeQhHLebn.pfx'; //私钥文件
+    const CERT_PWD = 'vPvWnHjBeQhHLebn'; //私钥证书密码
+    const MER_ID = '6888804025954';
+    const NOTIFY_HOST = 'http://116.62.175.234/';
     const NOTIFY_TEST_HOST = 'http://managepre.aizhuishu.com/';
     const PUB_PRODUCT_ID = '00000003'; //代付对公:00000003
     const PRI_PRODUCT_ID = '00000004'; //代付对私:00000004