用于商户提交银联支付订单进行报关申请,同步应答仅为受理结果,申报结果必须需要发起申报查询交易。 交易子类(txnSubType)上送值为00时,不支持商户拆单申报,也不支持重推,商户通过银联应答的原始交易流水号(origQryId)作为报关流水号向海关申报。 交易子类(txnSubType)上送值为01时,支持商户拆单申报,商户通过银联应答的支付单据号(payTransNo)作为报关流水号向海关申报。对于商户报送海关返回缺支付单状态的情况,支持重推支付单报关,重推时,需要保持订单三要素和原始请求一致(merId + orderId + txnTime 三要素相同),否则认为是一条新请求。重推原则如下: 1. 全要素相同定义为重推; 2. 仅交易子类(txnSubType)上送值为01新模式时,支持拆单,支持重推; 3. 对于同一个申报请求(merId + orderId + txnTime 三要素相同),返回支付单据号payTransNo不变,商户按照payTransNo的值作为报关流水号报送海关; 4. 距离上一次推送已超过至少5分钟(银联可能根据实际运行情况调整此时间值),如果5分钟内收到重复申报,直接拒绝,返回重复申报; 5. 本次支持重推海关包括:总署接口、上海口岸、宁波口岸,其他口岸暂不支持。
POST
https://gateway.95516.com/gateway/api/backTransReq.do
https://gateway.test.95516.com/gateway/api/backTransReq.do
中文名称 | 英文名称 | 域类型 | 默认值 | 请求要求 | 备注 | |
---|---|---|---|---|---|---|
编码方式 | encoding | ANS1..20 | UTF-8 | M-必填 | 默认取值:UTF-8 | |
签名 | signature | ANS1..1024 | 0 | M-必填 | 填写对报文摘要的签名 | |
产品类型 | bizType | N6 | 无 | M-必填 | 默认:000000 | |
接入类型 | accessType | N1 | 无 | M-必填 |
0:商户直连接入 1:收单机构接入 2:平台商户接入 |
|
收单机构代码 | acqInsCode | AN8..11 | 无 | C-按条件必填 | 接入类型为收单机构接入时需上送 | |
商户类别 | merCatCode | N4 | 无 | C-按条件必填 | 接入类型为收单机构接入时需上送 | |
商户代码 | merId | AN15 | 无 | M-必填 | ||
商户名称 | merName | ANS1..40 | 无 | C-按条件必填 | 接入类型为收单机构接入时需上送 | |
商户简称 | merAbbr | ANS1..8 | 无 | C-按条件必填 | 接入类型为收单机构接入时需上送 | |
二级商户代码 | subMerId | AN5..15 | 无 | C-按条件必填 | 商户类型为平台类商户接入时必须上送 | |
二级商户名称 | subMerName | ANS1..40 | 无 | C-按条件必填 | 商户类型为平台类商户接入时必须上送 | |
二级商户简称 | subMerAbbr | ANS1…16 | 无 | C-按条件必填 | 商户类型为平台类商户接入时必须上送 | |
商户订单号 | orderId | AN8..40 | 无 | M-必填 |
商户订单号,不能含“-”或“_”; 商户自定义,同一交易日期内不可重复; 商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。 |
|
订单发送时间 | txnTime | YYYYMMDDhhmmss | 无 | M-必填 | 商户报关时间 | |
交易币种 | currencyCode | AN3 | 无 | M-必填 | 币种格式必须为3位代码,默认取值:156(人民币) | |
银行卡验证信息及身份信息 | customerInfo | ANS1..1024 | 无 | M-必填 | 查看详情 | |
交易金额 | txnAmt | N1..12 | 无 | M-必填 | 对应原支付交易的交易金额 支付金额,为(货款+税款+运费) | |
保留域 | reserved | ANS1..2048 | 无 | C-按条件必填 | 查看详情 | |
加密证书ID | encryptCertId | N1..128 | 无 | C-按条件必填 | ||
渠道类型 | channelType | N2 | 无 | M-必填 | 07 | |
签名方法 | signMethod | N2 | 无 | M-必填 | 01 | |
交易类型 | txnType | N2 | 无 | M-必填 | 交易类型 82 | |
交易子类 | txnSubType | N2 | 无 | M-必填 |
默认00 01:新模式,支持拆单,支持重推 |
|
请求方保留域 | reqReserved | ANS1..1024 | 无 | O-选填 | 商户自定义保留域,交易应答时会原样返回 | |
证书ID | certId | N1..128 | 无 | C-按条件必填 | ||
版本号 | version | NS5 | 无 | M-必填 | 固定填写 | |
原交易商户订单号 | origOrderId | AN8..40 | 无 | C-按条件必填 |
原全渠道支付交易必送,表示原全渠道支付交易的商户订单号OrderId,应与报送海关的订单流、物流信息保持一致; |
|
原交易商户发送交易时间 | origTxnTime | YYYYMMDDhhmmss | 无 | C-按条件必填 |
原全渠道支付交易必送,表示原全渠道支付交易的商户发送时间TxnTime; |
|
海关申报信息域 | customsData | ANS1..2048 | 无 | M-必填 | 查看详情 | |
商户英文名称 | merEnName | ANS40 | C-按条件必填 | 查看详情 | ||
原交易渠道 | origTransChannel | N1 | 默认0 | O-选填 |
0:全渠道 1:ExpressPay (leased line mode)和SeucrePlus;origTransChannel上送取值1时,交易子类txnSubType仅支持上送取值01 |
|
原域7交易传输时间 | origTraceTime | YYYYMMddhhmmss | C-按条件必填 |
原支付交易发起方的系统工作日日期和时间;原交易渠道origTransChannel=1时必送 |
||
原域11系统跟踪号 | origTraceNo | N6 | C-按条件必填 |
原支付交易交易的系统跟踪号;原交易渠道origTransChannel =1时必送 |
||
原域32受理机构标识码 | origAcqInsCode | N1..11 | C-按条件必填 |
原支付交易的受理机构标识码;原交易渠道origTransChannel=1时必送 |
||
原域33发送机构标识码 | origFwdInsCode | N1..11 | C-按条件必填 |
原支付交易的发送机构标识码;原交易渠道origTransChannel=1时必送 |
中文名称 | 英文名称 | 域类型 | 默认值 | 请求要求 | 备注 | |
---|---|---|---|---|---|---|
编码方式 | encoding | ANS1..20 | UTF-8 | R-需要返回 | ||
签名 | signature | ANS1..1024 | 0 | M-必填 | ||
产品类型 | bizType | N6 | 无 | R-需要返回 | ||
接入类型 | accessType | N1 | 无 | R-需要返回 | 0:商户直连接入 1:收单机构接入 2:平台商户接入 |
|
收单机构代码 | acqInsCode | AN8..11 | 无 | R-需要返回 | ||
商户代码 | merId | AN15 | 无 | R-需要返回 | 同申报交易,原值返回 | |
商户订单号 | orderId | AN8..40 | 无 | R-需要返回 | 商户订单号,不能含“-”或“_”; 商户自定义,同一交易日期内不可重复; 商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。 |
|
订单发送时间 | txnTime | YYYYMMDDhhmmss | 无 | R-需要返回 | 同申报交易,原值返回 | |
保留域 | reserved | ANS1..2048 | 无 | O-选填 | 查看详情 | |
查询流水号 | queryId | AN20..21 | 无 | M-必填 | ||
应答码 | respCode | AN2 | 无 | M-必填 | 银联受理申报响应结果 | |
应答信息 | respMsg | ANS1..256 | 无 | M-必填 | 银联受理申报响应结果 | |
签名方法 | signMethod | N2 | 无 | M-必填 | ||
交易类型 | txnType | N2 | 无 | R-需要返回 | ||
交易子类 | txnSubType | N2 | 无 | R-需要返回 | ||
请求方保留域 | reqReserved | ANS1..1024 | 无 | O-选填 | 同申报交易,原值返回 | |
证书ID | certId | N1..128 | 无 | C-按条件必填 | ||
版本号 | version | NS5 | 无 | R-需要返回 | ||
原交易查询流水号 | origQryId | AN21 | 无 | R-需要返回 | 原始消费交易的queryId |
String merId = req.getParameter("merId");
String orderId = req.getParameter("orderId");
String txnTime = req.getParameter("txnTime");
String txnAmt = req.getParameter("txnAmt");
String customsData = req.getParameter("customsData");
String origOrderId = req.getParameter("origOrderId");
String origTxnTime = req.getParameter("origTxnTime");
String origMerId = req.getParameter("origMerId");
Map<String, String> data = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
data.put("version", DemoBase.version); //版本号
data.put("encoding", DemoBase.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法
data.put("txnType", "82"); //交易类型 31-消费撤销
data.put("txnSubType", "00"); //交易子类型 默认00
data.put("bizType", "000000"); //业务类型
data.put("channelType", "07"); //渠道类型,07-PC,08-手机
/***商户接入参数***/
data.put("merId", merId); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
data.put("accessType", "0"); //接入类型,商户接入固定填0,不需修改
data.put("orderId", orderId); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生,不同于原消费
data.put("txnTime", txnTime); //订单发送时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效
data.put("txnAmt", txnAmt); //【撤销金额】,消费撤销时必须和原消费金额相同
data.put("currencyCode", "156"); //交易币种(境内商户一般是156 人民币)
data.put("backUrl", DemoBase.backUrl); //后台通知地址,后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费撤销交易 商户通知,其他说明同消费交易的商户通知
/***要调通交易以下字段必须修改***/
data.put("origOrderId", origOrderId);//原交易订单号,取原消费/预授权完成的orderId
data.put("origTxnTime", origTxnTime);//原交易订单发送时间。取原消费/预授权完成的txnTime
data.put("customsData", customsData);//海关信息域,按规范填写
//申报商户为渠道商时需要送这个字段
if(null!=origMerId && !"".equals(origMerId))
data.put("reserved","{origMerId="+origMerId+"}");
//消费:交易要素卡号+验证码看业务配置(默认要短信验证码)。
Map<String,String> customerInfoMap = new HashMap<String,String>();
customerInfoMap.put("certifTp", req.getParameter("certifTp")); //证件类型,01-身份证
customerInfoMap.put("certifId", req.getParameter("certifId")); //证件号,15位身份证不校验尾号,18位会校验尾号,请务必在前端写好校验代码
customerInfoMap.put("customerNm", req.getParameter("customerNm")); //姓名
////////////如果商户号开通了【商户对敏感信息加密】的权限那么需要对 accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用:
data.put("encryptCertId",AcpService.getEncryptCertId()); //加密证书的certId,配置在acp_sdk.properties文件 acpsdk.encryptCert.path属性下
String customerInfoStr = AcpService.getCustomerInfo(customerInfoMap, "", DemoBase.encoding);
data.put("customerInfo", customerInfoStr);
// 请求方保留域,
// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。
// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:
// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。
// data.put("reqReserved", "透传信息1|透传信息2|透传信息3");
// 2. 内容可能出现&={}[]"'符号时:
// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);
// 2) 如果对账文件没有显示要求,可做一下base64(如下)。
// 注意控制数据长度,实际传输的数据长度不能超过1024位。
// 查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), DemoBase.encoding);解base64后再对数据做后续解析。
// data.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(DemoBase.encoding)));
/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文**/
Map<String, String> reqData = AcpService.sign(data,DemoBase.encoding);//报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
String url = SDKConfig.getConfig().getBackRequestUrl();//交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
Map<String, String> rspData = AcpService.post(reqData, url,DemoBase.encoding);//发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过
/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
//应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》
if(!rspData.isEmpty()){
if(AcpService.validate(rspData, DemoBase.encoding)){
LogUtil.writeLog("验证签名成功");
String respCode = rspData.get("respCode") ;
if(("00").equals(respCode)){
//交易已受理(不代表交易已成功),等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。
//TODO
}else if(("03").equals(respCode)||
("04").equals(respCode)||
("05").equals(respCode)){
//后续需发起交易状态查询交易确定交易状态
//TODO
}else{
//其他应答码为失败请排查原因
//TODO
}
}else{
LogUtil.writeErrorLog("验证签名失败");
//TODO 检查验证签名失败的原因
}
}else{
//未返回正确的http状态
LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
}
String reqMessage = DemoBase.genHtmlResult(reqData);
String rspMessage = DemoBase.genHtmlResult(rspData);
resp.getWriter().write("请求报文:<br/>"+reqMessage+"<br/>" + "应答报文:</br>"+rspMessage+"");
LogUtil.writeLog("BackRcvResponse接收后台通知开始");
String encoding = req.getParameter(SDKConstants.param_encoding);
// 获取银联通知服务器发送的后台通知参数
Map<String, String> reqParam = getAllRequestParam(req);
LogUtil.printRequestLog(reqParam);
Map<String, String> valideData = null;
if (null != reqParam && !reqParam.isEmpty()) {
Iterator<Entry<String, String>> it = reqParam.entrySet().iterator();
valideData = new HashMap<String, String>(reqParam.size());
while (it.hasNext()) {
Entry<String, String> e = it.next();
String key = (String) e.getKey();
String value = (String) e.getValue();
valideData.put(key, value);
}
}
//重要!验证签名前不要修改reqParam中的键值对的内容,否则会验签不过
if (!AcpService.validate(valideData, encoding)) {
LogUtil.writeLog("验证签名结果[失败].");
//验签失败,需解决验签问题
} else {
LogUtil.writeLog("验证签名结果[成功].");
//【注:为了安全验签成功才应该写商户的成功处理逻辑】交易成功,更新商户订单状态
String orderId =valideData.get("orderId"); //获取后台通知的数据,其他字段也可用类似方式获取
String respCode = valideData.get("respCode");
}
LogUtil.writeLog("BackRcvResponse接收后台通知结束");
//返回给银联服务器http 200 状态码
resp.getWriter().print("ok");
1. 不返回报文体的情况:
版本号,交易类型、子类,签名方法,签名值等关键域未上送,返回“Invalid request.”;
交易类型和请求地址校验有误,返回“Invalid request URI.”
2. 返回全部的请求报文,附加应答码和应答描述(包括的应答码有:01、02、03、10、11、32):
验证签名失败;
报文格式错(包含,必填域缺失,上送银联报文未定义的域,报文域的格式非法,交易时间超出范围);
签名失败;
超时等其他系统异常.