package com.ruoyi.payment.icbc.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.icbc.api.DefaultIcbcClient;
|
import com.icbc.api.IcbcRequest;
|
import com.icbc.api.IcbcResponse;
|
import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineOrderqryRequestV2;
|
import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2;
|
import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1;
|
import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineSersuborderRequestV1;
|
import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1;
|
import com.icbc.api.request.CardbusinessOrderSuborderqueryRequestV1;
|
import com.icbc.api.internal.util.internal.util.fastjson.JSONArray;
|
import com.icbc.api.internal.util.internal.util.fastjson.JSONObject;
|
import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineOrderqryResponseV2;
|
import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV2;
|
import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineMerrefundResponseV1;
|
import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineSersuborderResponseV1;
|
import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineSersuborderrefundResponseV1;
|
import com.icbc.api.response.CardbusinessOrderSuborderqueryResponseV1;
|
import com.icbc.api.utils.IcbcSignature;
|
import com.ruoyi.common.core.utils.StringUtils;
|
import com.ruoyi.payment.api.vo.IcbcPaymentReq;
|
import com.ruoyi.payment.api.vo.IcbcPaymentResp;
|
import com.ruoyi.payment.api.vo.IcbcRefundReq;
|
import com.ruoyi.payment.api.vo.IcbcRefundResp;
|
import com.ruoyi.payment.api.vo.IcbcSubOrderQueryReq;
|
import com.ruoyi.payment.api.vo.IcbcSubOrderQueryResp;
|
import com.ruoyi.payment.api.vo.IcbcSubOrderRefundReq;
|
import com.ruoyi.payment.api.vo.IcbcSubOrderRefundResp;
|
import com.ruoyi.payment.api.vo.IcbcSubOrderSplitReq;
|
import com.ruoyi.payment.api.vo.IcbcSubOrderSplitResp;
|
import com.ruoyi.payment.icbc.config.IcbcPayProperties;
|
import com.ruoyi.payment.icbc.service.IcbcPaymentFlowRecorder;
|
import com.ruoyi.payment.icbc.service.IcbcPayService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.Resource;
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.text.SimpleDateFormat;
|
import java.util.Arrays;
|
import java.util.Date;
|
import java.util.HashMap;
|
import java.util.Map;
|
import java.util.TreeMap;
|
|
/**
|
* 工行聚合支付骨架实现。
|
*/
|
@Slf4j
|
@Service
|
public class IcbcPayServiceImpl implements IcbcPayService {
|
@Resource
|
private IcbcPayProperties properties;
|
@Resource
|
private IcbcPaymentFlowRecorder flowRecorder;
|
|
@Override
|
public IcbcPaymentResp payment(IcbcPaymentReq req) {
|
IcbcPaymentResp resp = new IcbcPaymentResp();
|
if (req != null) {
|
resp.setOrderId(req.getOutTradeNo());
|
}
|
try {
|
validateCommonConfig();
|
validateConfig("icbc.pay.gatewayUrl", properties.getGatewayUrl());
|
validatePaymentReq(req);
|
CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2 request = new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2();
|
request.setServiceUrl(properties.getGatewayUrl());
|
request.setBizContent(buildConsumePurchaseBiz(req));
|
flowRecorder.record("PAY_REQUEST", req.getOutTradeNo(), req.getPaymentType(), req.getAmount(),
|
request.getBizContent(), null, "SENT", null);
|
|
CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV2 response = executeIcbc("聚合支付下单", request);
|
resp.setOrderId(firstNotBlank(response.getOut_trade_no(), req.getOutTradeNo()));
|
resp.setTradeNo(response.getOrder_id());
|
resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
|
resp.setPayData(buildConsumePurchasePayData(response, req.getPaymentType()));
|
resp.setRawResponse(JSON.toJSONString(response));
|
flowRecorder.record("PAY_RESPONSE", resp.getOrderId(), req.getPaymentType(), req.getAmount(),
|
request.getBizContent(), response, resp.getStatus(), null);
|
} catch (Exception e) {
|
log.error("工行聚合支付调用失败,订单号:{}", req == null ? null : req.getOutTradeNo(), e);
|
resp.setStatus("FAIL");
|
resp.setRawResponse(e.getMessage());
|
flowRecorder.record("PAY_RESPONSE", req == null ? null : req.getOutTradeNo(), req == null ? null : req.getPaymentType(),
|
req == null ? null : req.getAmount(), req, null, "FAIL", e.getMessage());
|
}
|
return resp;
|
}
|
|
@Override
|
public IcbcPaymentResp query(String outTradeNo) {
|
IcbcPaymentResp resp = new IcbcPaymentResp();
|
resp.setOrderId(outTradeNo);
|
try {
|
validateCommonConfig();
|
validateConfig("icbc.pay.orderQueryUrl", properties.getOrderQueryUrl());
|
CardbusinessAggregatepayB2cOnlineOrderqryRequestV2 request = new CardbusinessAggregatepayB2cOnlineOrderqryRequestV2();
|
request.setServiceUrl(properties.getOrderQueryUrl());
|
request.setBizContent(buildOrderQueryBiz(outTradeNo));
|
CardbusinessAggregatepayB2cOnlineOrderqryResponseV2 response = executeIcbc("聚合支付订单查询", request);
|
resp.setOrderId(firstNotBlank(response.getOut_trade_no(), outTradeNo));
|
resp.setTradeNo(firstNotBlank(response.getOrder_id(), response.getThird_trade_no(), response.getMsgId()));
|
resp.setStatus(firstNotBlank(response.getOrderStatus(), response.getPay_status()));
|
resp.setPayData(JSON.toJSONString(response));
|
resp.setRawResponse(JSON.toJSONString(response));
|
flowRecorder.record("QUERY", outTradeNo, null, null, buildOrderQueryBiz(outTradeNo), response,
|
response.isSuccess() ? "SUCCESS" : "FAIL", response.isSuccess() ? null : response.getReturnMsg());
|
} catch (Exception e) {
|
log.error("工行支付订单查询失败,订单号:{}", outTradeNo, e);
|
resp.setStatus("QUERY_FAIL");
|
resp.setRawResponse(e.getMessage());
|
flowRecorder.record("QUERY", outTradeNo, null, null, null, null, "FAIL", e.getMessage());
|
}
|
return resp;
|
}
|
|
@Override
|
public IcbcPaymentResp close(String outTradeNo) {
|
IcbcPaymentResp resp = new IcbcPaymentResp();
|
resp.setOrderId(outTradeNo);
|
resp.setStatus("CLOSE_TODO");
|
resp.setRawResponse("ICBC close skeleton: SDK close request is not mapped yet");
|
log.info("工行关闭订单骨架,订单号:{}", outTradeNo);
|
return resp;
|
}
|
|
@Override
|
public IcbcRefundResp refund(IcbcRefundReq req) {
|
IcbcRefundResp resp = new IcbcRefundResp();
|
if (req != null) {
|
resp.setOutTradeNo(req.getOutTradeNo());
|
resp.setOrderId(req.getOrderId());
|
resp.setOutRefundNo(req.getOutRefundNo());
|
}
|
try {
|
validateCommonConfig();
|
validateConfig("icbc.pay.refundUrl", properties.getRefundUrl());
|
if (req == null) {
|
throw new IllegalArgumentException("工行退款请求不能为空");
|
}
|
CardbusinessAggregatepayB2cOnlineMerrefundRequestV1 request = new CardbusinessAggregatepayB2cOnlineMerrefundRequestV1();
|
request.setServiceUrl(properties.getRefundUrl());
|
request.setBizContent(buildRefundBiz(req));
|
CardbusinessAggregatepayB2cOnlineMerrefundResponseV1 response = executeIcbc("线上POS退款", request);
|
resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
|
resp.setRawResponse(JSON.toJSONString(response));
|
resp.setOutTradeNo(firstNotBlank(response.getOutTradeNo(), req.getOutTradeNo()));
|
resp.setOrderId(firstNotBlank(response.getOrderId(), req.getOrderId()));
|
resp.setOutRefundNo(firstNotBlank(response.getOuttrxSerialNo(), req.getOutRefundNo()));
|
resp.setRefundId(firstNotBlank(response.getIntrx_serial_no(), response.getOuttrxSerialNo()));
|
resp.setRefundTime(response.getRefund_time());
|
resp.setRefundAmount(firstNotBlank(response.getRealRejectAmt(), response.getRejectAmt(), toFen(req.getRefundAmount())));
|
flowRecorder.record("REFUND", req.getOutTradeNo(), null, req.getRefundAmount(), request.getBizContent(), response,
|
resp.getStatus(), null);
|
} catch (Exception e) {
|
log.error("工行退款失败,订单号:{},退款单号:{}",
|
req == null ? null : req.getOutTradeNo(), req == null ? null : req.getOutRefundNo(), e);
|
resp.setStatus("FAIL");
|
resp.setRawResponse(e.getMessage());
|
flowRecorder.record("REFUND", req == null ? null : req.getOutTradeNo(), null, req == null ? null : req.getRefundAmount(),
|
req, null, "FAIL", e.getMessage());
|
}
|
return resp;
|
}
|
|
@Override
|
public IcbcSubOrderSplitResp splitSubOrder(IcbcSubOrderSplitReq req) {
|
IcbcSubOrderSplitResp resp = new IcbcSubOrderSplitResp();
|
if (req != null) {
|
resp.setChargingOrderCode(req.getChargingOrderCode());
|
resp.setSubOrderId(req.getSubOrderId());
|
}
|
try {
|
validateCommonConfig();
|
validateConfig("icbc.pay.merPrtclNo", properties.getMerPrtclNo());
|
validateConfig("icbc.pay.subOrderSplitUrl", properties.getSubOrderSplitUrl());
|
if (req == null) {
|
throw new IllegalArgumentException("工行分账请求不能为空");
|
}
|
CardbusinessAggregatepayB2cOnlineSersuborderRequestV1 request = new CardbusinessAggregatepayB2cOnlineSersuborderRequestV1();
|
request.setServiceUrl(properties.getSubOrderSplitUrl());
|
request.setBizContent(buildSubOrderSplitBiz(req));
|
CardbusinessAggregatepayB2cOnlineSersuborderResponseV1 response = executeIcbc("B2C小订单拆分", request);
|
resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
|
resp.setRawResponse(JSON.toJSONString(response));
|
fillSplitOutput(resp, response.getOrderSubmitActOutput());
|
flowRecorder.record("SPLIT", req.getChargingOrderCode(), null, req.getClassifyAmt(), request.getBizContent(), response,
|
resp.getStatus(), null);
|
} catch (Exception e) {
|
log.error("工行小订单拆分失败,订单号:{}", req == null ? null : req.getChargingOrderCode(), e);
|
resp.setStatus("FAIL");
|
resp.setRawResponse(e.getMessage());
|
flowRecorder.record("SPLIT", req == null ? null : req.getChargingOrderCode(), null, req == null ? null : req.getClassifyAmt(),
|
req, null, "FAIL", e.getMessage());
|
}
|
return resp;
|
}
|
|
@Override
|
public IcbcSubOrderRefundResp refundSubOrder(IcbcSubOrderRefundReq req) {
|
IcbcSubOrderRefundResp resp = new IcbcSubOrderRefundResp();
|
if (req != null) {
|
resp.setChargingOrderCode(req.getChargingOrderCode());
|
resp.setSubOrderId(req.getSubOrderId());
|
resp.setRetSubOrderId(req.getRetSubOrderId());
|
resp.setOuttrxSerialNo(req.getOuttrxSerialNo());
|
}
|
try {
|
validateCommonConfig();
|
validateConfig("icbc.pay.subOrderRefundUrl", properties.getSubOrderRefundUrl());
|
if (req == null) {
|
throw new IllegalArgumentException("工行分账退款请求不能为空");
|
}
|
CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1 request = new CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1();
|
request.setServiceUrl(properties.getSubOrderRefundUrl());
|
request.setBizContent(buildSubOrderRefundBiz(req));
|
CardbusinessAggregatepayB2cOnlineSersuborderrefundResponseV1 response = executeIcbc("B2C小订单拆分退款", request);
|
resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
|
resp.setRawResponse(JSON.toJSONString(response));
|
resp.setOrderId(response.getOrder_id());
|
resp.setSubOrderSeq(response.getSub_order_seq());
|
resp.setSubOrderTrxid(response.getSub_order_trxid());
|
resp.setRefundAmount(toFen(req.getRefundAmount()));
|
flowRecorder.record("SPLIT_REFUND", req.getChargingOrderCode(), null, req.getRefundAmount(), request.getBizContent(), response,
|
resp.getStatus(), null);
|
} catch (Exception e) {
|
log.error("工行小订单拆分退款失败,订单号:{},子订单号:{},退款流水:{}",
|
req == null ? null : req.getChargingOrderCode(), req == null ? null : req.getSubOrderId(),
|
req == null ? null : req.getOuttrxSerialNo(), e);
|
resp.setStatus("FAIL");
|
resp.setRawResponse(e.getMessage());
|
flowRecorder.record("SPLIT_REFUND", req == null ? null : req.getChargingOrderCode(), null,
|
req == null ? null : req.getRefundAmount(), req, null, "FAIL", e.getMessage());
|
}
|
return resp;
|
}
|
|
@Override
|
public IcbcSubOrderQueryResp querySubOrder(IcbcSubOrderQueryReq req) {
|
IcbcSubOrderQueryResp resp = new IcbcSubOrderQueryResp();
|
try {
|
validateCommonConfig();
|
validateConfig("icbc.pay.subOrderQueryUrl", properties.getSubOrderQueryUrl());
|
if (req == null) {
|
throw new IllegalArgumentException("工行子订单查询请求不能为空");
|
}
|
CardbusinessOrderSuborderqueryRequestV1 request = new CardbusinessOrderSuborderqueryRequestV1();
|
request.setServiceUrl(properties.getSubOrderQueryUrl());
|
request.setBizContent(buildSubOrderQueryBiz(req));
|
CardbusinessOrderSuborderqueryResponseV1 response = executeIcbc("子订单查询", request);
|
resp.setOrderStatus(response.getOrderStatus());
|
resp.setSubOutMerNo(response.getSubOutMerNo());
|
resp.setSubOutAgreeNo(response.getSubOutAgreeNo());
|
resp.setOutTradeNo(response.getOutTradeNo());
|
resp.setOrderId(response.getOrderId());
|
resp.setSubOrderNo(response.getSubOrderNo());
|
resp.setSubMerOrderId(response.getSubMerOrderId());
|
resp.setOrderAmnt(response.getOrderAmnt());
|
resp.setOrderRefundAmnt(response.getOrderRefundAmnt());
|
resp.setPayDate(response.getPayDate());
|
resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
|
resp.setRawResponse(JSON.toJSONString(response));
|
flowRecorder.record("SPLIT_QUERY", firstNotBlank(req.getMerOrderId(), req.getOrderId(), req.getSubOrderNo()), null, null, request.getBizContent(), response,
|
resp.getStatus(), null);
|
} catch (Exception e) {
|
log.error("工行子订单查询失败,子订单号:{}", req == null ? null : req.getSubOrderNo(), e);
|
resp.setStatus("FAIL");
|
resp.setRawResponse(e.getMessage());
|
flowRecorder.record("SPLIT_QUERY", req == null ? null : firstNotBlank(req.getMerOrderId(), req.getOrderId(), req.getSubOrderNo()), null, null,
|
req, null, "FAIL", e.getMessage());
|
}
|
return resp;
|
}
|
|
@Override
|
public Map<String, String> parseNotify(Map<String, String[]> requestParams) {
|
Map<String, String> params = new HashMap<>();
|
for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
|
String[] values = entry.getValue();
|
params.put(entry.getKey(), values == null || values.length == 0 ? "" : String.join(",", values));
|
}
|
try {
|
if (!verifyNotify(params)) {
|
throw new IllegalArgumentException("工行回调验签失败");
|
}
|
mergeBizContent(params);
|
Map<String, String> result = IcbcNotifyParser.parseVerified(params);
|
flowRecorder.record("PAY_NOTIFY", result.get("out_trade_no"), parseInteger(result.get("attach")), null,
|
params, result, "SUCCESS", null);
|
return result;
|
} catch (RuntimeException e) {
|
flowRecorder.record("PAY_NOTIFY", firstNotBlank(params.get("out_trade_no"), params.get("outTradeNo")),
|
parseInteger(firstNotBlank(params.get("attach"), params.get("paymentType"))), null,
|
params, null, "FAIL", e.getMessage());
|
throw e;
|
}
|
}
|
|
private Integer parseInteger(String value) {
|
try {
|
return StringUtils.isEmpty(value) ? null : Integer.valueOf(value);
|
} catch (NumberFormatException ignored) {
|
return null;
|
}
|
}
|
|
private DefaultIcbcClient buildClient() {
|
return new DefaultIcbcClient(
|
properties.getAppId(),
|
properties.getSignType(),
|
properties.getPrivateKey(),
|
properties.getIcbcPublicKey());
|
}
|
|
private <T extends IcbcResponse> T executeIcbc(String scene, IcbcRequest<T> request) throws Exception {
|
log.info("工行{}请求,url:{},bizContent:{}", scene, request.getServiceUrl(), JSON.toJSONString(request.getBizContent()));
|
T response = buildClient().execute(request, System.currentTimeMillis() + "");
|
log.info("工行{}响应:{}", scene, JSON.toJSONString(response));
|
return response;
|
}
|
|
private void validateCommonConfig() {
|
validateConfig("icbc.pay.appId", properties.getAppId());
|
validateConfig("icbc.pay.privateKey", properties.getPrivateKey());
|
validateConfig("icbc.pay.icbcPublicKey", properties.getIcbcPublicKey());
|
validateConfig("icbc.pay.merId", properties.getMerId());
|
}
|
|
private void validateConfig(String name, String value) {
|
if (StringUtils.isEmpty(value)) {
|
throw new IllegalStateException("工行配置缺失:" + name);
|
}
|
}
|
|
private void validatePaymentReq(IcbcPaymentReq req) {
|
if (req == null) {
|
throw new IllegalArgumentException("工行支付请求不能为空");
|
}
|
validateConfig("outTradeNo", req.getOutTradeNo());
|
if (req.getAmount() == null || req.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
|
throw new IllegalArgumentException("工行支付金额必须大于0");
|
}
|
if (Integer.valueOf(1).equals(req.getPaymentType()) || Integer.valueOf(2).equals(req.getPaymentType())) {
|
validateConfig("icbc.pay." + (Integer.valueOf(1).equals(req.getPaymentType()) ? "wxTpAppId" : "aliTpAppId"), getTpAppId(req.getPaymentType()));
|
}
|
if (Integer.valueOf(1).equals(req.getPaymentType()) || Integer.valueOf(2).equals(req.getPaymentType())) {
|
validateConfig("openId", req.getOpenId());
|
}
|
}
|
|
private CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2Biz buildConsumePurchaseBiz(IcbcPaymentReq req) {
|
CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2Biz biz =
|
new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2Biz();
|
biz.setMer_id(properties.getMerId());
|
setMerPrtclNoIfPresent(biz::setMer_prtcl_no);
|
biz.setOut_trade_no(req.getOutTradeNo());
|
biz.setIcbc_appid(properties.getAppId());
|
biz.setShop_appid(getTpAppId(req.getPaymentType()));
|
if (Integer.valueOf(1).equals(req.getPaymentType()) || Integer.valueOf(2).equals(req.getPaymentType())) {
|
biz.setOpen_id(req.getOpenId());
|
}
|
setAlipayUnionIdIfNeeded(biz, req);
|
biz.setPay_mode(toIcbcPayMode(req.getPaymentType()));
|
biz.setAccess_type(toIcbcAccessType(req.getPaymentType()));
|
biz.setOrig_date_time(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date()));
|
biz.setDecive_info(buildDeviceInfo(req));
|
biz.setBody(firstNotBlank(req.getBody(), req.getSubject(), "充电充值"));
|
biz.setFee_type("001");
|
biz.setTotal_fee(toFen(req.getAmount()));
|
biz.setSpbill_create_ip(properties.getSpbillCreateIp());
|
biz.setMer_url(StringUtils.isNotEmpty(properties.getNotifyUrl()) ? properties.getNotifyUrl() : req.getNotifyUrl());
|
biz.setReturn_url(req.getReturnUrl());
|
biz.setAttach(String.valueOf(req.getPaymentType()));
|
biz.setNotify_type("HS");
|
biz.setResult_type("0");
|
biz.setOrder_apd_inf(req.getSubject());
|
return biz;
|
}
|
|
private void setAlipayUnionIdIfNeeded(CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2Biz biz,
|
IcbcPaymentReq req) {
|
if (!Integer.valueOf(2).equals(req.getPaymentType()) && !Integer.valueOf(3).equals(req.getPaymentType())) {
|
return;
|
}
|
if (StringUtils.isNotEmpty(req.getUnionId())) {
|
biz.setUnion_id(truncate(req.getUnionId(), 16));
|
}
|
}
|
|
private String buildConsumePurchasePayData(CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV2 response, Integer paymentType) {
|
Map<String, Object> payData = new HashMap<>();
|
payData.put("orderId", response.getOrder_id());
|
payData.put("outTradeNo", response.getOut_trade_no());
|
payData.put("totalAmt", response.getTotal_amt());
|
payData.put("payMode", response.getPay_mode());
|
payData.put("accessType", response.getAccess_type());
|
payData.put("tradeType", response.getTrade_type());
|
payData.put("wxDataPackage", response.getWx_data_package());
|
payData.put("zfbDataPackage", response.getZfb_data_package());
|
payData.put("unionDataPackage", response.getUnion_data_package());
|
if (Integer.valueOf(1).equals(paymentType) && StringUtils.isNotEmpty(response.getWx_data_package())) {
|
payData.put("package", response.getWx_data_package());
|
}
|
if ((Integer.valueOf(2).equals(paymentType) || Integer.valueOf(3).equals(paymentType)) && StringUtils.isNotEmpty(response.getZfb_data_package())) {
|
payData.put("package", response.getZfb_data_package());
|
}
|
return JSON.toJSONString(payData);
|
}
|
|
private CardbusinessAggregatepayB2cOnlineOrderqryRequestV2.CardbusinessAggregatepayB2cOnlineOrderqryRequestV2Biz buildOrderQueryBiz(String outTradeNo) {
|
CardbusinessAggregatepayB2cOnlineOrderqryRequestV2.CardbusinessAggregatepayB2cOnlineOrderqryRequestV2Biz biz =
|
new CardbusinessAggregatepayB2cOnlineOrderqryRequestV2.CardbusinessAggregatepayB2cOnlineOrderqryRequestV2Biz();
|
biz.setOut_trade_no(outTradeNo);
|
biz.setDeal_flag("0");
|
biz.setIcbc_appid(properties.getAppId());
|
biz.setMer_id(properties.getMerId());
|
setMerPrtclNoIfPresent(biz::setMer_prtcl_no);
|
return biz;
|
}
|
|
private CardbusinessAggregatepayB2cOnlineMerrefundRequestV1.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1Biz buildRefundBiz(IcbcRefundReq req) {
|
CardbusinessAggregatepayB2cOnlineMerrefundRequestV1.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1Biz biz =
|
new CardbusinessAggregatepayB2cOnlineMerrefundRequestV1.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1Biz();
|
biz.setOrder_id(req.getOrderId());
|
biz.setOut_trade_no(req.getOutTradeNo());
|
biz.setOuttrx_serial_no(req.getOutRefundNo());
|
biz.setRet_total_amt(toFen(req.getRefundAmount()));
|
biz.setTrnsc_ccy("001");
|
biz.setMer_id(properties.getMerId());
|
setMerPrtclNoIfPresent(biz::setMer_prtcl_no);
|
biz.setIcbc_appid(properties.getAppId());
|
biz.setMer_acct(req.getMerAcct());
|
biz.setOrder_apd_inf(req.getReason());
|
return biz;
|
}
|
|
private CardbusinessAggregatepayB2cOnlineSersuborderRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderRequestV1Biz buildSubOrderSplitBiz(IcbcSubOrderSplitReq req) {
|
CardbusinessAggregatepayB2cOnlineSersuborderRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderRequestV1Biz biz =
|
new CardbusinessAggregatepayB2cOnlineSersuborderRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderRequestV1Biz();
|
JSONArray orderSubmitActInput = new JSONArray();
|
JSONObject item = new JSONObject();
|
item.put("rec_num", "1");
|
item.put("oper_flag", "0");
|
item.put("sub_order_id", req.getSubOrderId());
|
item.put("busi_type", firstNotBlank(req.getBusiType(), "1"));
|
item.put("seq_no", req.getSeqNo());
|
item.put("ori_mer_id", firstNotBlank(req.getOriMerId(), properties.getMerId()));
|
item.put("ori_trx_date", req.getOriTrxDate());
|
item.put("sub_mer_id", req.getSubMerId());
|
item.put("sub_mer_prtcl_no", req.getSubMerPrtclNo());
|
item.put("classify_amt", toFen(req.getClassifyAmt()));
|
item.put("mer_acct", req.getMerAcct());
|
item.put("mer_sp_infor", req.getMerSpInfor());
|
item.put("term_id", req.getTermId());
|
orderSubmitActInput.add(item);
|
biz.setMerId(firstNotBlank(properties.getServiceMerId(), properties.getMerId()));
|
biz.setMerPrtclNo(firstNotBlank(properties.getServiceMerPrtclNo(), properties.getMerPrtclNo()));
|
biz.setIcbcAppid(properties.getAppId());
|
biz.setOrderNum("1");
|
biz.setOrderSubmitActInput(orderSubmitActInput);
|
return biz;
|
}
|
|
private CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1Biz buildSubOrderRefundBiz(IcbcSubOrderRefundReq req) {
|
CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1Biz biz =
|
new CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1Biz();
|
biz.setMerId(firstNotBlank(properties.getServiceMerId(), properties.getMerId()));
|
biz.setMerPrtclNo(firstNotBlank(properties.getServiceMerPrtclNo(), properties.getMerPrtclNo()));
|
biz.setIcbcAppid(properties.getAppId());
|
biz.setOperFlag("0");
|
biz.setSubOrderId(req.getSubOrderId());
|
biz.setSeqNo(req.getSeqNo());
|
biz.setBusiType(firstNotBlank(req.getBusiType(), "1"));
|
biz.setOriTrxDate(req.getOriTrxDate());
|
biz.setOriMerId(firstNotBlank(req.getOriMerId(), properties.getMerId()));
|
biz.setSubMerId(req.getSubMerId());
|
biz.setSubMerPrtclNo(req.getSubMerPrtclNo());
|
biz.setClassifyAmt(toFen(req.getRefundAmount()));
|
biz.setRetSubOrderId(req.getRetSubOrderId());
|
biz.setOuttrxSerialNo(req.getOuttrxSerialNo());
|
biz.setRefundSource(req.getRefundSource());
|
biz.setMerAcct(req.getMerAcct());
|
biz.setMerSpInfor(req.getMerSpInfor());
|
biz.setTermId(req.getTermId());
|
return biz;
|
}
|
|
private CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz buildSubOrderQueryBiz(IcbcSubOrderQueryReq req) {
|
CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz biz =
|
new CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz();
|
biz.setIcbcAppid(properties.getAppId());
|
biz.setSerNo(firstNotBlank(req.getSerNo(), properties.getServiceMerId(), properties.getMerId()));
|
biz.setOrderNoType(req.getOrderNoType());
|
biz.setOrderId(req.getOrderId());
|
biz.setMerOrderId(req.getMerOrderId());
|
biz.setPrtiId(firstNotBlank(req.getPrtiId(), properties.getMerId()));
|
biz.setQueryType(req.getQueryType());
|
biz.setSubOrderNo(req.getSubOrderNo());
|
biz.setMerTrnscSerno(req.getMerTrnscSerno());
|
biz.setSubPrtiId(req.getSubPrtiId());
|
return biz;
|
}
|
|
private void fillSplitOutput(IcbcSubOrderSplitResp resp, JSONArray output) {
|
if (output == null || output.isEmpty()) {
|
return;
|
}
|
JSONObject item = output.getJSONObject(0);
|
resp.setRecResult(item.getString("rec_result"));
|
resp.setClassifyAmt(item.getString("classify_amt"));
|
resp.setSubMerId(item.getString("sub_mer_id"));
|
resp.setOrderId(item.getString("order_id"));
|
resp.setSubOrderSeq(item.getString("sub_order_seq"));
|
resp.setSubOrderTrxid(item.getString("sub_order_trxid"));
|
resp.setSubOrderId(firstNotBlank(item.getString("sub_order_id"), resp.getSubOrderId()));
|
if (!"0".equals(resp.getRecResult())) {
|
resp.setStatus("FAIL");
|
}
|
}
|
|
private String toFen(BigDecimal amount) {
|
return amount == null ? "0" : amount.multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).toPlainString();
|
}
|
|
private String getTpAppId(Integer paymentType) {
|
if (Integer.valueOf(1).equals(paymentType)) {
|
return firstNotBlank(properties.getWxTpAppId(), properties.getTpAppId());
|
}
|
if (isAlipay(paymentType)) {
|
return firstNotBlank(properties.getAliTpAppId(), properties.getTpAppId());
|
}
|
return properties.getTpAppId();
|
}
|
|
private boolean isAlipay(Integer paymentType) {
|
return Integer.valueOf(2).equals(paymentType) || Integer.valueOf(3).equals(paymentType);
|
}
|
|
private void setMerPrtclNoIfPresent(java.util.function.Consumer<String> setter) {
|
if (StringUtils.isNotEmpty(properties.getMerPrtclNo())) {
|
setter.accept(properties.getMerPrtclNo());
|
}
|
}
|
|
private String buildDeviceInfo(IcbcPaymentReq req) {
|
String value = firstNotBlank(req.getOpenId(), req.getUnionId(), "USER") + "_" + firstNotBlank(req.getDeviceInfo(), properties.getSpbillCreateIp(), "MINIPROGRAM");
|
return value.length() > 32 ? value.substring(0, 32) : value;
|
}
|
|
private String toIcbcPayMode(Integer paymentType) {
|
if (Integer.valueOf(1).equals(paymentType)) {
|
return "9";
|
}
|
if (Integer.valueOf(2).equals(paymentType) || Integer.valueOf(3).equals(paymentType)) {
|
return "10";
|
}
|
return "9";
|
}
|
|
private String toIcbcAccessType(Integer paymentType) {
|
if (Integer.valueOf(3).equals(paymentType)) {
|
return "7";
|
}
|
return "9";
|
}
|
|
private boolean verifyNotify(Map<String, String> params) {
|
if (Boolean.FALSE.equals(properties.getVerifyNotifySign())) {
|
return true;
|
}
|
String sign = params.get("sign");
|
if (StringUtils.isEmpty(sign)) {
|
return false;
|
}
|
try {
|
String content = buildSignContent(params);
|
String signType = firstNotBlank(params.get("sign_type"), properties.getSignType());
|
String publicKey = normalizePublicKey(properties.getIcbcPublicKey());
|
log.info("工行回调验签参数,signType:{},publicKeyLength:{},content:{}", signType, publicKey == null ? 0 : publicKey.length(), content);
|
return IcbcSignature.verify(content, signType, sign, properties.getCharset(), publicKey);
|
} catch (Exception e) {
|
log.error("工行回调验签异常", e);
|
return false;
|
}
|
}
|
|
private void mergeBizContent(Map<String, String> params) {
|
String bizContent = params.get("biz_content");
|
if (StringUtils.isEmpty(bizContent)) {
|
return;
|
}
|
try {
|
JSONObject biz = JSONObject.parseObject(bizContent);
|
if (biz == null) {
|
return;
|
}
|
for (Map.Entry<String, Object> entry : biz.entrySet()) {
|
if (entry.getValue() != null && !params.containsKey(entry.getKey())) {
|
params.put(entry.getKey(), String.valueOf(entry.getValue()));
|
}
|
}
|
} catch (Exception e) {
|
log.warn("工行回调biz_content解析失败,bizContent:{}", bizContent, e);
|
}
|
}
|
|
private String normalizePublicKey(String publicKey) {
|
if (publicKey == null) {
|
return null;
|
}
|
String normalized = publicKey;
|
String lower = normalized.toLowerCase();
|
int start = lower.indexOf("start public key");
|
int end = lower.indexOf("end public key");
|
if (start >= 0 && end > start) {
|
normalized = normalized.substring(start + "start public key".length(), end);
|
}
|
return normalized
|
.replace("-----BEGIN PUBLIC KEY-----", "")
|
.replace("-----END PUBLIC KEY-----", "")
|
.replace("#", "")
|
.replaceAll("\\s+", "");
|
}
|
|
private String buildSignContent(Map<String, String> params) {
|
Map<String, String> sorted = new TreeMap<>(params);
|
String api = sorted.remove("api");
|
sorted.remove("sign");
|
StringBuilder content = new StringBuilder(firstNotBlank(api, ""));
|
content.append('?');
|
boolean hasParam = false;
|
for (Map.Entry<String, String> entry : sorted.entrySet()) {
|
if (StringUtils.isEmpty(entry.getValue())) {
|
continue;
|
}
|
if (hasParam) {
|
content.append('&');
|
}
|
hasParam = true;
|
content.append(entry.getKey()).append('=').append(entry.getValue());
|
}
|
return content.toString();
|
}
|
|
private boolean isPaidStatus(String status) {
|
return Arrays.asList("1", "2", "SUCCESS", "PAID", "PAY_SUCCESS", "TRADE_SUCCESS").contains(status);
|
}
|
|
private String firstNotBlank(String... values) {
|
for (String value : values) {
|
if (StringUtils.isNotEmpty(value)) {
|
return value;
|
}
|
}
|
return "";
|
}
|
|
private String truncate(String value, int maxLength) {
|
if (StringUtils.isEmpty(value) || value.length() <= maxLength) {
|
return value;
|
}
|
return value.substring(0, maxLength);
|
}
|
}
|