支付下单接口
国内:https://18pay.net/pay.html
海外:http://www.18pay.net/pay.html
提示:安全起见,提交方式推荐使用 POST ,请不要用 GET 方式提交。
| 参数名称 | 参数含义 | 类型 | 示例值 | 是否必填 | 参与签名 | 描述 |
|---|---|---|---|---|---|---|
| appid | 商户编号 | string | 20206491 | 是 | 是 | 平台分配商户编号 |
| payid | 唯一标识 | string | 202305310100203715872 | 是 | 是 | 订单编号或者用户ID(必须确保是唯一的) |
| money | 订单金额 | string | 10 | 是 | 是 | 订单提交的金额(单位:元) |
| type | 支付方式 | string | 1 | 是 | 是 | 传入数字(1为支付宝,2为微信支付) |
| notify_url | 异步通知地址 | string | http://296o.com/notify_url.php | 是 | 是 | 接收平台通知的URL,需给绝对路径,并确保平台能通过互联网访问该地址 |
| return_url | 同步跳转地址 | string | http://296o.com/return_url.php | 是 | 是 | 前端页面跳转的URL(支付成功后会跳转到这个地址), 该地址只作为前端页面的一个跳转,须使用notify_url通知作为支付最终结果 |
| subject | 商品名称 | string | 购买商品 | 是 | 是 | 商品说明,游戏充值可传玩家账号或者角色名 |
| client_ip | 客户端IP | string | 114.114.114.114 | 是 | 是 | 请正确提交用户的真实IP,否则会出现"支付IP地址异常"问题 |
| param | 自定义参数 | string | 没有请留空 | 否 | 是 | 原封返回,避免特殊字符 |
| account | 充值账号 | string | qq1234 | 否 | 是 | 接口用于游戏充值时,可传入玩家账号 |
| contact | 联系方式 | string | 79899080 | 否 | 是 | 接口用于游戏充值时,可传入玩家联系方式 |
| native | 原生接口 | string | true | 否 | 是 | native不为空时,接口返回原生支付结果,比如支付宝和微信的官方二维码链接 |
| hidetitle | 隐藏标题 | string | true | 否 | 是 | hidetitle不为空时,自动隐藏支付页面上的"支付中心"标题栏 |
| codesize | 二维码大小 | int | 150 | 否 | 是 | 二维码大小默认200px |
| agent | 代理账号 | string | 88888 | 否 | 是 | 支持三级代理分销,直接传入代理的登录账号 |
| sign | 数据签名 | string | fa7337122f6082b6ac14433ecf8ec73a | 是 | 否 | 对数据进行签名,请参考签名算法 |
| sign_type | 签名类型 | string | rsa | 否 | 否 | 默认md5(V1.0),可选rsa(V2.0) |
| charset | 编码 | string | utf-8 | 否 | 否 | utf-8或gb2312,默认utf-8 |
重要提示:平台现已支持两种签名算法版本!
• MD5签名(V1.0):传统签名方式,默认使用;
• RSA签名(V2.0):强烈推荐使用,更安全、更高效;
使用V2.0需要在基本资料中配置RSA密钥,并将 sign_type 参数设为 rsa
▍ MD5签名算法(V1.0 - 默认)
-
第一步
设所有发送或者接收到的数据为集合,将集合内非空参数值的参数按照参数名从小到大排序(ASCII码字典序),使用 URL 键值对的格式(即key1=value1&key2=value2…)拼接成字符串 str 。
$postDatas = array( "appid" => '20206491', "money" => '10', "payid" => '202305310100203715872', "type" => '1', "subject" => '购买商品', "notify_url" => 'http://296o.com/notify_url.php', "return_url" => 'http://296o.com/return_url.php' ); //ksort()对数组按照键名进行升序排序 ksort($postDatas); //reset()内部指针指向数组中的第一个元素 reset($postDatas); $str = ASCII($postDatas); function ASCII($postDatas = array()){ $str = '';//初始化 foreach ($postDatas AS $key => $val) { //遍历参数数组 if ($val == ''||$key == 'sign') continue; //跳过这些不签名 if ($str) $str .= '&'; //第一个字符串签名不加& 其他加&连接起来参数 $str .= "$key=$val"; //拼接为url参数形式 } return $str; } -
第二步
然后在字符串 str 后面拼接上 appkey(平台商户密钥),得到 stringSignTemp 字符串,最后对stringSignTemp 进行 MD5 运算,得到 sign 值
$str = "appid=20206491&money=10&notify_url=http://296o.com/notify_url.php&payid=202305310100203715872&return_url=http://296o.com/return_url.php&subject=购买商品&type=1"; $stringSignTemp = $str.$appkey; $sign = md5($stringSignTemp);
▍ RSA签名算法(V2.0 - 强烈推荐)
RSA签名流程(2密钥模式):
1. 商户自行生成RSA密钥对,将商户公钥配置到平台,商户私钥自己保管
2. 从平台"基本资料"获取平台RSA公钥,用于验证异步通知签名
3. 发起请求时,用商户私钥进行RSA签名
4. 异步通知时,平台用平台私钥签名,商户用平台公钥验签
5. 私钥各自保管,安全性更高!
-
第一步:生成商户RSA密钥对
推荐方式:在平台"基本资料"页面点击 "生成商户RSA密钥对" 按钮,一键生成:
• 商户私钥:弹窗展示,请立即复制保存(平台不保存,丢失无法找回)
• 商户公钥:自动保存到平台,无需手动配置
• 平台公钥:从平台"基本资料"复制,用于验证异步通知签名
也可使用OpenSSL命令自行生成:
# 使用OpenSSL生成商户RSA密钥对(可选) openssl genpkey -algorithm RSA -out merchant_private_key.pem -pkeyopt rsa_keygen_bits:2048 openssl rsa -pubout -in merchant_private_key.pem -out merchant_public_key.pem -
第二步:使用商户私钥签名(PHP示例)
//构造待签名字符串(与MD5签名第一步相同) $postDatas = array( "appid" => '20206491', "money" => '10', "payid" => '202305310100203715872', "type" => '1', "subject" => '购买商品', "notify_url" => 'http://296o.com/notify_url.php', "return_url" => 'http://296o.com/return_url.php', "sign_type" => 'rsa' ); ksort($postDatas); reset($postDatas); $str = ASCII($postDatas); //使用商户RSA私钥进行RSA签名 $privateKey = "-----BEGIN PRIVATE KEY----- 你的商户RSA私钥 -----END PRIVATE KEY-----"; $res = openssl_get_privatekey($privateKey); openssl_sign($str, $sign, $res, OPENSSL_ALGO_SHA256); openssl_free_key($res); $sign = base64_encode($sign); -
第三步:验证异步通知签名(PHP示例)
$platformPublicKey = "-----BEGIN PUBLIC KEY----- 平台RSA公钥 -----END PUBLIC KEY-----"; $postDatas = $_POST; ksort($postDatas); reset($postDatas); $str = ASCII($postDatas); $sign = base64_decode($_POST['sign']); $res = openssl_get_publickey($platformPublicKey); $verifyResult = openssl_verify($str, $sign, $res, OPENSSL_ALGO_SHA256); openssl_free_key($res); if($verifyResult === 1 && !empty($_POST['pay_no'])){ exit('success'); }else{ exit('fail'); }
-
常见问题排查
1、可以使用signArray参数,提交参与签名的数组字符串,然后对比前后计算签名的数组参数有什么区别;
2、param参数值里包含逗号(,)等符号,http请求提交到平台时,会被检测层过滤掉符号,从而导致签名不一致;
解决方案:
如果你的param参数里必须携带符号,那么可以先进行base64编码转换,异步通知和同步跳转接收param参数时,再转换回来即可。3、如果参与签名的数组字符串里存在转义字符,那么要先去掉转义,再进行签名,参考代码如下:
function createSignArray($postDatas) { ksort($postDatas); reset($postDatas); $str = ""; while (list ($key, $val) = each ($postDatas)) { $str.=$key."=".urlencode($val)."&"; } $str = substr($str,0,count($str)-2); if(get_magic_quotes_gpc()){ $str = stripslashes($str); } return $str; }
支付回调通知
| 参数名称 | 参数含义 | 参数说明 |
|---|---|---|
| status | 返回状态 | status为1表示成功,status为0表示失败 |
| pay_no | 交易流水号 | 平台生成的交易流水号 |
| pay_money | 支付金额 | 买家需要支付的实际金额 |
| pay_url | 支付链接 | 商户可用此参数自定义创建支付页面,或者跳转到支付宝、微信等第三方平台 |
| code_url | 二维码链接 | 商户可用此参数自定义去生成二维码后展示出来进行扫码支付 |
| code_img | 二维码图片 | 此参数的值即是根据code_url生成的可以扫码支付的二维码图片地址 |
| 参数名称 | 参数含义 | 类型 | 示例值 | 参与签名 | 参数说明 |
|---|---|---|---|---|---|
| pay_id | 唯一标识 | string | admin | 是 | 商户提交的的唯一标识(订单编号或者用户ID) |
| pay_money | 订单金额 | string | 100.00 | 是 | 实际付款金额 |
| pay_no | 交易流水号 | string | 20200217200042408995 | 是 | 平台生成的交易流水号 |
| pay_time | 交易时间 | int | 1487597795 | 是 | 付款的时间戳 |
| pay_type | 支付方式 | int | 1 | 是 | 1:支付宝 2:微信支付 3:QQ钱包 |
| param | 扩展返回 | string | 没有则不返回 | 是 | 商户附加数据,原封返回,提交什么就返回什么。注意:值为空时不参与签名 |
| sign | 数据签名 | string | c47f3cba123456b6b24542110a8928af | 否 | 加密签名,验证订单是否为合法,请参考签名算法 |
| sign_type | 签名类型 | string | rsa | 否 | 默认md5(V1.0),如果下单时使用rsa,通知也会使用rsa |
通知返回的参数示例如下:
MD5签名(V1.0)通知示例:
pay_id=admin&pay_money=100.00&pay_no=20200217200042408995&pay_time=1487597795&pay_type=1&sign=c47f3cba123456b6b24542110a8928af
RSA签名(V2.0)通知示例:
pay_id=admin&pay_money=100.00&pay_no=20200217200042408995&pay_time=1487597795&pay_type=1&sign_type=rsa&sign=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855...
| 返回类型 | 返回数据 |
|---|---|
| 业务处理完成返回 | ok 或者 success |
| 业务处理失败还需要下次继续通知返回 | fail |
1、业务处理完成返回:ok 或者 success
2、业务处理失败还需要下次继续通知返回:fail
3、注意:一定要验证是否有pay_no参数值,因为只有该值存在,才是付款成功 。
-
notify_url异步通知示例(Thinkphp):
$appkey="平台的商户密钥"; //平台RSA公钥(使用RSA签名时需要) $platformPublicKey = "-----BEGIN PUBLIC KEY----- 平台RSA公钥 -----END PUBLIC KEY-----"; if (empty($_POST)) $_POST = $_GET; //如果为GET方式访问 //ksort()对数组按照键名进行升序排序 ksort($_POST); //reset()内部指针指向数组中的第一个元素 reset($_POST); $sign = '';//初始化 foreach ($_POST AS $key => $val) { //遍历POST参数 if ($val == ''||$key == 'sign'||$key == 'sign_type') continue; //跳过这些不签名 if ($sign) $sign .= '&'; //第一个字符串签名不加& 其他加&连接起来参数 $sign .= "$key=$val"; //拼接为url参数形式 } $pay_id = $_POST['pay_id']; //需要充值的ID 或订单号 或用户名 $pay_money = (float)$_POST['pay_money']; //实际付款金额 $pay_no = $_POST['pay_no']; //交易流水号 $pay_time = $_POST['pay_time']; //付款的时间戳 $pay_type = (int)$_POST['pay_type']; //支付方式 1:支付宝 2:微信支付 3:QQ钱包 $param = isset($_POST['param'])?$_POST['param']:'';//自定义参数 //验证签名(根据sign_type选择验证方式) $signValid = false; if(isset($_POST['sign_type']) && strtolower($_POST['sign_type']) == 'rsa'){ $res = openssl_get_publickey($platformPublicKey); $signValid = openssl_verify($sign, base64_decode($_POST['sign']), $res, OPENSSL_ALGO_SHA256) === 1; openssl_free_key($res); }else{ $signValid = (md5($sign . $appkey) == $_POST['sign']); } if (!$_POST['pay_no'] || !$signValid) { //不合法的数据 exit('fail'); //返回失败 继续补单 } else { //合法的数据 /** * 业务处理在这里写 */ exit('success'); //返回成功,业务处理完成,下面不再执行了 } -
return_url同步跳转示例(Thinkphp):
$appkey="平台的商户密钥"; //平台RSA公钥(使用RSA签名时需要) $platformPublicKey = "-----BEGIN PUBLIC KEY----- 平台RSA公钥 -----END PUBLIC KEY-----"; if (empty($_POST)) $_POST = $_GET; //如果为GET方式访问 //ksort()对数组按照键名进行升序排序 ksort($_POST); //reset()内部指针指向数组中的第一个元素 reset($_POST); $sign = '';//初始化 foreach ($_POST AS $key => $val) { //遍历POST参数 if ($val == ''||$key == 'sign'||$key == 'sign_type') continue; //跳过这些不签名 if ($sign) $sign .= '&'; //第一个字符串签名不加& 其他加&连接起来参数 $sign .= "$key=$val"; //拼接为url参数形式 } $pay_id = $_POST['pay_id']; //需要充值的ID 或订单号 或用户名 $pay_money = (float)$_POST['pay_money']; //实际付款金额 $pay_no = $_POST['pay_no']; //交易流水号 $pay_time = $_POST['pay_time']; //付款的时间戳 $pay_type = (int)$_POST['pay_type']; //支付方式 1:支付宝 2:微信支付 3:QQ钱包 $param = isset($_POST['param'])?$_POST['param']:'';//自定义参数 //验证签名 $signValid = false; if(isset($_POST['sign_type']) && strtolower($_POST['sign_type']) == 'rsa'){ $res = openssl_get_publickey($platformPublicKey); $signValid = openssl_verify($sign, base64_decode($_POST['sign']), $res, OPENSSL_ALGO_SHA256) === 1; openssl_free_key($res); }else{ $signValid = (md5($sign . $appkey) == $_POST['sign']); } if (!$_POST['pay_no'] || !$signValid) { //不合法的数据 $result = '支付失败'; } else { //合法的数据 /** * 业务处理在这里写 */ $result = '支付成功'; } $this->assign('pay_id',$pay_id); $this->assign('pay_no',$pay_no); $this->assign('pay_money',$pay_money); $this->assign('pay_type',$pay_type); $this->assign('pay_time',date("Y-m-d H:i:s",$pay_time)); $this->assign('param',$param); $this->assign('result',$result); return view();
订单查询接口
| 参数名称 | 参数含义 | 类型 | 示例值 | 是否必填 | 参与签名 | 参数说明 |
|---|---|---|---|---|---|---|
| appid | 商户编号 | string | 20206491 | 是 | 是 | 平台分配商户编号 |
| pay_no | 交易流水号 | string | 20200217200042408995 | 否 | 是 | 平台生成的交易流水号,pay_no和pay_id必须提交其中一个,否则查询不到订单,同时提交2个参数时,pay_no优先 |
| pay_id | 唯一标识 | string | admin | 否 | 是 | 商户提交的的唯一标识(订单编号或者用户ID) |
| sign | 数据签名 | string | d7242db1748f4baef9ecec43b4106553 | 是 | 否 | 对数据进行签名,请参考签名算法 |
| sign_type | 签名类型 | string | rsa | 否 | 否 | 默认md5,可选rsa(推荐) |
| charset | 编码 | string | utf-8 | 否 | 否 | utf-8或gb2312,默认utf-8 |
系统会以json格式返回查询的结果(数据签名类型和提交的一样),可根据code的值来判断订单是否支付成功
| 返回类型 | 返回数据 |
|---|---|
| 支付成功 | {"code":1,"type":"success","pay_no":"平台订单交易流水号","msg":"订单支付成功","sign_type":"md5","sign":"c47f3cba123456b6b24542110a8928af"} |
| 未支付 | {"code":0,"type":"error","pay_no":"平台订单交易流水号","msg":"订单未支付","sign_type":"md5","sign":"c47f3cba123456b6b24542110a8928af"} |
| 其他情况 | {"code":0,"type":"error","title":"提示","msg":"订单不存在","sign_type":"md5","sign":"c47f3cba123456b6b24542110a8928af"} |
