银联在线支付在2014年重建了一个新的系统(全渠道acp)代替老系统(upop),并2014-2015期间将全部商户从upop切换到了acp。本文简要介绍一下银联在线支付的两种主要支付方式:前台网关支付(用户的浏览器从商户页面跳转到银联在线页面进行支付)和后台网关支付(用户在商户页面进行支付,商户后台系统调用银联系统完成支付)的切换方式。 javascript
此次切换有几个特色: php
灰度切换。不是全部交易一次性所有切换到acp,而是按照商户维度分批切换。这就意味着upop和acp两个系统要同时保持运行。 html
acp的报文格式变了,与upop的报文不兼容,包括请求报文,响应报文,交易成功的异步通知报文,这意味着切换时须要对报文进行转换。 java
upop保持原url,acp采用新的url。 api
如下是切换过程当中3种类型的商户的处理方式。 浏览器
尚未切换的upop时期入网的老商户,使用upop url,请求由upop系统处理。 frontend
已经切换的upop时期入网的老商户,使用upop url,请求由upop作报文转换后转发到acp,由acp系统处理。 异步
Acp上线后新入网的商户,使用acp url,请求由acp系统直接处理。 post
下图为前台网关支付的3种类型商户的处理流程 网站
这里咱们主要关注已经切换的upop时期入网的老商户的处理流程。
以壹基金网站为例。
首先,用户来到壹基金的捐款页面
http://www.onefoundation.cn/index.php?g=home&m=page&a=index&id=73
输入金额1元,点击”银联在线支付”按钮这时浏览器会访问http://www.onefoundation.cn/unionPay/action/front.php
壹基金在后台对”我要捐款1元给壹基金” 生成发送给upop的订单请求报文,并用商户私钥进行签名, 而后生成包含订单报文和商户签名的自动提交表单,表单的地址为upop前台网关地址https://unionpaysecure.com/api/Pay.action ,随后浏览器自动提交表单,访问upop前台网关。
<body onload="javascript:document.pay_form.submit();"> <form id="pay_form" name="pay_form" action="https://unionpaysecure.com/api/Pay.action" method="post"> <input type="hidden" name="origQid" id="origQid" value="" /> <input type="hidden" name="acqCode" id="acqCode" value="" /> <input type="hidden" name="merCode" id="merCode" value="" /> <input type="hidden" name="commodityUrl" id="commodityUrl" value="http://dev.one-foundation.com/" /> <input type="hidden" name="commodityName" id="commodityName" value="壹基金爱心捐赠" /> <input type="hidden" name="commodityUnitPrice" id="commodityUnitPrice" value="100" /> <input type="hidden" name="commodityQuantity" id="commodityQuantity" value="1" /> <input type="hidden" name="commodityDiscount" id="commodityDiscount" value="" /> <input type="hidden" name="transferFee" id="transferFee" value="" /> <input type="hidden" name="customerName" id="customerName" value="" /> <input type="hidden" name="defaultPayType" id="defaultPayType" value="" /> <input type="hidden" name="defaultBankNumber" id="defaultBankNumber" value="" /> <input type="hidden" name="transTimeout" id="transTimeout" value="" /> <input type="hidden" name="merReserved" id="merReserved" value="" /> <input type="hidden" name="version" id="version" value="1.0.0" /> <input type="hidden" name="charset" id="charset" value="UTF-8" /> <input type="hidden" name="merId" id="merId" value="210440383989452" /> <input type="hidden" name="merAbbr" id="merAbbr" value="壹基金" /> <input type="hidden" name="transType" id="transType" value="01" /> <input type="hidden" name="orderAmount" id="orderAmount" value="100" /> <input type="hidden" name="orderNumber" id="orderNumber" value="20151015154414354" /> <input type="hidden" name="orderTime" id="orderTime" value="20151015154414" /> <input type="hidden" name="orderCurrency" id="orderCurrency" value="156" /> <input type="hidden" name="customerIp" id="customerIp" value="58.246.226.97" /> <input type="hidden" name="frontEndUrl" id="frontEndUrl" value="http://dev.one-foundation.com/html/cn/beneficence_T_UPay.html" /> <input type="hidden" name="backEndUrl" id="backEndUrl" value="http://dev.one-foundation.com/unionPay/action/back_notify.php" /> <input type="hidden" name="signature" id="signature" value="41f5a7e35baf221c079279bcb5cb7037" /> <input type="hidden" name="signMethod" id="signMethod" value="md5" /> <input type="submit" type="hidden" style="display:none;"> </form>
Upop在收到浏览器发来的订单报文后。先对用商户公钥对报文进行验签,确保这笔订单报文确实是由这个商户发来的,验签成功后根据商户号判断该商户是否已经切换到acp,若是商户还没切换,则继续在upop进行后续处理;若是商户已经切换,则对报文进行转换,转换成acp格式的报文,并用银联公钥进行签名,而后生成包含acp格式报文和银联签名的自动提交表单,表单地址为acp前台网关的地址 https://cashier.95516.com/b2c/api/Pay.action,随后浏览器自动提交表单,访问acp前台网关。
<form id="defaultForm" action="https://cashier.95516.com/b2c/api/Pay.action" method="post"><input type="hidden" id="accNo" name="accNo" value="" /> <input type="hidden" id="accType" name="accType" value="" /> <input type="hidden" id="accessType" name="accessType" value="0" /> <input type="hidden" id="acqInsCode" name="acqInsCode" value="" /> <input type="hidden" id="backUrl" name="backUrl" value="http://dev.one-foundation.com/unionPay/action/back_notify.php" /> <input type="hidden" id="bindId" name="bindId" value="" /> <input type="hidden" id="bizType" name="bizType" value="000201" /> <input type="hidden" id="cardTransData" name="cardTransData" value="" /> <input type="hidden" id="certId" name="certId" value="0" /> <input type="hidden" id="channelType" name="channelType" value="07" /> <input type="hidden" id="currencyCode" name="currencyCode" value="156" /> <input type="hidden" id="customerInfo" name="customerInfo" value="" /> <input type="hidden" id="customerIp" name="customerIp" value="58.246.226.97" /> <input type="hidden" id="defaultPayType" name="defaultPayType" value="" /> <input type="hidden" id="encoding" name="encoding" value="UTF-8" /> <input type="hidden" id="encryptCertId" name="encryptCertId" value="" /> <input type="hidden" id="frontFailUrl" name="frontFailUrl" value="" /> <input type="hidden" id="frontUrl" name="frontUrl" value="http://dev.one-foundation.com/html/cn/beneficence_T_UPay.html" /> <input type="hidden" id="instalTransInfo" name="instalTransInfo" value="" /> <input type="hidden" id="internal.logId" name="internal.logId" value="1500120122" /> <input type="hidden" id="internal.merReferer" name="internal.merReferer" value="http://www.onefoundation.cn/unionPay/action/front.php" /> <input type="hidden" id="internal.origReqInfo" name="internal.origReqInfo" value="9001|07|0101" /> <input type="hidden" id="issInsCode" name="issInsCode" value="" /> <input type="hidden" id="merAbbr" name="merAbbr" value="壹基金" /> <input type="hidden" id="merCatCode" name="merCatCode" value="" /> <input type="hidden" id="merId" name="merId" value="210440383989452" /> <input type="hidden" id="merName" name="merName" value="壹基金" /> <input type="hidden" id="orderDesc" name="orderDesc" value="" /> <input type="hidden" id="orderId" name="orderId" value="20151015154414354" /> <input type="hidden" id="orderTimeout" name="orderTimeout" value="" /> <input type="hidden" id="payCardType" name="payCardType" value="" /> <input type="hidden" id="payTimeout" name="payTimeout" value="" /> <input type="hidden" id="reqReserved" name="reqReserved" value="" /> <input type="hidden" id="reserved" name="reserved" value="" /> <input type="hidden" id="riskRateInfo" name="riskRateInfo" value="{commodityQty=1&commodityName=壹基金爱心捐赠&commodityUrl=http://dev.one-foundation.com/&commodityUnitPrice=100}" /> <input type="hidden" id="securityType" name="securityType" value="" /> <input type="hidden" id="signMethod" name="signMethod" value="02" /> <input type="hidden" id="signature" name="signature" value="130519e5c6df6053a1410e0e87f099ad" /> <input type="hidden" id="subMerAbbr" name="subMerAbbr" value="" /> <input type="hidden" id="subMerId" name="subMerId" value="" /> <input type="hidden" id="subMerName" name="subMerName" value="" /> <input type="hidden" id="supPayType" name="supPayType" value="" /> <input type="hidden" id="termId" name="termId" value="" /> <input type="hidden" id="tn" name="tn" value="" /> <input type="hidden" id="txnAmt" name="txnAmt" value="100" /> <input type="hidden" id="txnSubType" name="txnSubType" value="01" /> <input type="hidden" id="txnTime" name="txnTime" value="20151015154414" /> <input type="hidden" id="txnType" name="txnType" value="01" /> <input type="hidden" id="userMac" name="userMac" value="" /> <input type="hidden" id="version" name="version" value="1.0.0" /> <input type="hidden" id="vpcTransData" name="vpcTransData" value="" /> </form> <script type="text/javascript"> document.getElementById("defaultForm").submit(); </script>
Acp前台网关在收到浏览器发来的订单报文后,先用银联公钥对报文进行验签,确保这笔订单报文确实是由银联(这里是upop)生成的。而后对订单进行验证,存库,展现等处理,返回给浏览器银联在线支付的首页https://cashier.95516.com/b2c/index.action?transNumber=xxxxxx
<form id="defaultForm" action="https://cashier.95516.com/b2c/index.action?transNumber=201510151544144032128" method="post"> </form> <script type="text/javascript"> document.getElementById("defaultForm").submit(); </script>
用户在银联在线的支付页面输入卡号,密码,短信验证码等信息,并点击支付,浏览器便访问acp前台网关进行支付。
Acp前台网关进行一系列支付相关的处理后,成功完成了这笔支付。而后判断出这笔订单的商户是一个”已经切换的upop时期入网的老商户”,因而生成一个upop格式的后台通知报文并用银联私钥签名,而后异步发送给商户的后台系统(backendurl),通知商户这笔交易已经成功完成。同时生成一个upop格式前台通知报文并用银联私钥签名,而后把这个前台通知报文放入支付成功结果页面的”返回商户”的按钮的连接(frontendurl)。商户在收到前台通知或者后台通知报文后,对报文用银联公钥进行验签,验签成功后则将这笔订单认定为成功。
接下来咱们来看后台网关的交易流程,后台网关的报文是由商户系统直接发送给银联系统的下图为后台网关支付的3种类型商户的处理流程
和前台网关支付相同,这里咱们主要关注已经切换的upop时期入网的老商户的处理流程。