'RESP_SIGN_VERIFY_UNDO', 'msg' => '无法对应答数据进行验签', ); } if (!self::$rspDatas) { # 无法解析出json格式的情况,就直接就把应答数据返回出去,针对返回数据为html页面的接口,例如:页面版的快捷支付等 self::$rspDatas = $resultString; } return self::$error;; } $resp_data = isset(self::$rspDatas['data']) ? self::$rspDatas['data'] : ''; // TODO response 应答错误 hardcode 待优化 --- Charles.huang 2021/09/09 # 对返回数据验签失败的逻辑分支 if (!self::verifySign_sort($resp_sign, $resp_data, self::$public_key)) { self::$error = array( 'code' => 'RESP_SIGN_VERIFY_FAILED', 'msg' => '接口结果返回签名验证失败', ); return self::$error; } # 应答码异常情况 if (self::$httpStateCode < 200 || self::$httpStateCode >= 400) { self::$error = array( 'code' => 'HTTP_REQUEST_FAILED', 'msg' => "请求失败: HTTP_STATE_CODE-".self::$httpStateCode, ); return self::$error; } return self::$rspDatas; } private static function createHeaders($header_data = array()){ $headers = $header_data; if (empty($header_data)){ $headers = array('Content-type: application/x-www-form-urlencoded'); } array_push($headers, 'sdk_version:' .self::$SDK_VERSION); array_push($headers, 'charset:UTF-8'); return $headers; } private static function createBody($post_data, $file = null){ $body = array(); $body['sys_id'] = self::$sys_id; $body['product_id'] =self::$product_id; ksort($post_data); // 根据key排序 $body['data'] = $post_data; # 执行签名 $sign = self::sha_with_rsa_sign( json_encode($post_data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE), // 数据里有中文和斜杠都不转码 self::$rsa_merch_private_key); $body['sign'] = $sign; if(!empty($file)){ $body['file'] = $file; $body['data'] = json_encode($post_data, JSON_UNESCAPED_UNICODE); } return $body; } /** * 本次请求的http头信息 */ public static function getHttpHeaders() { return self::$httpHeaders; } /** * 本次请求应答的 http状态码 */ public static function getHttpStateCode() { return self::$httpStateCode; } /** * 本次请求的数据 */ public static function getReqDatas() { return self::$reqDatas; } /** * 本次请求的应答数据 */ public static function getRspDatas() { return self::$rspDatas; } /** * 获取请求执行失败信息 */ public static function getErrorInfo() { return self::$error; } /** * 请求是否成功执行 */ public static function isError() { return self::$error != null; } public static function wxJsPay($OpenId,$order_id,$order_amount,$order_name,$attach='product') { $order_amount = bcmul($order_amount,'1',2); $data['time_expire'] = date('YmdHis',time()+60); $data['req_date'] = date('Ymd'); $data['req_seq_id'] = $order_id; $data['huifu_id'] = self::$sys_id; $data['goods_desc'] = $order_name; $data['trans_amt'] = $order_amount; $data['trade_type'] = 'T_MINIAPP'; $data['wx_data']['attach'] = $attach; if($OpenId) { $data['wx_data']['sub_appid'] = 'wx9d53e46a38eda4b9'; $data['wx_data']['sub_openid'] = $OpenId; } if($attach=='user_recharge') { $huifu_id = '6666000158522222'; $rs['acct_infos'] = [ [ 'huifu_id' => $huifu_id, 'percentage_div' => '100.00' ], [ 'huifu_id' => self::$sys_id, 'percentage_div' => '0.00' ] ]; $rs['percentage_flag'] = 'Y'; $rs['is_clean_split'] = 'Y'; $data['acct_split_bunch'] = json_encode($rs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } elseif($attach=='product') { $order1 = explode('_',$order_id); if(sizeof($order1)==2) $order_id = $order1[1]; $store_id = VideoOrder::where('out_trade_no', $order_id)->value('store_id'); } $data['notify_url'] = self::$notify_url."/weixin"; $result = self::curlRequest(self::pay,$data); return $result; if (!$result || self::isError()) { //失败处理 var_dump(self::getErrorInfo()); } else { //成功处理 var_dump($result); } } public static function Ali_NATIVE($order_id,$order_amount,$order_name,$attach='product') { $order_amount = bcmul($order_amount,'1',2); $data['pay_scene'] = '02'; $data['time_expire'] = date('YmdHis',time()+60); $data['req_date'] = date('Ymd'); $data['req_seq_id'] = $order_id; $data['huifu_id'] = self::$sys_id; $data['goods_desc'] = $order_name; $data['trans_amt'] = $order_amount; $data['trade_type'] = 'A_NATIVE'; $data['alipay_data']['buyer_id'] = self::$buyer_id; $data['notify_url'] = self::$notify_url."/alipay"; $result = self::curlRequest(self::pay,$data); if (!$result || self::isError()) { //失败处理 var_dump(self::getErrorInfo()); } else { //成功处理 var_dump($result); } } public static function micropay($auth_code,$order_id,$order_amount,$order_name,$attach='product') { $order_amount = bcmul($order_amount,'1',2); $data['pay_scene'] = '02'; $data['time_expire'] = date('YmdHis',time()+60); $data['req_date'] = date('Ymd'); $data['req_seq_id'] = $order_id; $data['huifu_id'] = self::$sys_id; $data['goods_desc'] = $order_name; $data['trans_amt'] = $order_amount; $data['auth_code'] = $auth_code; if($attach=='user_recharge') { $huifu_id = '6666000158522222'; $rs['acct_infos'] = [ [ 'huifu_id' => $huifu_id, 'percentage_div' => '100.00' ], [ 'huifu_id' => self::$sys_id, 'percentage_div' => '0.00' ] ]; $rs['percentage_flag'] = 'Y'; $rs['is_clean_split'] = 'Y'; $data['acct_split_bunch'] = json_encode($rs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } elseif($attach=='product') { $order1 = explode('_',$order_id); if(sizeof($order1)==2) $order_id = $order1[1]; $store_id = StoreOrder::where('order_id', $order_id)->value('store_id'); if ($store_id > 0) { $huifu_id = \app\model\store\SystemStore::where('id', $store_id)->value('huifu_id'); if ($huifu_id) { $rs['acct_infos'] = [ [ 'huifu_id' => $huifu_id, 'percentage_div' => '100.00' ], [ 'huifu_id' => self::$sys_id, 'percentage_div' => '0.00' ] ]; $rs['percentage_flag'] = 'Y'; $rs['is_clean_split'] = 'Y'; $data['acct_split_bunch'] = json_encode($rs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } } else { } } $data['risk_check_data']['ip_addr'] = app()->request->ip(); $data['notify_url'] = self::$notify_url."/micropay?attach=".$attach; @file_put_contents("micropay.txt",json_encode($data)."\r\n"); $rs = self::curlRequest(self::micropay,$data); @file_put_contents("micropay.txt",json_encode($rs),FILE_APPEND); if($rs['data']['resp_code']=='00000000' && $rs['data']['trans_stat']=='S') { (new Hook(PayNotifyServices::class, 'huifu'))->listen($attach, $order_id, $rs['data']['out_trans_id'],$rs['data']['req_seq_id']); return $rs; }elseif($rs['data']['resp_code']=='00000100') { return $rs; } else { self::$error = $rs['data']['resp_desc']; return false; } } public static function refund($order_id,$pay_time,$refund_amount,$refund_reason,$attach="product") { $data['req_date'] = date('Ymd'); $data['req_seq_id'] = 'ref'.uniqid(true); $data['huifu_id'] = self::$sys_id; $data['ord_amt'] = bcmul($refund_amount,'1',2); $data['remark'] = $refund_reason; $data['org_req_date'] = date("Ymd",$pay_time); $data['org_req_seq_id'] = $order_id; $data['notify_url'] = self::$refund_notify_url."/micropay?attach=".$attach; $result = self::curlRequest(self::refund,$data); @file_put_contents("hf1.txt", json_encode($result)); if(($result['data']['trans_stat']=='S' || $result['data']['trans_stat']=='P')) { return true; } else { return false; } } public static function miniapp_pay($OpenId,$order_id,$order_amount,$order_name,$attach='product') { //$data['time_expire'] = date('YmdHis',time()+120); $order_amount = bcmul($order_amount,'1',2); $data['req_date'] = date('Ymd'); $data['req_seq_id'] = $order_id; $data['huifu_id'] = self::$sys_id; $data['goods_desc'] = $order_name; $data['trade_type'] = 'T_MINIAPP'; $data['trans_amt'] = $order_amount; if($OpenId) { $data['wx_data']['sub_appid'] = 'wx9d53e46a38eda4b9'; $data['wx_data']['sub_openid'] = $OpenId; } $data['openid'] = $OpenId; if($attach=='user_recharge') { $huifu_id = '6666000158522222'; $rs['acct_infos'] = [ [ 'huifu_id' => $huifu_id, 'percentage_div' => '100.00' ], [ 'huifu_id' => self::$sys_id, 'percentage_div' => '0.00' ] ]; $rs['percentage_flag'] = 'Y'; $rs['is_clean_split'] = 'Y'; $data['acct_split_bunch'] = json_encode($rs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } elseif($attach=='product') { $order1 = explode('_',$order_id); if(sizeof($order1)==2) $order_id = $order1[1]; $store_id = StoreOrder::where('order_id', $order_id)->value('store_id'); if ($store_id > 0) { $huifu_id = \app\model\store\SystemStore::where('id', $store_id)->value('huifu_id'); if ($huifu_id) { $rs['acct_infos'] = [ [ 'huifu_id' => $huifu_id, 'percentage_div' => '100.00' ], [ 'huifu_id' => self::$sys_id, 'percentage_div' => '0.00' ] ]; $rs['percentage_flag'] = 'Y'; $rs['is_clean_split'] = 'Y'; $data['acct_split_bunch'] = json_encode($rs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } } else { } } $data['notify_url'] = self::$notify_url."/weixin?attach=".$attach; @file_put_contents("miniapp_pay.txt", json_encode($data)); $result = self::curlRequest(self::pay,$data); return $result; } public static function notify( callable $notifyFn) { if(input('resp_code')!='00000000') return false; $data = json_decode(input('resp_data'),true); //商户订单号 $postOrder['out_trade_no'] = $data['req_seq_id']; $postOrder['req_seq_id'] = $data['req_seq_id']; //支付宝交易号 $postOrder['trade_no'] = $data['out_trans_id']; $postOrder['out_trans_id'] = $data['out_trans_id']; //交易状态 $postOrder['trade_status'] = $data['trans_stat']; //备注 $postOrder['attach'] = input('attach','product'); if($data['trans_stat']=='S') { try { if ($notifyFn((object)$postOrder)) { return 'SUCCESS'; } } catch (\Exception $e) { Log::error('辉聚异步会回调成功,执行函数错误。错误单号:' . $postOrder['out_trade_no'] . PHP_EOL . $e->getMessage()); } } return 'FAIL'; } /** * 私钥加签(对数据源排序),可用于 V2 版本接口数据加签 * * @param @param array $data 原数据( 排序后的json字符串; 数组参数排序后转json字符串(数据的中文和斜杠均不转码): ksort($post_data); json_encode($post_data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); ) * @param string $rsaPrivateKey 私钥 * @param int $alg 默认 OPENSSL_ALGO_SHA256 * * @return string 签名串 */ public static function sha_with_rsa_sign($data, $rsaPrivateKey, $alg=OPENSSL_ALGO_SHA256){ $key = "-----BEGIN PRIVATE KEY-----\n".wordwrap($rsaPrivateKey, 64, "\n", true)."\n-----END PRIVATE KEY-----"; $signature= ''; try { openssl_sign($data, $signature, $key, $alg); } catch (\Exception $e) { echo $e->getMessage(); } return base64_encode($signature); } public static function encrypt_with_rsa_pubkey($data, $rsaPublicKey, $padding=OPENSSL_PKCS1_PADDING){ $key = "-----BEGIN PUBLIC KEY-----\n".wordwrap($rsaPublicKey, 64, "\n", true)."\n-----END PUBLIC KEY-----"; $encryptResult= ''; try { openssl_public_encrypt($data, $encryptResult, $key, $padding); } catch (\Exception $e) { echo $e->getMessage(); } return base64_encode($encryptResult); } /** * 使用公钥验签,可用于异步应答验签 * * @param string $signature 签文 * @param string $data 原数据(string) * @param string $rsaPublicKey 公钥 * @param int $alg 默认 OPENSSL_ALGO_SHA256 * * @return false|int 验证结果:成功/失败 */ public static function verifySign($signature, $data, $rsaPublicKey, $alg=OPENSSL_ALGO_SHA256){ $key = "-----BEGIN PUBLIC KEY-----\n".wordwrap($rsaPublicKey, 64, "\n", true)."\n-----END PUBLIC KEY-----"; return openssl_verify($data, base64_decode($signature), $key, $alg); } /** * 使用公钥验签(对数据源排序),可用于 V2 版本接口返回数据验签 * * @param string $signature 签文 * @param array $data 原数据(array) * @param string $rsaPublicKey 公钥 * @param int $alg 默认 OPENSSL_ALGO_SHA256 * * @return false|int 验证结果:成功/失败 */ public static function verifySign_sort($signature, $data, $rsaPublicKey, $alg=OPENSSL_ALGO_SHA256){ $key = "-----BEGIN PUBLIC KEY-----\n".wordwrap($rsaPublicKey, 64, "\n", true)."\n-----END PUBLIC KEY-----"; ksort($data); $data = json_encode($data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); $data = str_replace("\n","\\n",$data); $data = str_replace('"enter_fee":0,','"enter_fee":0.00,',$data); // 单独替换指定类型格式不正确的字段 如 enter_fee return openssl_verify($data, base64_decode($signature), $key, $alg); } public static function checkEmpty($value) { return !isset($value) || trim($value) === ""; } public static function endWith($str, $suffix) { $length = strlen($suffix); if($length == 0){ return false; } return (substr($str, -$length) === $suffix); } /** * 校验 webhook 返回报文签名 * * @param string $signature 签文 * @param array $data 原数据(array) * @param string $key 加签 key * @param int $alg 默认 OPENSSL_ALGO_SHA256 * * @return true|false 验证结果:成功/失败 */ public static function verify_webhook_sign($signature, $data, $key){ $sign = md5(json_encode($data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).$key); return $sign == strtolower($signature); } }