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