| | |
| | | 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; |
| | |
| | | public class IcbcPayServiceImpl implements IcbcPayService { |
| | | @Resource |
| | | private IcbcPayProperties properties; |
| | | @Resource |
| | | private IcbcPaymentFlowRecorder flowRecorder; |
| | | |
| | | @Override |
| | | public IcbcPaymentResp payment(IcbcPaymentReq 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(firstNotBlank(response.getOrder_id(), response.getMsgId())); |
| | | 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) { |
| | | validateCommonConfig(); |
| | | validateConfig("icbc.pay.orderQueryUrl", properties.getOrderQueryUrl()); |
| | | 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)); |
| | |
| | | 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 IcbcRefundResp refund(IcbcRefundReq req) { |
| | | validateCommonConfig(); |
| | | validateConfig("icbc.pay.refundUrl", properties.getRefundUrl()); |
| | | IcbcRefundResp resp = new IcbcRefundResp(); |
| | | resp.setOutTradeNo(req.getOutTradeNo()); |
| | | resp.setOrderId(req.getOrderId()); |
| | | resp.setOutRefundNo(req.getOutRefundNo()); |
| | | 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)); |
| | |
| | | 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.getOutTradeNo(), req.getOutRefundNo(), 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) { |
| | | validateCommonConfig(); |
| | | validateConfig("icbc.pay.merPrtclNo", properties.getMerPrtclNo()); |
| | | validateConfig("icbc.pay.subOrderSplitUrl", properties.getSubOrderSplitUrl()); |
| | | IcbcSubOrderSplitResp resp = new IcbcSubOrderSplitResp(); |
| | | resp.setChargingOrderCode(req.getChargingOrderCode()); |
| | | resp.setSubOrderId(req.getSubOrderId()); |
| | | 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)); |
| | |
| | | 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.getChargingOrderCode(), 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) { |
| | | validateCommonConfig(); |
| | | validateConfig("icbc.pay.subOrderRefundUrl", properties.getSubOrderRefundUrl()); |
| | | IcbcSubOrderRefundResp resp = new IcbcSubOrderRefundResp(); |
| | | resp.setChargingOrderCode(req.getChargingOrderCode()); |
| | | resp.setSubOrderId(req.getSubOrderId()); |
| | | resp.setRetSubOrderId(req.getRetSubOrderId()); |
| | | resp.setOuttrxSerialNo(req.getOuttrxSerialNo()); |
| | | 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)); |
| | |
| | | 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.getChargingOrderCode(), req.getSubOrderId(), req.getOuttrxSerialNo(), 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) { |
| | | validateCommonConfig(); |
| | | validateConfig("icbc.pay.subOrderQueryUrl", properties.getSubOrderQueryUrl()); |
| | | 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)); |
| | |
| | | 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.getSubOrderNo(), 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; |
| | | } |
| | |
| | | String[] values = entry.getValue(); |
| | | params.put(entry.getKey(), values == null || values.length == 0 ? "" : String.join(",", values)); |
| | | } |
| | | if (!verifyNotify(params)) { |
| | | throw new IllegalArgumentException("工行回调验签失败"); |
| | | 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; |
| | | } |
| | | mergeBizContent(params); |
| | | String status = firstNotBlank(params.get("return_code"), params.get("orderStatus"), params.get("pay_status"), params.get("status")); |
| | | if (StringUtils.isNotEmpty(status) && !isPaidStatus(status)) { |
| | | throw new IllegalArgumentException("工行回调支付状态非成功:" + status); |
| | | } |
| | | |
| | | private Integer parseInteger(String value) { |
| | | try { |
| | | return StringUtils.isEmpty(value) ? null : Integer.valueOf(value); |
| | | } catch (NumberFormatException ignored) { |
| | | return null; |
| | | } |
| | | String outTradeNo = firstNotBlank(params.get("outTradeNo"), params.get("out_trade_no"), params.get("order_id")); |
| | | if (StringUtils.isEmpty(outTradeNo)) { |
| | | throw new IllegalArgumentException("工行回调缺少订单号"); |
| | | } |
| | | String tradeNo = firstNotBlank(params.get("tradeNo"), params.get("trade_no"), params.get("msg_id")); |
| | | String attach = firstNotBlank(params.get("attach"), params.get("paymentType"), params.get("payType")); |
| | | if (StringUtils.isEmpty(attach)) { |
| | | throw new IllegalArgumentException("工行回调缺少支付类型"); |
| | | } |
| | | Map<String, String> result = new HashMap<>(); |
| | | result.put("out_trade_no", outTradeNo); |
| | | result.put("transaction_id", tradeNo); |
| | | result.put("attach", attach); |
| | | return result; |
| | | } |
| | | |
| | | private DefaultIcbcClient buildClient() { |
| | |
| | | CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz biz = |
| | | new CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz(); |
| | | biz.setIcbcAppid(properties.getAppId()); |
| | | biz.setSerNo(req.getSerNo()); |
| | | biz.setSerNo(firstNotBlank(req.getSerNo(), properties.getServiceMerId(), properties.getMerId())); |
| | | biz.setOrderNoType(req.getOrderNoType()); |
| | | biz.setOrderId(req.getOrderId()); |
| | | biz.setMerOrderId(req.getMerOrderId()); |