关于银联支付
目前B2C购物支付场景下,支付宝和微信的在线支付已经成为我们经常遇到的支付方式。另外,银联支付也是我们日常的一种支付方式,本文所指的银联支付即指中国银联网关支付产品,主要适用于持卡人在商户网站B2C购物支付场景,持卡人通过点击银联在线支付图标(可选择支付类型),并在银联在线支付网关完成支付信息录入,最终完成支付。银联在线支付支持输入卡号付款、用户登录支付、网银支付、迷你付(IC卡支付)等多种付款方式,用户通过统一入口,访问支付首页,按照提示和所列功能即可完成支付。
一些准备
做为软件开发商,实现银联在线支付,需要协助(但不必要)目标商户签署支付协议,开通商户平台。以2007版支付为例,开通过商户平台需要提交许多资料,包括企业及个人信息(如管理员用户名、名称、手机号等)备案。本文将不重点介绍如何实现在线支付,仅就支付反馈中遇到的一些问题进行分析。
在分析前,我们简单讲述一下要 POST 的一些参数和数据,参见下表:
序号 | 字段名 | 类型 | 说明 |
---|---|---|---|
1 | MerId | 数字串 | 必填,为银联统一分配给商户的商户号,15位长度 |
2 | OrdId | 数字串 | 必填,商户提交给银联的交易订单号,16位长度,请注意生成的订单号当天支付如果不成功,则再次使用相同的订单号提交后会提示重复交易,但次日该订单号仍可以做为交易失败后的“新”订单号继续在线支付。从另一个角度来说,使用固定订单号有助于商户端更新支付状态失败造成用户的重复交易。 |
3 | TransAmt | 数字串 | 必填,订单交易金额,12位长度,左补0,单位为分。如 000000007890,表示七十八元九角零分,即 78.90 元。 |
4 | CuryId | 数字串 | 必填,订单交易币种,3位长度,固定为人民币156 |
5 | TransDate | 数字串 | 必填,订单交易日期,8位长度 |
6 | TransType | 数字串 | 必填,交易类型,4位长度,0001 表示消费,0002 表示退款 |
7 | Version | 数字串 | 必填,支付接入版本号,如 20070129 |
8 | BgRetUrl | 数字串 | 必填,后台交易接收的回调 URL,URL地址的长度不超过80个字节 |
9 | PageRetUrl | 数字串 | 必填,前台交易页面接收的回调 URL(显示给用户方的页面,交易情况显示详情页面),URL地址的长度不超过80个字节 |
10 | GateId | 数字串 | 非必填,支付网关号 |
11 | Priv1 | 数字串 | 非必填,商户私有域,长度不超过60个字节,属于商户的备注内容 |
12 | ChkValue | 数字串 | 必填,256字节长的ASCII码,是本次交易的关键数字签名 |
以下是示例代码,提供了一个服务器 Form 和一些隐藏字段域 准备提交时使用
代码语言:javascript复制<form id="payform" runat="server">
<input type="hidden" name="MerId" value="" id="MerId" runat="server"/>
<input type="hidden" name="OrdId" value="" id="OrdId" runat="server"/>
<input type="hidden" name="TransAmt" value="" id="TransAmt" runat="server"/>
<input type="hidden" name="CuryId" value="156" id="CuryId" runat="server"/>
<input type="hidden" name="TransDate" value="" id="TransDate" runat="server"/>
<input type="hidden" name="TransType" value="0001" id="TransType" runat="server"/>
<input type="hidden" name="Version" value="" id="Version" runat="server"/>
<input type="hidden" name="BgRetUrl" value="https://x.x.com/BR.aspx" id="BgRetUrl" runat="server"/>
<input type="hidden" name="PageRetUrl" value="https://x.x.com/PR.aspx" id="PageRetUrl" runat="server"/>
<input type="hidden" name="GateId" value="" id="GateId" runat="server"/>
<input type="hidden" name="Priv1" value="" id="Priv1" runat="server"/>
<input type="hidden" name="ChkValue" value="" id="ChkValue" runat="server"/>
</form>
假设我们可以动态的设置 Form 的 Action (如测试环境或生产环境),示例代码如下:
代码语言:javascript复制protected void b_insbtn_Click(object sender, EventArgs e)
{
if (ViewState["RunType"].ToString() == "1")
{
payform.Action = "https://payment.chinapay.com/pay/TransGet"; //生产环境
payform.Method = "post";
}
else if (ViewState["RunType"].ToString() == "0")
{
payform.Action = "http://payment-test.chinapay.com/pay/TransGet"; //测试环境
payform.Method = "post";
}
//后续参数配置代码....
}
代码中 https://payment.chinapay.com/pay/TransGet ,即为正式提交在线支付的入口页。
交易状态码的一些分析
当引导用户到银联支付页面,并完成支付操作后(包括未成功的交易),通过 PageRetUrl 和 BgRetUrl 回调地址我们会获取银联的交易状态码,状态码为4位数字,交易状态码为非 “1001” 的即为失败交易,我们要根据实际的返回保存到数据库并给予对应的提示。
详细的交易状态码见下表:
交易状态码 | 说明 | 交易状态码 | 说明 |
---|---|---|---|
1001 | 消费交易成功 | 2063 | 违反安全保密规定 |
1003 | 退款交易成功 | 2064 | 原始金额不正确 |
1005 | 退款撤销成功 | 2065 | 超出取款次数限制 |
1111 | 未支付 | 2066 | 受卡方呼受理方安全保密部门 |
2000 | 银行无应答 | 2067 | 捕捉 |
2001 | 查发卡放 | 2068 | 收到的回答太迟 |
2002 | 查发卡放特殊条件 | 2075 | 允许输入PIN的次数超限 |
2003 | 无效商户 | 2090 | 日期切换正在处理 |
2004 | 没收卡 | 2091 | 发卡方与交换中心不能操作 |
2005 | 不予承兑 | 2092 | 金融机构或中间网络设施找不到或无法到达 |
2006 | 出错 | 2093 | 交易违法、不能完成 |
2007 | 特殊条件下没收卡 | 2094 | 重复交易 |
2009 | 请求正在处理中 | 2095 | 调节控制错 |
2010 | 卡bin未参与CPUSecure服务 | 2096 | 系统异常、失效 |
2012 | 无效交易 | 2097 | ATMPOS终端找不到 |
2013 | 无效金额 | 2098 | 交换中心收不到收卡方应答 |
2014 | 无效卡号 | 2099 | PIN格式错误 |
2015 | 无此发卡方 | 20A0 | MAC鉴别失败 |
2019 | 重新送入交易 | 2101 | 网关出错 |
2020 | 无效应答 | 2102 | 密码加密出错 |
2021 | 不作任何处理 | 2111 | 消息时间太新,请调整浏览器时间与实际时间一致 |
2022 | 怀疑操作有误 | 2112 | 消息时间太旧,请调整浏览器时间与实际时间一致 |
2023 | 不可接受的交易费 | 2113 | 信息不符 |
2030 | 格式错误 | 2114 | 商户号验证出错 |
2031 | 交换中心不支持的银行 | 2115 | 商户信息格式出错 |
2033 | 过期的卡 | 2116 | 撤销交易信息出错 |
2034 | 有作弊嫌疑 | 2117 | 查新个数为零 |
2035 | 受卡方与安全保密部门联系 | 2118 | 查询个数大于100 |
2036 | 受限制的卡 | 2141 | 系统出错 |
2037 | 受卡方呼受理方安全部门 | 2142 | 撤销交易出错 |
2038 | 超过允许的PIN试输入 | 2143 | 撤销交易已处理 |
2039 | 无此信用卡账户 | 2144 | 交易已被冲正 |
2040 | 请求的功能不支持 | 2145 | 撤销交易出错 |
2041 | 挂失卡 | 2146 | 交换中心无应答 |
2042 | 无此账户 | 2147 | 网管未收到该交易 |
2043 | 被窃卡 | 2148 | 重复交易 |
2044 | 无此投资账户 | 2150 | 通讯线路故障 |
2051 | 无足够的存款 | 2157 | 不允许持卡人进行交易 |
2052 | 无此支票账户 | 2158 | 该银行卡网上支付功能因系统升级而暂停 |
2053 | 无此储蓄卡账户 | 2199 | 系统出错 |
2054 | 过期的卡 | 2257 | 基于风险控制阻止的交易 |
2055 | 不正确的PIN | 2997 | 交易提交银行错误 |
2056 | 无此卡记录 | 4001 | HSBC交易中止 |
2057 | 不允许持卡人进行的交易 | 4055 | HSBC安全认证错误 |
2058 | 不允许此终端进行的交易 | 4097 | HSBC交易无应答 |
2059 | 有作弊嫌疑 | 5501 | 工行网关无应答 |
2060 | 受卡方与安全保密部门联系 | 5511 | 工行网关处理失败 |
2061 | 超出取款金额限制 | 5522 | 工行网关未收到后台应答 |
2062 | 受限制的卡 |
交易状态码 | 说明 | 交易状态码 | 说明 |
---|---|---|---|
8001 | HSBC交易失败 | 9000 | 电话支付订单已成功发给银行 |
8002 | HSBC交易失败 | 9001 | 电话支付订单发送银行失败(订单电话域非法) |
8003 | HSBC交易失败 | 9002 | 电话支付订单发送银行失败(秘密数域非法) |
8004 | HSBC交易失败 | 9003 | 电话支付订单发送银行失败(商户扩展域非法) |
8005 | HSBC交易失败 | 9004 | 电话支付订单发送银行失败(CP保留域非法) |
8006 | HSBC交易失败 | 9100 | 电话支付订单撤销成功 |
8007 | HSBC交易失败 | 9101 | 电话支付订单撤销失败(订单号不匹配) |
8008 | HSBC交易失败 | 9102 | 电话支付订单撤销失败(金额不匹配) |
8009 | HSBC交易失败 | 9103 | 电话支付订单撤销失败(交易日期不对) |
在银联支付20070129版本的实际应用中,回调获取的交易状态码基本正常,但出现了一些问题就是在回调获取成功交易状态码 “1001” 后,银联继续推送了一些状态码回调,因此在开发中需要进行判断 ,一味的接收并更新状态码,可能无法达到预期的效果,尤其当状态码已经为“1001”的情况下。在实际应用中,除正常状态码反馈,我们遇到了 “2006” 和 “2148” 错误。
从 “2148” 错误可以理解,是用户的重复交易(包括当日和隔日重复交易),因此我们需要在更新数据库前对两次交易码进行比对,如果之前为 “1001” 的可以无视 “2148” 的存在。
另外我们遇到的是 “2006” 错误,从代码表中仅查询出说明为 “出错” ,这个问题我们曾致电银联客服求解,未得到任何有效答案,包括可能出现的情况。关键是,状态码会在获得“1001” 后不定期的时间内(非正常回调周期)推送到回调地址,这就比较难以判断真实发生的情况。
因此对于非“1001”的特殊情况,建立日志表跟踪非常关键,目前来看,对于“2006”的拦截很有必要,实际应用中错误更新的情况明显减少。
小结
更多的详情请参考如下链接(中国银联开放平台):
https://open.unionpay.com/tjweb/index
在实际与银联客服的沟通中,我们尽量使用官方提供的联系邮件进行沟通,这样效率可以高一些。也便于留档查询,而且如果修改资料(包括更换联系人、修改企业信息等)手续比较繁琐,不是太友好,需要一定的时间,因此在应用上线功能时需要做好准备工作。
以上是本人的一些体会与实践,仅代表个人的一些观点,再次感谢您的阅读,欢迎讨论、指教!