支付完成后通知
本接口用于支付成功之后的通知,支付完成后采宝会将如下参数post到相关地址。
收到本接口的回调数据后,请异步处理订单逻辑,支付完成后的通知采用的是RSA签名算法,具体生成sign签名逻辑请看RSA请求签名规则, 并返回success
字符串(必须完全相同,不能有任何其他字符),否则采宝会尝试进行重试,重试时间会不断加长,具体重试时间间隔为:15S、30S、300S、1800S、3600S、(24 * 3600 - 1800)S,共重试6次,超过时间后,不再进行通知,请自行通过订单查询接口同步支付状态。
需要注意的是:
1. 当接入方收到通知后,请立刻返回success,自己的业务请异步进行处理,通知超时时间为5S,超过5S会超时,超时后会记录异常一次。
2. 为了保障系统的问题定性,我们队外部通知采取了保护机制,如果发现一段时间(20分钟)内,大量订单通知异常(通知异常80次),那我们会将停止这个服务器订单的通知,停止通知20分钟,20分钟之后自动恢复通知,如果再在20分钟内发现还是通知失败超过80次,则重复之前停止通知的逻辑。
参数简介
参数名 | 参数介绍 |
---|---|
cbOrderNo | 采宝的订单号,最长64位 |
appOrderNo | 调用方自己生成的订单号 |
outOrderNo | 微信或支付宝产生的订单号 |
orderStatus | 订单状态,详情参照:订单状态详解 |
totalAmount | 订单总额,以分为单位 |
receiveAmount | 实收金额,以分为单位 |
paymentChannel | 支付渠道, 详情参照:支付渠道详解 |
subject | 订单简介 |
discountAmount | 优惠金额,以分为单位 |
paymentWay | 支付方式,详情参照:支付方式详解 |
payTime | 支付时间,数字格式,值为距离1970.1.1日的毫秒数 |
sign | 签名值,具体参照: 接口验签规则 |
Rsa验证签名方法示例(java)
/**
* 排序需要签名的字段
* @param sortedParams 参数Map
* @return
*/
public static String getSignContent(Map<String, String> sortedParams) {
StringBuffer content = new StringBuffer();
ArrayList keys = new ArrayList(sortedParams.keySet());
Collections.sort(keys);
int index = 0;
for(int i = 0; i < keys.size(); ++i) {
String key = (String)keys.get(i);
String value = (String)sortedParams.get(key);
if (StringUtils.isNotEmpty(value)) {
content.append((index == 0 ? "" : "&") + key + "=" + value);
++index;
}
}
return content.toString();
}
/**
* 验证签名
* @param content 签证签名内容串
* @param sign 签名
* @param publicKey 公钥
* @param charset 字符集
* @param signType 签名类型
* @return
* @throws RsaSignException
*/
public static boolean rsaCheck(String content, String sign, String publicKey, String charset, String signType) throws RsaSignException {
if ("RSA".equals(signType)) {
return rsaCheckContent(content, sign, publicKey, charset);
} else if ("RSA2".equals(signType)) {
return rsa256CheckContent(content, sign, publicKey, charset);
} else {
throw new RsaSignException("Sign Type is Not Support : signType=" + signType);
}
}
public static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
StringWriter writer = new StringWriter();
StreamUtil.io(new InputStreamReader(ins), writer);
byte[] encodedKey = writer.toString().getBytes();
encodedKey = Base64.decodeBase64(encodedKey);
return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
}
public static boolean rsaCheckContent(String content, String sign, String publicKey, String charset) throws RsaSignException {
try {
PublicKey e = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
Signature signature = Signature.getInstance("SHA1WithRSA");
signature.initVerify(e);
if (CheckUtil.isEmpty(charset)) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
return signature.verify(Base64.decodeBase64(sign.getBytes()));
} catch (Exception var6) {
throw new RsaSignException("RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, var6);
}
}
public static boolean rsa256CheckContent(String content, String sign, String publicKey, String charset) throws RsaSignException {
try {
PublicKey e = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initVerify(e);
if (CheckUtil.isEmpty(charset)) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
return signature.verify(Base64.decodeBase64(sign.getBytes()));
} catch (Exception var6) {
throw new RsaSignException("RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, var6);
}
}