1、需要让招行提供商户号和收银员
pom.xml
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.69</version>
</dependency>
2、加密方式有两种:01:RSA2,02:SM2;第一种有时候请求不了,使用了第二种,SM2代码如下
SM2Util类
public class SM2Util {
private static final String SM2_KEY_TITLE = "3059301306072a8648ce3d020106082a811ccf5501822d03420004";
public static final String USER_ID = "1234567812345678";
public static String sm2Sign(String content, String privateKey) {
try {
//init privateKey
BCECPrivateKey bcecPrivateKey = BCUtil.getPrivatekeyFromD(new BigInteger(privateKey, 16));
byte[] sign = BCUtil.signSm3WithSm2(content.getBytes(), USER_ID.getBytes(), bcecPrivateKey);
return encodeBase64(signRawToAsn1(sign));
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
/**
* @param content
* @param rawSign
* @param publicKey
* @return
*/
public static boolean sm2Check(String content, String rawSign, String publicKey) {
try {
//init PublicKey
Sm2Vo sm2Vo = parseBase64TRawKey(publicKey);
if (null == sm2Vo) {
return false;
}
BCECPublicKey bcecPublicKey = BCUtil.getPublickeyFromXY(new BigInteger(sm2Vo.getSm2_x(), 16), new BigInteger(sm2Vo.getSm2_y(), 16));
byte[] sign = signAsn12Raw(decodeBase64(rawSign));
return BCUtil.verifySm3WithSm2(content.getBytes(), USER_ID.getBytes(), sign, bcecPublicKey);
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
/**
* BASE64格式公钥转换为裸公钥
*
* @param sm2Key
* @return
*/
private static Sm2Vo parseBase64TRawKey(String sm2Key) {
if (null == sm2Key) {
return null;
}
String sm2_asn1 = Hex.toHexString(decodeBase64(sm2Key));
if (!sm2_asn1.startsWith(SM2_KEY_TITLE)) {
return null;
}
String sm2_xy = sm2_asn1.substring(SM2_KEY_TITLE.length(), sm2_asn1.length());
String sm2_x = sm2_xy.substring(0, sm2_xy.length() / 2);
String sm2_y = sm2_xy.substring(sm2_xy.length() / 2, sm2_xy.length());
return new Sm2Vo(SM2_KEY_TITLE, sm2_x, sm2_y);
}
/**
* 将字节数组转换为Base64格式字符串
*
* @param data
* @return
*/
public static String encodeBase64(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
/**
* 将Base64格式字符串转为字节数组
*
* @param data
* @return
*/
public static byte[] decodeBase64(String data) {
return Base64.getDecoder().decode(data);
}
/**
* 将BC SM2 RAW签名值转化为ASN1格式签名值
*
* @param bcCipTxt
* @return
* @throws Exception
*/
private static byte[] signRawToAsn1(byte[] bcCipTxt) throws Exception {
byte[] netSignCipTxt = new byte[73];
byte[] signR = new byte[32];
byte[] signS = new byte[32];
System.arraycopy(bcCipTxt, 0, signR, 0, 32);
System.arraycopy(bcCipTxt, 32, signS, 0, 32);
//signR补位
int wPos = 4;
netSignCipTxt[0] = 0x30;
netSignCipTxt[2] = 0x02;
if ((signR[0] & 0xFF) >= 128) {
netSignCipTxt[wPos - 1] = 0x21;
netSignCipTxt[wPos] = 0x00;
wPos += 1;
} else {
netSignCipTxt[wPos - 1] = 0x20;
}
System.arraycopy(signR, 0, netSignCipTxt, wPos, 32);
wPos += 32;
//signS补位
netSignCipTxt[wPos] = 0x02;
wPos += 1;
if ((signS[0] & 0xFF) >= 128) {
netSignCipTxt[wPos] = 0x21;
wPos += 1;
netSignCipTxt[wPos] = 0x00;
wPos += 1;
} else {
netSignCipTxt[wPos] = 0x20;
wPos += 1;
}
System.arraycopy(signS, 0, netSignCipTxt, wPos, 32);
wPos += 32;
if (70 == wPos) {
netSignCipTxt[1] = 0x44;
} else if (71 == wPos) {
netSignCipTxt[1] = 0x45;
} else if (72 == wPos) {
netSignCipTxt[1] = 0x46;
} else {
throw new Exception("signRawToAsn1 Error!");
}
byte[] resultBytes = new byte[wPos];
System.arraycopy(netSignCipTxt, 0, resultBytes, 0, wPos);
return resultBytes;
}
/**
* 将ASN1格式签名值转化为BC SM2 RAW 签名值
*
* @param signature Asn1格式签名值
* @return byte[] Raw签名值
*/
private static byte[] signAsn12Raw(byte[] signature) throws Exception {
byte[] resultBytes = new byte[64];
//截取signR
int wPos = 3;
if ((signature[wPos] & 0xFF) == 32) {
wPos += 1;
} else if ((signature[wPos] & 0xFF) == 33) {
wPos += 2;
} else {
throw new Exception("signR length Error!");
}
System.arraycopy(signature, wPos, resultBytes, 0, 32);
wPos += 32;
//截取signS
wPos += 1;
if ((signature[wPos] & 0xFF) == 32) {
wPos += 1;
} else if ((signature[wPos] & 0xFF) == 33) {
wPos += 2;
} else {
throw new Exception("signS length Error!");
}
System.arraycopy(signature, wPos, resultBytes, 32, 32);
//System.out.println("\nhhh:\n" + ByteToHex(resultBytes));
return resultBytes;
}
}
BCUtil类
public class BCUtil {
private final static int RS_LEN = 32;
private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
private static ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
static {
if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
/**
* @param msg
* @param userId
* @param privateKey
* @return r||s,直接拼接byte数组的rs
*/
public static byte[] signSm3WithSm2(byte[] msg, byte[] userId, PrivateKey privateKey) {
return rsAsn1ToPlainByteArray(signSm3WithSm2Asn1Rs(msg, userId, privateKey));
}
/**
* @param msg
* @param userId
* @param privateKey
* @return rs in <b>asn1 format</b>
*/
public static byte[] signSm3WithSm2Asn1Rs(byte[] msg, byte[] userId, PrivateKey privateKey) {
try {
SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);
Signature signer = Signature.getInstance("SM3withSM2", "BC");
signer.setParameter(parameterSpec);
signer.initSign(privateKey, new SecureRandom());
signer.update(msg, 0, msg.length);
byte[] sig = signer.sign();
return sig;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param msg
* @param userId
* @param rs r||s,直接拼接byte数组的rs
* @param publicKey
* @return
*/
public static boolean verifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, PublicKey publicKey) {
return verifySm3WithSm2Asn1Rs(msg, userId, rsPlainByteArrayToAsn1(rs), publicKey);
}
/**
* @param msg
* @param userId
* @param rs in <b>asn1 format</b>
* @param publicKey
* @return
*/
public static boolean verifySm3WithSm2Asn1Rs(byte[] msg, byte[] userId, byte[] rs, PublicKey publicKey) {
try {
SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);
Signature verifier = Signature.getInstance("SM3withSM2", "BC");
verifier.setParameter(parameterSpec);
verifier.initVerify(publicKey);
verifier.update(msg, 0, msg.length);
return verifier.verify(rs);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s
*
* @param rsDer rs in asn1 format
* @return sign result in plain byte array
*/
private static byte[] rsAsn1ToPlainByteArray(byte[] rsDer) {
ASN1Sequence seq = ASN1Sequence.getInstance(rsDer);
byte[] r = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue());
byte[] s = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue());
byte[] result = new byte[RS_LEN * 2];
System.arraycopy(r, 0, result, 0, r.length);
System.arraycopy(s, 0, result, RS_LEN, s.length);
return result;
}
/**
* BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式
*
* @param sign in plain byte array
* @return rs result in asn1 format
*/
private static byte[] rsPlainByteArrayToAsn1(byte[] sign) {
if (sign.length != RS_LEN * 2) throw new RuntimeException("err rs. ");
BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, RS_LEN));
BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, RS_LEN, RS_LEN * 2));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(r));
v.add(new ASN1Integer(s));
try {
return new DERSequence(v).getEncoded("DER");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS) {
// for sm2p256v1, n is 00fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123,
// r and s are the result of mod n, so they should be less than n and have length<=32
byte[] rs = rOrS.toByteArray();
if (rs.length == RS_LEN) return rs;
else if (rs.length == RS_LEN + 1 && rs[0] == 0) return Arrays.copyOfRange(rs, 1, RS_LEN + 1);
else if (rs.length < RS_LEN) {
byte[] result = new byte[RS_LEN];
Arrays.fill(result, (byte) 0);
System.arraycopy(rs, 0, result, RS_LEN - rs.length, rs.length);
return result;
} else {
throw new RuntimeException("err rs: " + Hex.toHexString(rs));
}
}
public static BCECPrivateKey getPrivatekeyFromD(BigInteger d) {
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecParameterSpec);
return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
}
public static BCECPublicKey getPublickeyFromXY(BigInteger x, BigInteger y) {
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecParameterSpec);
return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
}
}
Sm2Vo类
public class Sm2Vo {
//标准公钥头
private String sm2_h;
//裸公钥X
private String sm2_x;
//裸公钥Y
private String sm2_y;
public Sm2Vo(String sm2_h, String sm2_x, String sm2_y) {
this.sm2_h = sm2_h;
this.sm2_x = sm2_x;
this.sm2_y = sm2_y;
}
public String getSm2_h() {
return sm2_h;
}
public void setSm2_h(String sm2_h) {
this.sm2_h = sm2_h;
}
public String getSm2_x() {
return sm2_x;
}
public void setSm2_x(String sm2_x) {
this.sm2_x = sm2_x;
}
public String getSm2_y() {
return sm2_y;
}
public void setSm2_y(String sm2_y) {
this.sm2_y = sm2_y;
}
}
3、提供回调给招行notifyUrl,审核时间要2~3天,记得提前提供
4、业务代码嵌入,代码如下
controller(控制层)
/**
* Description: 充值记录控制层
*
* @author: YJZ
* @date: 2021/9/10 14:12
*/
@Slf4j
@RestController
@RequestMapping("/rechargeRecord")
public class RechargeRecordController {
@Autowired
private RechargeRecordService recordService;
/**
* 招商充值
*
* @param orderNo 订单号
* @return 支付二维码
*/
@ApiOperation("招商充值")
@PostMapping("/recharge")
public RetResult recharge(@RequestParam String orderNo) {
String result = recordService.recharge(orderNo);
return RetResult.success(result);
}
/**
* 支付成功回调接口
*
* @param requestBodyString 银行回调对象
* @return 返回是否回调成功
*/
@ApiOperation("支付成功回调接口")
@PostMapping("/notifyUrl")
public Map<String, String> notifyUrl(@RequestBody String requestBodyString) {
log.info(String.format("回调开始,请求报文:%s", requestBodyString));
Map<String, String> result = recordService.notifyUrl(requestBodyString);
return result;
}
}
service(服务层)
@Slf4j
@Service
public class RechargeRecordService {
@Autowired
private RechargeRecordMapper rechargeRecordMapper;
@Autowired
private RechargeRecordConfig recordConfig;
public String recharge(String orderNo) {
// 查询支付信息是否存在
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("ORDER_NO", orderNo);
RechargeRecord rechargeRecord = rechargeRecordMapper.selectOne(queryWrapper);
if (rechargeRecord == null) {
throw new GlobalException("支付信息不存在");
}
String appId = recordConfig.getAppId();
String appSecret = recordConfig.getAppSecret();
try {
String signResult = signMethod(rechargeRecord);
ObjectMapper mapper = new ObjectMapper();
Map<String, String> signResultMap = mapper.readValue(signResult, Map.class);
// 组apiSign加密Map
Map<String, String> apiSign = new TreeMap<>();
apiSign.put("appid", appId);
apiSign.put("secret", appSecret);
apiSign.put("sign", signResultMap.get("sign"));
apiSign.put("timestamp", "" + System.currentTimeMillis() / 1000);
// MD5加密
String MD5Content = SignatureUtil.getSignContent(apiSign);
String apiSignString = MD5Utils.getMD5Content(MD5Content).toLowerCase();
// 组request头部Map
Map<String, String> apiHeader = new HashMap<>();
apiHeader.put("appid", appId);
apiHeader.put("timestamp", "" + System.currentTimeMillis() / 1000);
apiHeader.put("apisign", apiSignString);
// 发送HTTP post请求
Map<String, String> response = HttpUtils.postForEntity(recordConfig.getReceivingCodeUrl(), signResult, apiHeader);
log.info(String.format("二维码-%s二维码报文返回结果:%s", orderNo, JSONObject.toJSONString(response)));
// 返回结果验签
Boolean checkResult1 = checkSign(mapper.writeValueAsString(response), recordConfig.getSm2PublicKey());
if (!checkResult1) {
throw new GlobalException("验签失败");
}
if ("FAIL".equals(response.get("returnCode"))) {
throw new GlobalException(response.get("respMsg"));
}
String bizContent = response.get("biz_content");
JSONObject object = JSONObject.parseObject(bizContent);
String qrCode = object.getString("qrCode");
if (!StringUtils.isEmpty(qrCode)) {
qrCode.replace("https://qr.95516.com", "http://payment-uat.cs.cmburl.cn");
}
return qrCode;
} catch (Exception e) {
throw new GlobalException(e.getMessage());
}
}
public Map<String, String> notifyUrl(String requestBodyString) {
Map<String, String> respData = new HashMap<>();
//设置响应数据
respData.put("version", "0.0.1");//版本号,固定为0.0.1(必传)
respData.put("encoding", "UTF-8");//编码方式,固定为UTF-8(必传)
respData.put("signMethod", "02");//签名方法,固定为01,表示签名方法为RSA2(必传)
String orderId = "";
try {
//非空校验
if (requestBodyString == null || "".equals(requestBodyString.trim())) {
respData.put("returnCode", "FAIL");
return respData;
}
Map<String, String> requestBodyMap = str2Map(requestBodyString);
Map<String, String> resultMap = requestBodyMap.entrySet().stream().collect(Collectors.toMap(e -> SignatureUtil.decode(e.getKey()), e -> SignatureUtil.decode(e.getValue())));
if (resultMap == null) {
respData.put("returnCode", "FAIL");
return respData;
}
//验证签名-使用招行公钥进行验签
ObjectMapper mapper = new ObjectMapper();
Boolean flag = checkSign(mapper.writeValueAsString(resultMap), recordConfig.getSm2PublicKey());
if (!flag) {
respData.put("returnCode", "FAIL");
respData.put("respMsg", "验签失败");
return respData;
}
String bizContent = resultMap.get("biz_content");
JSONObject object = JSONObject.parseObject(bizContent);
orderId = object.getString("orderId");
// 查询支付信息是否存在
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("ORDER_NO", orderId);
RechargeRecord rechargeRecord = rechargeRecordMapper.selectOne(queryWrapper);
if (rechargeRecord == null) {
respData.put("returnCode", "FAIL");
respData.put("respCode", "FAIL");
respData.put("respMsg", "支付信息不存在");
return respData;
}
if (rechargeRecord.getBlStatus() == 1) {
respData.put("returnCode", "SUCCESS");
respData.put("respCode", "SUCCESS");
respData.put("respMsg", "该订单已支付成功");
return respData;
}
// 3、验证充值金额是否相同
BigDecimal txnAmt = new BigDecimal(object.getString("txnAmt"));
txnAmt = txnAmt.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
BigDecimal arrivalAmount = rechargeRecord.getArrivalAmount();
if (txnAmt.compareTo(arrivalAmount) == -1) {
respData.put("returnCode", "FAIL");
respData.put("respCode", "FAIL");
respData.put("respMsg", "支付金额与实际支付金额不符");
return respData;
}
// 4、更新数据
rechargeRecord.setBlStatus(1);
rechargeRecord.setTradeNo(object.getString("cmbOrderId"));
Integer result = rechargeRecordMapper.updateById(rechargeRecord);
if (result <= 0) {
respData.put("returnCode", "FAIL");
respData.put("respCode", "FAIL");
respData.put("respMsg", "更新失败");
return respData;
}
respData.put("returnCode", "SUCCESS");
respData.put("respCode", "SUCCESS");
respData.put("respMsg", "更新成功");
//对待加签内容进行排序拼接
String signContent = SignatureUtil.getSignContent(respData);
//加签-使用商户私钥加签
String sign = SM2Util.sm2Sign(signContent, recordConfig.getSm2PrivateKey());
respData.put("sign", sign);
return respData;
} catch (Exception e) {
log.error(String.format("回调-单号%s;回调失败;失败原因:%s", orderId, e.getMessage()));
respData.put("returnCode", "FAIL");
return respData;
}
}
private String signMethod(RechargeRecord rechargeRecord) {
Map<String, String> requestPublicParams = new TreeMap<>();
String requestStr = "";
try {
BigDecimal arrivalAmount = rechargeRecord.getArrivalAmount();
BigDecimal txnAmt = arrivalAmount.multiply(new BigDecimal(100));// 转分
//公共请求参数
requestPublicParams.put("version", "0.0.1"); //版本号,固定为0.0.1(必传字段)
requestPublicParams.put("encoding", "UTF-8"); //编码方式,固定为UTF-8(必传)
requestPublicParams.put("signMethod", "02"); //签名方法,固定为01,表示签名方式为RSA2(必传)
//业务要素
Map<String, String> requestTransactionParams = new HashMap<>();
requestTransactionParams.put("body", "聚合支付测试"); //支付描述
requestTransactionParams.put("currencyCode", "156"); //交易币种,默认156,目前只支持人民币(156)
requestTransactionParams.put("merId", recordConfig.getMerId()); //商户号(必传)
requestTransactionParams.put("notifyUrl", recordConfig.getNotifyUrl()); //交易通知地址(必传)
requestTransactionParams.put("orderId", rechargeRecord.getOrderNo()); //商户订单号(必传)
requestTransactionParams.put("payValidTime", "1200"); //支付有效时间
requestTransactionParams.put("txnAmt", txnAmt.stripTrailingZeros().toPlainString()); //交易金额,单位为分(必传)
requestTransactionParams.put("userId", "N003399854"); //收银员
requestTransactionParams.put("tradeScene", "OFFLINE"); //支付场景
ObjectMapper mapper = new ObjectMapper();
requestPublicParams.put("biz_content", mapper.writeValueAsString(requestTransactionParams));
//私钥
String privateKey = recordConfig.getSm2PrivateKey();
//对待加签内容进行排序拼接
String signContent = SignatureUtil.getSignContent(requestPublicParams);
//加签
String sign = SM2Util.sm2Sign(signContent, privateKey);
requestPublicParams.put("sign", sign);
requestStr = mapper.writeValueAsString(requestPublicParams);
return requestStr;
} catch (Exception e) {
log.error(String.format("二维码-加签发生异常!单号:%s", rechargeRecord.getOrderNo()));
e.printStackTrace();
return requestStr;
}
}
public static Boolean checkSign(String string, String publicKey) {
try {
//验签
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> responseBodyMap = objectMapper.readValue(string, Map.class);
String sign = responseBodyMap.remove("sign");
String contentStr = SignatureUtil.getSignContent(responseBodyMap);
boolean result = SM2Util.sm2Check(contentStr, sign, publicKey);
if (result) {
log.info("报文验签成功!");
} else {
log.error("报文验签失败!");
}
return result;
} catch (Exception e) {
log.error("验签发生异常!");
e.printStackTrace();
return false;
}
}
private Map<String, String> str2Map(String str) {
Map<String, String> result = new HashMap<>();
String[] results = str.split("&");
if (results != null && results.length > 0) {
for (int var = 0; var < results.length; ++var) {
String pair = results[var];
String[] kv = pair.split("=", 2);
if (kv != null && kv.length == 2) {
result.put(kv[0], kv[1]);
}
}
}
return result;
}
}
5、定时任务
/**
* 定时任务推送模板消息(物流轨迹)
*
* @author langao_q
* @since 2021-03-01 15:27
*/
@Slf4j
@Component
public class RechargeRecordTask {
@Autowired
private RechargeRecordMapper rechargeRecordMapper;
@Autowired
private RechargeRecordConfig recordConfig;
/**
* 支付结果查询
* 1.先查出所有数据
* 2.回写支付信息
*/
@Scheduled(cron = "0 */1 * * * ?")
public void paymentResultQuery() {
List<RechargeRecord> rechargeRecordList = rechargeRecordMapper.getRechargeRecordList();
if (rechargeRecordList.size() == 0) {
log.error("没有拉到支付信息数据");
return;
}
for (RechargeRecord record : rechargeRecordList) {
try {
String appId = recordConfig.getAppId();
String appSecret = recordConfig.getAppSecret();
String signResult = signMethod(record);
ObjectMapper mapper = new ObjectMapper();
Map<String, String> signResultMap = mapper.readValue(signResult, Map.class);
// 组apiSign加密Map
Map<String, String> apiSign = new TreeMap<>();
apiSign.put("appid", appId);
apiSign.put("secret", appSecret);
apiSign.put("sign", signResultMap.get("sign"));
apiSign.put("timestamp", "" + System.currentTimeMillis() / 1000);
// MD5加密
String MD5Content = SignatureUtil.getSignContent(apiSign);
String apiSignString = MD5Utils.getMD5Content(MD5Content).toLowerCase();
// 组request头部Map
Map<String, String> apiHeader = new HashMap<>();
apiHeader.put("appid", appId);
apiHeader.put("timestamp", "" + System.currentTimeMillis() / 1000);
apiHeader.put("apisign", apiSignString);
// 发送HTTP post请求
Map<String, String> response = HttpUtils.postForEntity(recordConfig.getPaymentResultUrl(), signResult, apiHeader);
log.info(String.format("支付结果查询返回结果:%s;单号:%s", JSONObject.toJSONString(response), record.getOrderNo()));
// 返回结果验签
Boolean checkResult1 = RechargeRecordService.checkSign(mapper.writeValueAsString(response), recordConfig.getSm2PublicKey());
if (!checkResult1) {
log.error("验签失败");
return;
}
if ("FAIL".equals(response.get("returnCode"))) {
log.error(response.get("respMsg"));
return;
}
String bizContent = response.get("biz_content");
JSONObject object = JSONObject.parseObject(bizContent);
String respCode = response.get("respCode");
String tradeState = object.getString("tradeState");
if ("SUCCESS".equals(respCode) && "S".equals(tradeState)) {// 交易是否成功
BigDecimal txnAmt = new BigDecimal(object.getString("txnAmt"));
txnAmt = txnAmt.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
BigDecimal arrivalAmount = record.getArrivalAmount();
if (txnAmt.compareTo(arrivalAmount) == -1) {
log.error(String.format("定时任务-支付金额与实际支付金额不符;单号:%s", record.getOrderNo()));
return;
} else {
record.setBlStatus(1);
record.setTradeNo(object.getString("cmbOrderId"));
Integer result = rechargeRecordMapper.updateById(record);
if (result > 0) {
log.info(String.format("定时任务-处理成功;单号:%s", record.getOrderNo()));
} else {
log.error(String.format("定时任务-处理失败;单号:%s", record.getOrderNo()));
}
}
}
} catch (Exception e) {
log.error(String.format("定时任务-支付结果查询异常;单号:%s, 原因:%s", record.getOrderNo(), e.getMessage()));
return;
}
}
}
private String signMethod(RechargeRecord rechargeRecord) {
Map<String, String> requestPublicParams = new TreeMap<>();
String requestStr = "";
try {
//公共请求参数
requestPublicParams.put("version", "0.0.1"); //版本号,固定为0.0.1(必传字段)
requestPublicParams.put("encoding", "UTF-8"); //编码方式,固定为UTF-8(必传)
requestPublicParams.put("signMethod", "02"); //签名方法,固定为01,表示签名方式为RSA2(必传)
//业务要素
Map<String, String> requestTransactionParams = new HashMap<>();
requestTransactionParams.put("merId", recordConfig.getMerId()); //商户号(必传)
requestTransactionParams.put("orderId", rechargeRecord.getOrderNo()); //商户订单号(必传)
requestTransactionParams.put("userId", "N003399854"); //收银员
ObjectMapper mapper = new ObjectMapper();
requestPublicParams.put("biz_content", mapper.writeValueAsString(requestTransactionParams));
//私钥
String privateKey = recordConfig.getSm2PrivateKey();
//对待加签内容进行排序拼接
String signContent = SignatureUtil.getSignContent(requestPublicParams);
//加签
String sign = SM2Util.sm2Sign(signContent, privateKey);
requestPublicParams.put("sign", sign);
requestStr = mapper.writeValueAsString(requestPublicParams);
return requestStr;
} catch (Exception e) {
log.error(String.format("定时任务-加签发生异常!单号:%s;原因:%s", rechargeRecord.getOrderNo(), e.getMessage()));
return requestStr;
}
}
}
HttpUtils类
@Slf4j
public class HttpUtils {
public static Map<String, String> postForEntity(String url,String requestBody, Map<String, String> apiHeader){
RestTemplate client = getRestTemplate();
client.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
HttpHeaders headers = getHttpHeaders();
// 以json的方式提交
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("appid", apiHeader.get("appid"));
headers.add("timestamp", apiHeader.get("timestamp"));
headers.add("apisign", apiHeader.get("apisign"));
// 将请求头部和参数合成一个请求
HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);
// 执行HTTP请求
Map<String,String> response = client.postForEntity(url,requestEntity,Map.class).getBody();
return response;
}
public static RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static HttpHeaders getHttpHeaders(){
return new HttpHeaders();
}
}
以上便是对接招行二维码申请、支付结果回调、支付结果反查的全部内容
如有侵权联系我删除