前言:
前不久刚搞定了易宝支付,运营说,不用易宝支付了,改用连连支付。🆗🆗🆗 连连支付搞起~
对接过程:
一 在商户后台,下载连连提供的密钥生成工具。
获取公私密钥后,在商户后台配置公钥。
注意:按着里面的使用说明文档进行操作生成。我最开始使用秘钥生成批处理生成秘钥,即使用上图中的 私钥生成双击开始 来生成的密钥,结果一直对接不成功。之后用了公私钥校验工具,发现公私钥不匹配,然后按照 RSA秘钥生成工具使用说明 里面的步骤 生成的公私钥。
二 下载连连支付php-SDK,根据连连接口对接文档进行开发。
可以根据连连支付提供的SDK文件自行开发,也可以下载我个人整理的SDK文件,其中包含有官方提供的SDK,以及我个人对SDK的修改和调整之后使用的SDK文件,还有私钥生成工具。
// 商户在充值/消费交易模式场景下使用,通过该接口完成收银台创单,然后跳转至连连收银台完成支付。function test_tradeCreate(){ $user_id = "LLianPayTest-In-User-12345"; $current = date("YmdHis"); $params = new TradeCreateParams(); $current = date("YmdHis"); $params->timestamp = $current; $params->oid_partner = "商户号"; $params->txn_type = 'GENERAL_CONSUME'; $params->user_id = $user_id; $params->user_type = 'ANONYMOUS'; $params->notify_url = '回调地址'; $params->return_url = '完成支付后跳转地址'; $params->pay_expire = '120'; // 订单失效时间 $params->flag_chnl = 'H5'; // 交易发起渠道 $params->risk_item = json_encode(array( 'frms_ware_category' => '4007', 'user_info_mercht_userno' => 'LLianPayTest-In-User-12345', 'user_info_bind_phone' => '手机号', 'user_info_dt_register' => '注册时间', 'goods_name' => '数藏', 'user_info_full_name' => '姓名', 'user_info_id_type' => 0, 'user_info_id_no' => '身份证号', 'user_info_identify_state' => 1, 'user_info_identify_type' => 3, 'frms_client_chnl' => 'H5', 'frms_ip_addr' => "IP地址", 'user_auth_flag' => 1, )); // 风险控制参数(不同类别,风控参数不同,请自行调整) $orderInfo = new TradeCreateOrderInfo(); $orderInfo->txn_seqno = 'LLianPayTest' . $current; $orderInfo->txn_time = $current; $orderInfo->total_amount = '0.01'; $orderInfo->goods_name = "shuzicangping"; $orderInfo->order_info = '订单信息'; $params->orderInfo = $orderInfo; // 收款方信息 $m_payeeInfo = new TradeCreatePayeeInfo(); $m_payeeInfo->payee_id = "商户号"; $m_payeeInfo->payee_type = 'MERCHANT'; $m_payeeInfo->payee_amount = '0.01'; $params->payeeInfo = array($m_payeeInfo); // 付款方信息 $payerInfo['payer_type'] = "USER"; $payerInfo['payer_id'] = "LLianPayTest-In-User-12345"; $params->payerInfo = $payerInfo; // 测试环境地址 $url = 'https://accpgw-ste.lianlianpay-inc.com/v1/cashier/paycreate'; $result = LLianPayClient::sendRequest($url, json_encode($params)); echo $result;}test_tradeCreate();
class LLianPayClient{ static public function sendRequest($url, $content) { if (empty($url)) { log_write("[发送请求中]:请求URL非法,请核实!"."\n",'error'); } if (empty($content)) { log_write("[发送请求中]:请求参数非法,请核实!"."\n",'error'); } // 签名 $signVar = LLianPayAccpSignature::sign($content); log_write("请求签名值为:: " . $signVar."\n",'error'); log_write("请求参数为:: " . $content."\n",'error'); $sResult = LLianPayAccpSignature::getHttpResponseJSON($url, $content,$signVar); return $sResult; } }
class LLianPayAccpSignature{ public static function sign($data = '') { if (empty($data)) { log_write("[加签处理中]:待加签数据为空,请核实!"."\n",'error'); return False; } $private_key = self::_get_pem_content('/keys/merchant_private_key.pem'); if (empty($private_key)) { log_write("[加签处理中]:私钥错误,请核实!"."\n",'error'); return False; } $pkeyid = openssl_get_privatekey($private_key); if (empty($pkeyid)) { log_write("[加签处理中]:私钥错误,请核实!"."\n",'error'); return False; } // 对数据进行MD5处理 $md5Var = md5($data); // 使用数据的MD5值和私钥进行RSA加密 $verify = openssl_sign($md5Var, $signature, $pkeyid, OPENSSL_ALGO_MD5); $result = base64_encode($signature); log_write("[加签处理中],签名值为:" . $result."\n",'error'); return $result; } public static function checkSign($data = '', $signature = '') { if (empty($data) || empty($signature)) { log_write("[验签处理中]:待验签数据或签名值为空,请核实!"."\n",'error'); return False; } $public_key = self::_get_pem_content('/keys/llianpay_public_key.pem'); if (empty($public_key)) { log_write("[验签处理中]:验签公钥错误,请核实!"."\n",'error'); return False; } $pkeyid = openssl_get_publickey($public_key); if (empty($pkeyid)) { log_write("[验签处理中]:验签公钥错误,请核实!"."\n",'error'); return False; } // 对数据进行MD5处理 $md5Var = md5($data); log_write("[验签处理中]:待验签数据对应MD5值为:" . $md5Var."\n",'error'); // 使用数据的MD5值和签名值进行RSA校验 $ret = openssl_verify($md5Var, base64_decode($signature), $pkeyid, OPENSSL_ALGO_MD5); switch ($ret) { case 0: log_write("[验签处理中]:验签完成,验签结果为:错误"."\n",'error'); break; case 1: log_write("[验签处理中]:验签完成,验签结果为:正确"."\n",'error'); break; default: log_write("[验签处理中]:验签异常!"."\n",'error'); break; } return $ret; } // 发送post 请求 public static function getHttpResponseJSON($url, $para,$Signature) { // log_write($json,'error'); $curl = curl_init($url); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); //信任任何证书 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($curl, CURLOPT_POSTFIELDS, $para); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, array( 'timestamp' => date("YmdHis"), 'Content-type:application/json;charset=utf-8', 'Signature-Type:RSA', 'Signature-Data:'.$Signature )); $responseText = curl_exec($curl); log_write("返回值:".$responseText."\n",'error'); curl_close($curl); $result = json_decode($responseText,true); return $result; } // 使用连连公钥进行数据加密(用于绑定银行卡接口参数加密) public static function publicKeyEncrypt($data = '') { $public_key = self::_get_pem_content('/keys/llianpay_public_key.pem'); if (empty($public_key)) { log_write("[加签处理中]:公钥错误,请核实!"."\n",'error'); return False; } openssl_public_encrypt($data,$encrypted,$public_key); $encrypted = base64_encode($encrypted); return $encrypted; } private static function _get_pem_content($file_path) { return file_get_contents(dirname(__FILE__) . $file_path); }}
注意:
(1)注意调整相应参数,以及写日志方法
(2)传输参数类型为object对象;
(3)user_type 参数 要传 ANONYMOUS。
之前根据SDK,里面的用户类型默认:REGISTERED:注册用户,第一次支付的时候就会有报错:接口返回 2003 用户查询失败或不存在 。
三 回调
public function notify() { $data = file_get_contents("php://input"); $signature = \request()->header('Signature-Data'); // SDK 初始化文件加载 $llpaySubmit = new LLianPayAccpSignature(); $result = $llpaySubmit->checkSign($data,$signature); if($result){ // log_write('哈哈哈哈回调成功了--------','error'); $data = json_decode($data,true); // 订单支付成功业务处理 $status = $model->onPaySuccess($pay_type, ['transaction_id' => $data['order_no']]); // 清除未支付订单的url缓存 RedisLocal::delete_from_redis($data['order_no'].'llnomal_pay_url'); if ($status == false) { log_write('连连业务处理失败:'.$model->getError(),'error'); } }else{ log_write('验签名失败','error'); return 'error'; } return 'Success'; }
注意:
(1)发起支付调起时的回调地址一定要写对!这个至关重要!
(2)从接收到的数据的 header 中获取 Signature-Data 签名值 进行验签。
(3)如果回调不成功,可以通过写日志形式进行记录,便于查找出错点。
注意事项:
一 签约项目类型
要搞清楚与连连的签约类型,不同的签约项目有不同的请求接口,以及支付对接文档;
我上面对接的支付类型是 银行卡快捷支付。
二 参数类型
传参类型为object对象,而不是array数组!!!
这个真的是很坑很坑,因为目前项目中有连连支付的sdk,前期偷懒想着改改就可以了,谁知一直验签不成功,后来才发现,系统中的请求参数是数组类型的,就是因为这个原因一直不成功~无语至极@--@
三 如果使用连连支付的支付页面,要考虑储存调起支付后返回的网关地址
问题:使用连连支付,支付取消,返回订单列表,再次选择支付-连连支付,则提示“流水号重复,请勿重复提交。”
原因:请求连连支付后,此笔订单已生成相应的流水号,当返回再次选择连连支付,该订单已创建有流水账号,因此会有错误提示。
解决方案:订单状态为:未支付,点击去支付-连连支付。
(1)拿到请求连连接口返回的网关地址,先将网关地址记录到redis缓存中,并设置缓存失效时间;
(2)支付成功、关闭/取消订单操作中清除该地址缓存;
(3)在订单列表,判断如果订单状态为:未支付&&支付方式为:连连支付,则在该笔订单返回字段中增加 网关地址 字段;
(4)前端判断 未支付&&连连支付&&网关地址 则直接使用返回的 网关地址 字段,即可再次调起支付。
总结
以上就是我对接第三方连连支付的过程,以及对接中出现的问题。主要为了记录处理问题的方法,也希望能帮到有需要的同学们~
来源地址:https://blog.csdn.net/qq_32845825/article/details/127261286