1、开发准备概述:
1.1 微信直连官方文档地址:微信支付-开发者文档
微信环境准备参考1.1官方文档,支付思维导图如下:
2、开发代码
2.1 代码环境
- 开发语言:JAVA
- JDK版本:1.8
- MAVEN版本:3.8.1
- SpringBoot: 2.2.1(建议升级,这版本有漏洞,练手无所谓)
本文版本是基于老项目改造的,没有升级,建议可根据自身情况选择最新稳定版本的架构
2.2 支付依赖
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.15</version>
</dependency>
application.yml支付相关配置
#微信直连配置
wechat:
#商户API私钥
merchCertFilePath: /root/conf/beta/apiclient_key.pem
#微信支付的平台公钥,验签使用 生成秘钥方法:java -jar CertificateDownloader.jar -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}
wechatCertFilePath: /root/conf/beta/wechatpay_6FDF4C2B9EE0E9EC46CD85FE72AF2DA111116111.pem
mchId: 1411111111
#证书序列号
merchCertSerialNo: 4A844A858D1E95889D06E4AAD7276E4238111111
appKeyV3: 1C4FCC49C061111B321111115D38A111
appKeyV2: kjWEIXINZHILIANqazwxQA1111111111
callback:
pay: https://xxxx.cn/cashier/pay/wechatpay/callBack
refund: https://xxxx.cn/cashier/pay/wechatpay/refundCallBack
declare:
pfxPwd: 1411111111
pfxPath: /root/conf/beta/apiclient_cert.p12
2.3 支付代码
2.3.1 WechatPayController
package com.etone.backend.controller;
import com.alibaba.fastjson.JSONObject;
import com.etone.backend.service.CashierService;
import com.etone.backend.service.PayService;
import com.etone.backend.service.WechatPayService;
import com.etone.backend.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("pay/wechatpay/")
public class WechatPayController {
@Resource
private WechatPayService wechatPayService;
/**
* 1.微信直连--预下单接口
*/
@RequestMapping("wxPreOrder")
public Result wxPreOrder(@RequestBody JSONObject reqData){
return wechatPayService.wxPreOrder(reqData);
}
/**
* 2.微信直连--支付回调
*/
@RequestMapping("callBack")
public JSONObject wxCallback(HttpServletRequest request) {
JSONObject resp = new JSONObject(1);
try {
wechatPayService.wxCallback(request);
resp.put("result", "success");
}catch (Exception e) {
resp.put("result", "failed");
}
return resp;
}
/**
* 3.支付结果查询接口
*/
@RequestMapping("/queryWxPayOrder")
public Result queryWxPayOrder(@RequestBody JSONObject req){
return wechatPayService.queryWxPayOrder(req);
}
/**
* 4.退款接口
*/
@RequestMapping("/wxOrderRefund")
public Result wxOrderRefund(@RequestBody JSONObject req){
return wechatPayService.wxOrderRefund(req);
}
/**
* 5.微信直连对账文件处理
* 去微信下载对账文件,每天早上10点之前跑批,跑前一天的数据
*/
@RequestMapping("/generateTradeBillFile")
public Result generateTradeBillFile(@RequestBody JSONObject req){
return wechatPayService.generateTradeBillFile(req);
}
/**
* 6.直连报关
*/
@RequestMapping("/declareOrder")
public Result declareOrder(@RequestBody JSONObject req){
return wechatPayService.declareOrder(req);
}
/**
* 7.直连报关查询
*/
@RequestMapping("/queryDeclareOrder")
public Result queryDeclareOrder(@RequestBody JSONObject req){
return wechatPayService.queryDeclareOrder(req);
}
/**
* 8.直连报关重推
*/
@RequestMapping("/redeclareOrder")
public Result redeclareOrder(@RequestBody JSONObject req){
return wechatPayService.redeclareOrder(req);
}
/**
* 9.微信原始报文查询
*/
@RequestMapping("/queryWechatOriginalOrder")
public Result queryWechatOriginalOrder(@RequestBody JSONObject req){
return wechatPayService.queryWechatOriginalOrder(req);
}
}
2.3.2 WechatPayService
package com.etone.backend.service;
import com.alibaba.fastjson.JSONObject;
import com.etone.backend.vo.Result;
import javax.servlet.http.HttpServletRequest;
public interface WechatPayService {
/**
* 预下单
* @param reqData
* @return
*/
String wxPreOrder(JSONObject reqData) throws Exception;
/**
* 直连支付回调
* @param request
*/
JSONObject wxCallback(HttpServletRequest request);
/**
* 退款
* @param req
* @return
*/
Result wxOrderRefund(JSONObject req);
Result queryWxPayOrder(JSONObject req);
Result generateTradeBillFile(JSONObject req);
Result declareOrder(JSONObject req);
Result queryDeclareOrder(JSONObject req);
Result redeclareOrder(JSONObject req);
Result queryWechatOriginalOrder(JSONObject req);
}
2.3.3 WechatPayServiceImpl
package com.etone.backend.service.impl;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.support.csv.CSVParser;
import com.etone.backend.constant.Const;
import com.etone.backend.dto.PayCallbackDTO;
import com.etone.backend.exception.InnerPayException;
import com.etone.backend.mapper.WechatPayMapper;
import com.etone.backend.service.ConsumerService;
import com.etone.backend.service.DeclareService;
import com.etone.backend.service.GatewayItfService;
import com.etone.backend.service.WechatPayService;
import com.etone.backend.utils.wechat.*;
import com.etone.backend.vo.Result;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.billdownload.BillDownloadService;
import com.wechat.pay.java.service.billdownload.model.GetTradeBillRequest;
import com.wechat.pay.java.service.billdownload.model.QueryBillEntity;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.Status;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
@Service
@Slf4j
public class WechatPayServiceImpl implements WechatPayService {
@Resource
private ConsumerService consumerService;
@Resource
private IRedisService iRedisService;
@Value("${wechat.merchCertFilePath}")
private String privateKeyPath;
@Value("${wechat.wechatCertFilePath}")
private String wechatCertFilePath;
@Value("${wechat.mchId}")
private String merchantId;
@Value("${wechat.merchCertSerialNo}")
private String merchantSerialNumber;
@Value("${wechat.appKeyV3}")
private String apiV3key;
@Resource
private WechatPayMapper wechatPayMapper;
@Value("${wechat.callback.pay}")
private String wxPayCallBackUrl;
@Value("${wechat.callback.refund}")
private String wxRefundCallBackUrl;
@Resource
private DeclareService declareService;
@Resource
private GatewayItfService gatewayItfService;
@Override
public String wxPreOrder(JSONObject reqData) {
String merOrderNum=reqData.getString("mer_order_num");
log.info("{}|微信直连预下单接口请求 {}", merOrderNum, reqData);
//1.存订单
reqData.put("orderDate", DateUtil.format(new Date(),"yyyyMMdd"));
reqData.put("orderTime", DateUtil.format(new Date(),"HHmmss"));
reqData.put("backUrl",wxPayCallBackUrl);
reqData.put("sq_no",UUID.randomUUID().toString().replace("-",""));
reqData.put("mer_id",merchantId);
wechatPayMapper.savePayOrder(reqData);
//2.发送请求调微信直连
PrepayRequest prepayRequest=new PrepayRequest();
//应用ID 由微信生成的应用ID,全局唯一。请求基础下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的服务号APPID
//示例值:wxd678efh567hg6787
prepayRequest.setAppid(reqData.getString("sub_appid"));
//直连商户号string[1,32] 直连商户的商户号,由微信支付生成并下发 示例值:1230000109
prepayRequest.setMchid(merchantId);
//商品描述string[1,127] /示例值:Image形象店-深圳腾大-QQ公仔
prepayRequest.setDescription("海外购产品");
//商户订单号string[6,32] 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
prepayRequest.setOutTradeNo(merOrderNum);
//通知地址 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http
prepayRequest.setNotifyUrl(wxPayCallBackUrl);
//订单金额信息
Amount amount=new Amount();
//总金额 订单总金额,单位为分。
amount.setTotal(reqData.getInteger("tran_amt"));
//货币类型 CNY:人民币,境内商户号仅支持人民币
amount.setCurrency("CNY");
prepayRequest.setAmount(amount);
//支付者信息 用户标识 用户在直连商户appid下的唯一标识。 下单前需获取到用户的Openid
Payer payer=new Payer();
payer.setOpenid(reqData.getString("sub_openid"));
prepayRequest.setPayer(payer);
//原始请求报文
String payRequest=JSONObject.toJSONString(prepayRequest);
log.info("微信直连支付请求原始报文:{}",payRequest);
//获取证书
JsapiServiceExtension service=new JsapiServiceExtension.Builder().config(getConfig(merchantId)).build();
//请求微信预下单
PrepayWithRequestPaymentResponse prepayResponse=service.prepayWithRequestPayment(prepayRequest);
String prepayId=prepayResponse.getPackageVal().replace("prepay_id=","");
//原始响应报文
String payResponse="";
if(StringUtils.isNotBlank(prepayId)){
JSONObject params=new JSONObject();
params.put("prepay_id",prepayId);
payResponse=params.toJSONString();
}
log.info("微信直连预下单接口返回 {}", payResponse);
//保存原始报文
wechatPayMapper.updateWxRequestAndResponse(payRequest,payResponse,merOrderNum,prepayId);
//拼接返回参数
JSONObject result=new JSONObject();
result.put("appId",prepayResponse.getAppId());
result.put("paySign",prepayResponse.getPaySign());
result.put("nonceStr",prepayResponse.getNonceStr());
result.put("package",prepayResponse.getPackageVal());
result.put("timeStamp",prepayResponse.getTimeStamp());
result.put("signType",prepayResponse.getSignType());
reqData.put("pay_info",result.toString());
reqData.put("resp_code",Const.GATEWAY_STATUS_SUCCESS);
return reqData.toString();
}
private Config getConfig(String merchantId){
// 使用自动更新平台证书的RSA配置
// 建议将 config 作为单例或全局静态对象,避免重复的下载浪费系统资源
return new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
}
/**
* 微信返回应答不验签
* @param request
* @return
*/
private CloseableHttpResponse sendRequestwithValidator(HttpUriRequest request) {
log.info(">>>微信通道,开始发送http请求");
try {
PrivateKey privateKey = PemUtil.loadPrivateKey(Files.newInputStream(Paths.get(privateKeyPath)));
X509Certificate x509Certificate = PemUtil.loadCertificate(Files.newInputStream(Paths.get(wechatCertFilePath)));
List<X509Certificate> certList = new ArrayList<>();
certList.add(x509Certificate);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(merchantId, merchantSerialNumber, privateKey)
.withWechatPay(certList)
// 无需进行签名验证、通过withValidator((response) -> true)实现
.withValidator((response) -> true);
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
CloseableHttpClient httpClient = builder.build();
return httpClient.execute(request);
} catch (IOException e) {
e.printStackTrace();
log.error("IOException:{}",e.getMessage());
} catch (Exception e) {
e.printStackTrace();
log.error("Exception:{}",e.getMessage());
}
return null;
}
@Override
public JSONObject wxCallback(HttpServletRequest request) {
log.info(">>> 微信支付回调v3开始!");
JSONObject result=new JSONObject();
result.put("code","SUCCESS");
result.put("message","");
try {
//1 签名验证 解密回调内容
WechatResponseBeanV3 wechatResponseBeanV3 = decode(request);
if (wechatResponseBeanV3 == null) {
result.put("code","FAIL");
result.put("message","解密回调内容失败!");
return result;
}
String successTime=LocalDateTime.parse(wechatResponseBeanV3.getSuccess_time(), DateTimeFormatter.ISO_OFFSET_DATE_TIME).format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
JSONObject payOrder=new JSONObject();
payOrder.put("out_trade_no",wechatResponseBeanV3.getOut_trade_no());
payOrder.put("transaction_id",wechatResponseBeanV3.getTransaction_id());
payOrder.put("openid",wechatResponseBeanV3.getPayer().getOpenid());
payOrder.put("success_time",successTime);
payOrder.put("trade_state_desc",wechatResponseBeanV3.getTrade_state_desc());
payOrder.put("total",Long.valueOf(wechatResponseBeanV3.getAmount().getTotal()));
PayCallbackDTO payCallback = new PayCallbackDTO();
// 2. 更新订单状态
if(wechatResponseBeanV3.isPaySuccess()){
payOrder.put("orderStatus","11");
payCallback.setRespCode("0000");
payOrder.put("trade_state","21");
}else {
payOrder.put("orderStatus","12");
payCallback.setRespCode("9999");
payOrder.put("trade_state","22");
}
wechatPayMapper.updatePayOrder(payOrder);
//3. 回调业务
payCallback.setMerOrderNum(wechatResponseBeanV3.getOut_trade_no());
payCallback.setTranAmt(wechatResponseBeanV3.getAmount().getTotal());
payCallback.setPaymentTime(successTime);
payCallback.setPayTypeChannel("WECHAT");
payCallback.setBussId("WXZL");
String redisKey = Const.RES_INNER_PAY_INFO + payCallback.getMerOrderNum();
Map payInfo = iRedisService.hmgetOrDefault(redisKey);
String redisStatus = (String) payInfo.get("status");
// 如果redis中记录的支付信息,已经是确定的成功或失败,说明已轮询主查,回调这里就不再处理了
if (Const.BusinessCode.SUCCESS_COMMON.getCode().equals(redisStatus)
|| Const.BusinessCode.ERROR_COMMON.getCode().equals(redisStatus)) {
return result;
}
JSONObject resp;
try {
// 回调三方
resp = consumerService.payCallback("bh", payCallback);
} catch (Exception e) {
throw new InnerPayException("回调三方业务系统异常");
}
String resCode = resp.getString("resCode");
if (Const.BusinessCode.SUCCESS_COMMON.getCode().equals(resCode)) {
//更新redis中状态
String status = Const.GATEWAY_STATUS_SUCCESS.equals(payCallback.getRespCode()) ? Const.BusinessCode.SUCCESS_COMMON.getCode() : Const.BusinessCode.ERROR_COMMON.getCode();
payInfo.put("status", status);
iRedisService.hmset(redisKey, payInfo);
} else {
throw new InnerPayException("三方回调处理异常");
}
} catch (Exception e) {
log.error("", e);
}
return result;
}
@Override
public Result wxOrderRefund(JSONObject reqData) {
log.info("微信直连退款接口请求参数:{}", reqData);
//商户号
String merId=reqData.getString("mer_id");
//原订单号
String merOrderNum=reqData.getString("old_mer_order_num");
//退款订单号
String refundOrderNum=reqData.getString("mer_order_num");
//订单状态
String orderStatus="11";
//查询下订单流水,入参订单号、商户号
JSONObject orderInfo= wechatPayMapper.queryPayOrderInfo(merOrderNum,merId,orderStatus);
if(orderInfo==null||orderInfo.isEmpty()){
return Result.error("订单不存在!");
}
Integer oldRefundAmt=Optional.ofNullable(orderInfo.getInteger("REFUND_AMT")).orElse(0);
//原订单金额
Long tranAmt=orderInfo.getLong("TRAN_AMT");
//退款金额
Long refundAmt=reqData.getLong("refund_amt");
//退款金额校验
if(refundAmt>tranAmt){
return Result.error("退款金额不可大于交易金额!");
}
if(refundAmt<1){
return Result.error("退款金额不可小于1分!");
}
//插入退款流水
//拼接退款参数
getRefundParams(orderInfo,refundAmt,refundOrderNum);
wechatPayMapper.saveRefundOrder(orderInfo);
JSONObject params = new JSONObject();
params.put("mer_order_num",refundOrderNum);
params.put("merch_id",merchantId);
try {
RefundService service=new RefundService.Builder().config(getConfig(merchantId)).build();
//金额
AmountReq amountReq=new AmountReq();
amountReq.setCurrency("CNY");
amountReq.setTotal(tranAmt);
amountReq.setRefund(refundAmt);
CreateRequest request = new CreateRequest();
request.setAmount(amountReq);
request.setOutTradeNo(merOrderNum);
request.setNotifyUrl(wxRefundCallBackUrl);
request.setOutRefundNo(refundOrderNum);
Refund response=service.create(request);
//拼接返回参数
reqData.remove("card_list");
reqData.put("tran_amt",tranAmt);
reqData.put("settle_date","");
reqData.put("req_refund_amt",oldRefundAmt);
reqData.put("resp_code","0000");
reqData.put("resp_msg","退款成功!");
String status=response.getStatus().name();
//status等于CLOSED或ABNORMAL,则为失败
if (Status.CLOSED.name().equals(status)||Status.ABNORMAL.name().equals(status)) {
//退款失败 22
params.put("channelOrderStatus","22");
//退款失败 12
params.put("orderStatus","12");
params.put("channelOrderMsg",status);
wechatPayMapper.updateRefundOrder(params);
reqData.put("resp_code","9999");
reqData.put("resp_msg","退款失败!");
return Result.res(Const.BusinessCode.ERROR_COMMON,reqData);
}
/**
* 校验金额一致、更新渠道订单号、渠道状态
* 退款中为退款成功
*/
if (Status.PROCESSING.name().equals(status)||Status.SUCCESS.name().equals(status)) {
if (refundAmt == response.getAmount().getRefund()) {
params.put("orderId",response.getOutRefundNo());
//退款成功 21
params.put("channelOrderStatus","21");
//退款成功 11
params.put("orderStatus","11");
params.put("channelOrderId",response.getRefundId());
wechatPayMapper.updateRefundOrder(params);
long newRefundAmt=BigDecimal.valueOf(oldRefundAmt).add(new BigDecimal(refundAmt)).longValue();
reqData.put("req_refund_amt",newRefundAmt);
//更新退款金额
wechatPayMapper.updateOrderRefundAmt(merOrderNum,newRefundAmt);
}
}
return Result.ok(reqData);
} catch (ServiceException e) {
//状态不等于200为失败
log.error("退款接口异常,退款订单号:{},异常信息:{}" ,refundOrderNum,e.getResponseBody());
//退款失败 22
params.put("channelOrderStatus","22");
//退款失败 12
params.put("orderStatus","12");
params.put("channelOrderMsg",e.getErrorMessage());
wechatPayMapper.updateRefundOrder(params);
reqData.put("resp_code","9999");
reqData.put("resp_msg",e.getErrorMessage());
return Result.res(Const.BusinessCode.ERROR_COMMON,reqData);
}
}
private void getRefundParams(JSONObject orderInfo,Long refundAmt,String refundOrderNum){
orderInfo.put("refundAmt",refundAmt);
orderInfo.put("refundOrderNum",refundOrderNum);
orderInfo.put("SQ_NO",UUID.randomUUID().toString().replace("-",""));
orderInfo.put("orderDate", DateUtil.format(new Date(),"yyyyMMdd"));
orderInfo.put("orderTime", DateUtil.format(new Date(),"HHmmss"));
orderInfo.put("backUrl",wxRefundCallBackUrl);
}
@Override
public Result queryWxPayOrder(JSONObject req) {
log.info(">>>微信查询支付订单接口");
//商户订单号
String out_trade_no=req.getString("mer_order_num");
try {
JsapiService jsapiService=new JsapiService.Builder().config(getConfig(merchantId)).build();
QueryOrderByOutTradeNoRequest request=new QueryOrderByOutTradeNoRequest();
request.setOutTradeNo(out_trade_no);
request.setMchid(merchantId);
//查询返回结果
Transaction result =jsapiService.queryOrderByOutTradeNo(request);
log.info("微信订单查询接口返回 {}", result);
JSONObject params = new JSONObject();
params.put("mer_order_num",out_trade_no);
params.put("merch_id",merchantId);
//描述
String message=result.getTradeStateDesc();
//状态
String trade_state=result.getTradeState().name();
//微信订单号
String transaction_id=result.getTransactionId();
//未支付
if (Transaction.TradeStateEnum.NOTPAY.name().equals(trade_state)) {
return Result.error(message);
}
/**
* 校验金额一致、更新订单状态
* 异常情况不用更新订单?
*/
if (Transaction.TradeStateEnum.SUCCESS.name().equals(trade_state)) {
params.put("txnFlag","00");
params.put("orderStatus","11");
params.put("orderId",out_trade_no);
params.put("channelOrderId",transaction_id);
params.put("channelOrderMsg",message);
wechatPayMapper.updateQueryResult(params);
return Result.ok(result);
} else {
return Result.error(message);
}
} catch (ServiceException e) {
log.error("微信直连订单查询异常,订单号:{},异常信息:{}",out_trade_no, e.getResponseBody());
return Result.error(e.getErrorMessage());
}
}
@Override
public Result generateTradeBillFile(JSONObject req) {
String tradeBillDate=req.getString("tradeBillDate");
if(StringUtils.isBlank(tradeBillDate)){
tradeBillDate=DateUtil.format(DateUtil.yesterday(), "yyyy-MM-dd");
}
try {
BillDownloadService service=new BillDownloadService.Builder().config(getConfig(merchantId)).build();
GetTradeBillRequest request = new GetTradeBillRequest();
request.setBillDate(tradeBillDate);
QueryBillEntity billEntity=service.getTradeBill(request);
JSONObject params = new JSONObject();
String downloadUrl=billEntity.getDownloadUrl();
params.put("download_url",downloadUrl);
HttpGet httpGetDown = new HttpGet(downloadUrl);
httpGetDown.addHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse responseDown = sendRequestwithValidator(httpGetDown);
String bodyString=EntityUtils.toString(responseDown.getEntity());
CSVParser parser=CSVParser.of(bodyString);
System.out.println("对账详情表头:"+Arrays.toString(parser.readLine()));
List<JSONObject> listDetail=new ArrayList<>();
String mer_id="";
Integer refundSum=0;
String create_time=DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
while(true){
String [] sf=parser.readLine();
if(sf!=null){
if("总交易单数".equals(sf[0])){
JSONObject total=new JSONObject();
sf=parser.readLine();
log.info("总交易数据{}",Arrays.toString(sf));
total.put("mer_id",mer_id);
total.put("trade_id",UUID.randomUUID().toString().replace("-",""));
total.put("settle_date",tradeBillDate);
total.put("trans_volume",sf[0].replace("`",""));
String transAmt=new BigDecimal(sf[1].replace("`","")).multiply(new BigDecimal("100")).setScale(0, RoundingMode.DOWN).toString();
String refundAmt=new BigDecimal(sf[2].replace("`","")).multiply(new BigDecimal("100")).setScale(0, RoundingMode.DOWN).toString();
//交易总金额=正向-逆向交易
String transAllAmt=new BigDecimal(transAmt).subtract(new BigDecimal(refundAmt)).toString();
total.put("trans_amt",transAllAmt);
//手续费
String charges=new BigDecimal(sf[4].replace("`","")).multiply(new BigDecimal("100")).setScale(0, RoundingMode.DOWN).toString();
total.put("charges",charges);
total.put("refund_volume",refundSum);
total.put("refund_amt",refundAmt);
//结算金额=交易总金额-手续费
String settle_amt=new BigDecimal(transAllAmt).subtract(new BigDecimal(charges)).toString();
total.put("settle_amt",settle_amt);
total.put("create_time",create_time);
wechatPayMapper.insertWxTradeBillTotal(total);
}else {
if(StringUtils.isBlank(mer_id)){
mer_id=sf[2].replace("`","");
}
JSONObject detail=new JSONObject();
detail.put("mer_id",sf[2].replace("`",""));
detail.put("trade_id",UUID.randomUUID().toString().replace("-",""));
String order_no=sf[6].replace("`","");
detail.put("tran_time",sf[0].replace("`",""));
detail.put("product_type","微信直连");
String tran_type="00";
//订单金额
String tran_amt=new BigDecimal(sf[24].replace("`","")).multiply(new BigDecimal("100")).setScale(0, RoundingMode.DOWN).toString();
if("REFUND".equals(sf[9].replace("`",""))){
tran_type="02";
refundSum=refundSum+1;
tran_amt=new BigDecimal(sf[16].replace("`","")).multiply(new BigDecimal("100")).setScale(0, RoundingMode.DOWN).toString();
order_no=sf[15].replace("`","");
}
detail.put("order_no",order_no);
detail.put("tran_type",tran_type);
detail.put("tran_channel","微信直连");
detail.put("tran_amt",tran_amt);
//手续费
String charges=new BigDecimal(sf[22].replace("`","")).multiply(new BigDecimal("100")).setScale(0, RoundingMode.DOWN).toString();
detail.put("charges",charges);
detail.put("charges_rates",sf[23].replace("`",""));
detail.put("t_charges","");
//结算金额=订单金额-手续费
detail.put("settle_amt",new BigDecimal(tran_amt).subtract(new BigDecimal(charges)).toString());
detail.put("create_time",create_time);
listDetail.add(detail);
}
}else {
break;
}
}
if(!listDetail.isEmpty()){
log.info("微信直连对账跑批数据:{}", listDetail);
wechatPayMapper.insertWxTradeBillDetail(listDetail);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return Result.ok();
}
@Override
public Result declareOrder(JSONObject req) {
log.info("报关请求参数:{}",req);
//根据订单号,查询商户号
JSONObject orderInfo=wechatPayMapper.queryMerIdByOrderNo(req.getString("out_trade_no"));
if(orderInfo==null||orderInfo.isEmpty()){
log.error("报关请求订单不存在!");
return Result.error("订单不存在!");
}
String mer_id=orderInfo.getString("MER_ID");
req.put("mer_id",mer_id);
String orderChannel=req.getString("order_channel");
if("WXZL".equals(orderChannel)){
//微信直连报关
return declareService.declareOrder(req);
}else {
//调用网关报关接口
String result=gatewayItfService.declare(req,ConstUtil.WG_DECLARE);
JSONObject declare=JSONObject.parseObject(result);
if(declare!=null && !declare.isEmpty()){
if(declare.containsKey("result_code")){
if("SUCCESS".equals(declare.getString("result_code"))){
return Result.ok(declare);
}
}
}
return Result.res("99","报关失败",declare);
}
}
@Override
public Result queryDeclareOrder(JSONObject req) {
String orderChannel=req.getString("order_channel");
//根据订单号,查询商户号
JSONObject orderInfo=wechatPayMapper.queryMerIdByOrderNo(req.getString("out_trade_no"));
if(orderInfo==null||orderInfo.isEmpty()){
log.error("报关查询订单不存在!");
return Result.error("订单不存在!");
}
if("WXZL".equals(orderChannel)){
//微信直连报关
return declareService.queryDeclareOrder(req);
}else {
//调用网关报关接口
String result=gatewayItfService.declare(req,ConstUtil.WG_QUERY_DECLARE);
JSONObject declare=JSONObject.parseObject(result);
if(declare!=null && !declare.isEmpty()){
if(declare.containsKey("result_code")){
if("SUCCESS".equals(declare.getString("result_code"))){
return Result.ok(declare);
}
}
}
return Result.res("99","报关查询失败",declare);
}
}
@Override
public Result redeclareOrder(JSONObject req) {
String orderChannel=req.getString("order_channel");
//根据订单号,查询商户号
JSONObject orderInfo=wechatPayMapper.queryMerIdByOrderNo(req.getString("out_trade_no"));
if(orderInfo==null||orderInfo.isEmpty()){
log.error("报关重推订单不存在!");
return Result.error("订单不存在!");
}
if("WXZL".equals(orderChannel)){
//微信直连报关
return declareService.redeclareOrder(req);
}else {
//调用网关报关接口
String result=gatewayItfService.declare(req,ConstUtil.WG_REDECLARE);
JSONObject declare=JSONObject.parseObject(result);
if(declare!=null && !declare.isEmpty()){
if(declare.containsKey("result_code")){
if("SUCCESS".equals(declare.getString("result_code"))){
return Result.ok(declare);
}
}
}
return Result.res("99","报关重推失败",declare);
}
}
@Override
public Result queryWechatOriginalOrder(JSONObject req) {
//订单号
String merOrderNo=req.getString("mer_order_num");
if(StringUtils.isBlank(merOrderNo)){
return Result.error("订单编号不可为空!");
}
//查询
JSONObject jsonObject= wechatPayMapper.queryPayOrderInfo(merOrderNo,merchantId,"11");
if(jsonObject==null|| jsonObject.isEmpty()){
return Result.error("未查询到订单!");
}
String wxPayRequest=jsonObject.getString("WX_PAY_REQUEST");
JSONObject result=new JSONObject();
result.put("WX_PAY_REQUEST",wxPayRequest);
result.put("WX_PAY_RESPONSE",jsonObject.getString("WX_PAY_RESPONSE"));
return Result.ok(result);
}
public WechatResponseBeanV3 decode(HttpServletRequest request) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String body = sb.toString();
if (StringUtils.isBlank(body)) {
log.error("请求内容为空");
return null;
}
String nonce = request.getHeader(WechatCryptUtil.Nonce);
String timestamp = request.getHeader(WechatCryptUtil.Timestamp);
String signature = request.getHeader(WechatCryptUtil.Signature);
String verifyMessage = timestamp + "\n" + nonce + "\n" + body + "\n";
log.info(">>>verifyMessage {}", verifyMessage);
boolean checkSign = WechatCryptUtil.verify(wechatCertFilePath, verifyMessage, signature);
if (checkSign) {
log.info(">>>微信回调验签成功");
WechatResponseBeanV3 wechatResponseBeanV3 = WechatCryptUtil.decodeNotify(body, apiV3key);
log.info(">>> wechatResponseBeanV3 {}", wechatResponseBeanV3);
return wechatResponseBeanV3;
}
log.info(">>>微信回调验签失败");
return null;
} catch (Exception e) {
log.info(">>>微信回调验签错误 {}", e.getMessage());
log.error("", e);
}
return null;
}
}
2.3.4 WechatPayMapper
package com.etone.backend.mapper;
import com.alibaba.fastjson.JSONObject;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
import java.util.List;
@Mapper
@Component
public interface WechatPayMapper {
void savePayOrder(JSONObject params);
void updatePayOrder(JSONObject params);
void updateRefundOrder(JSONObject params);
void updateQueryResult(JSONObject params);
JSONObject queryPayOrderInfo(String merOrderNum,String merId,String orderStatus);
void insertWxTradeBillTotal(JSONObject total);
void insertWxTradeBillDetail(@Param("list") List<JSONObject> list);
void saveRefundOrder(JSONObject orderInfo);
void updateWxRequestAndResponse(String payRequest,String payResponse,String merOrderNum,String prepayId);
void updateOrderRefundAmt(String merOrderNum, long newRefundAmt);
JSONObject queryDeclareOrder(String merOrderNo,String sub_order_no);
void saveDeclareOrder(JSONObject params);
void updateDeclareOrder(JSONObject jsonObject);
JSONObject queryMerIdByOrderNo(String outTradeNo);
}
2.3.5 WechatPayMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.etone.backend.mapper.WechatPayMapper">
<sql id="columns">
id, customer_id, trans_no, trans_channel, trans_acct, trans_acct_type,
trans_type, trans_time, trans_amt, trans_target, trans_status, trans_result, refund_voucher
</sql>
<insert id="savePayOrder">
insert into tbl_kq_txn_dtl (sq_no,order_id,channel,order_date,order_time,txn_flag,merch_id,mer_order_num,back_url,tran_amt,user_id,pay_type,app_id)
values (#{sq_no}, #{mer_order_num},#{channel},#{orderDate},#{orderTime}, '00', #{mer_id},#{mer_order_num}, #{backUrl}, #{tran_amt}, #{user_phone},'WXZL',#{sub_appid})
</insert>
<insert id="insertWxTradeBillTotal">
insert into t_wx_trade_bill_total
(trade_id, mer_id, settle_date, trans_volume, trans_amt, charges, refund_volume, refund_amt, settle_amt, create_time)
values
(#{trade_id}, #{mer_id}, #{settle_date}, #{trans_volume}, #{trans_amt}, #{charges}, #{refund_volume}, #{refund_amt}, #{settle_amt}, #{create_time})
</insert>
<insert id="insertWxTradeBillDetail" parameterType="list">
<foreach collection="list" item="detail" separator=";" open="begin" close=";end;">
insert into t_wx_trade_bill_detail
(trade_id, mer_id, order_no, tran_time, product_type, tran_type, tran_channel, tran_amt, charges, t_charges, settle_amt,charges_rates)
values
(#{detail.trade_id}, #{detail.mer_id}, #{detail.order_no}, #{detail.tran_time}, #{detail.product_type}, #{detail.tran_type}, #{detail.tran_channel}, #{detail.tran_amt}, #{detail.charges}, #{detail.t_charges}, #{detail.settle_amt},#{detail.charges_rates})
</foreach>
</insert>
<insert id="saveRefundOrder">
insert into tbl_kq_txn_dtl (sq_no,order_id,channel,order_date,order_time,txn_flag,merch_id,mer_order_num,back_url,tran_amt,user_id,pay_type,orig_mer_order_num,order_status)
values (#{SQ_NO}, #{refundOrderNum},#{CHANNEL},#{orderDate},#{orderTime}, '02', #{MERCH_ID},#{refundOrderNum}, #{backUrl}, #{refundAmt}, '','WXZL',#{MER_ORDER_NUM},'00')
</insert>
<insert id="saveDeclareOrder">
insert into tbl_declare_record
(event_id, state, sub_order_no, channel_type, merch_id, out_trade_no, transaction_id, req_date, req_time, customs, mch_customs_no, cert_type, cert_id, cert_name, order_fee, transport_fee, product_fee)
values
(#{event_id}, '', #{sub_order_no}, 'WXZL', #{merch_id}, #{out_trade_no}, #{transaction_id}, #{req_date}, #{req_time}, #{customs}, #{mch_customs_no}, #{cert_type}, #{cert_id}, #{cert_name}, #{order_fee}, #{transport_fee}, #{product_fee})
</insert>
<update id="updatePayOrder">
update tbl_kq_txn_dtl set
order_status = #{orderStatus}
<if test="transaction_id != null and transaction_id != ''">
,channel_Order_Id = #{transaction_id}
</if>
<if test="trade_state != null and trade_state != ''">
,channel_order_status = #{trade_state}
</if>
<if test="trade_state_desc != null and trade_state_desc != ''">
,channel_order_msg = #{trade_state_desc}
</if>
where mer_order_num=#{out_trade_no}
<if test="orderStatus =='12'">
and order_status <![CDATA[ <> '11' ]]>
</if>
and txn_flag='00'
</update>
<update id="updateRefundOrder">
update tbl_kq_txn_dtl set
order_status = #{orderStatus}
<if test="channelOrderId != null and channelOrderId != ''">
,channel_Order_Id = #{channelOrderId}
</if>
<if test="channelOrderStatus != null and channelOrderStatus != ''">
,channel_order_status = #{channelOrderStatus}
</if>
<if test="channelOrderMsg != null and channelOrderMsg != ''">
,channel_Order_msg = #{channelOrderMsg}
</if>
where mer_order_num=#{mer_order_num}
and merch_id=#{merch_id}
<if test="orderStatus =='12'">
and order_status <![CDATA[ <> '11' ]]>
</if>
and txn_flag='02'
</update>
<update id="updateQueryResult">
update tbl_kq_txn_dtl set
order_status = #{orderStatus}
<if test="qrSeq != null and qrSeq != ''">
,qr_seq = #{qrSeq}
</if>
<if test="reserve1 != null and reserve1 != ''">
,reserve1 = #{reserve1}
</if>
<if test="channelOrderId != null and channelOrderId != ''">
,channel_Order_Id = #{channelOrderId}
</if>
<if test="channelClientId != null and channelClientId != ''">
,channel_client_id = #{channelClientId}
</if>
<if test="channelOrderFinishTime != null and channelOrderFinishTime != ''">
,channel_Order_Finish_Time = #{channelOrderFinishTime}
</if>
<if test="channelOrderMsg != null and channelOrderMsg != ''">
,channel_Order_msg = #{channelOrderMsg}
</if>
where mer_order_num=#{mer_order_num}
and merch_id=#{merch_id}
<if test="orderId != null and orderId != ''">
and order_id=#{orderId}
</if>
<if test="txnFlag != null and txnFlag != ''">
and txn_flag=#{txnFlag}
</if>
<if test="orderStatus =='12'">
and order_status <![CDATA[ <> '11' ]]>
</if>
</update>
<update id="updateWxRequestAndResponse">
update tbl_kq_txn_dtl t
set t.wx_pay_request=#{payRequest},
t.wx_pay_response=#{payResponse},
t.prepay_id=#{prepayId}
where t.MER_ORDER_NUM=#{merOrderNum} and t.txn_flag='00'
</update>
<update id="updateOrderRefundAmt">
update tbl_kq_txn_dtl t set t.refund_amt=#{newRefundAmt} where t.mer_order_num=#{merOrderNum} and t.txn_flag='00' and t.order_status='11'
</update>
<update id="updateDeclareOrder">
update tbl_declare_record t set t.return_code=#{return_code}
<if test="result_code != null and result_code != ''">
,t.result_code=#{result_code}
</if>
<if test="err_code != null and err_code != ''">
,t.err_code=#{err_code}
</if>
<if test="err_code_des != null and err_code_des != ''">
,t.err_code_des=#{err_code_des}
</if>
<if test="modify_time != null and modify_time != ''">
,t.modify_time=#{modify_time}
</if>
<if test="cert_check_result != null and cert_check_result != ''">
,t.cert_check_result=#{cert_check_result}
</if>
<if test="verify_department != null and verify_department != ''">
,t.verify_department=#{verify_department}
</if>
<if test="verify_department_trade_id != null and verify_department_trade_id != ''">
,t.verify_department_trade_id=#{verify_department_trade_id}
</if>
<if test="state != null and state != ''">
,t.state=#{state}
</if>
where t.event_id=#{event_id}
</update>
<select id="queryPayOrderInfo" resultType="com.alibaba.fastjson.JSONObject">
select t.* from tbl_kq_txn_dtl t
where t.mer_order_num=#{merOrderNum}
and t.merch_id=#{merId}
and t.txn_flag='00'
<if test="orderStatus != null and orderStatus != ''">
and t.order_status=#{orderStatus}
</if>
</select>
<select id="queryDeclareOrder" resultType="com.alibaba.fastjson.JSONObject">
select t.* from tbl_declare_record t where t.out_trade_no=#{merOrderNo} and t.sub_order_no=#{sub_order_no} and t.state in ('SUCCESS','EXCEPT') and rownum =1
</select>
<select id="queryMerIdByOrderNo" resultType="com.alibaba.fastjson.JSONObject">
select t.mer_id from OMS_PAYMENT_INFO t where t.payid=#{outTradeNo} and t.payment_status='00' and rownum=1
</select>
</mapper>
2.4 报关代码
2.4.1 DeclareService
package com.etone.backend.service;
import com.alibaba.fastjson.JSONObject;
import com.etone.backend.vo.Result;
/**
* @description: 海关报关接口
*/
public interface DeclareService {
Result declareOrder(JSONObject req);
Result queryDeclareOrder(JSONObject req);
Result redeclareOrder(JSONObject req);
}
2.4.2 DeclareServiceImpl
package com.etone.backend.service.impl;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.etone.backend.mapper.WechatPayMapper;
import com.etone.backend.service.DeclareService;
import com.etone.backend.utils.wechat.ConstUtil;
import com.etone.backend.utils.wechat.WXPayConstants;
import com.etone.backend.utils.wechat.WXPayUtil;
import com.etone.backend.vo.Result;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.JdkSslContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.asynchttpclient.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.net.ssl.*;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static org.asynchttpclient.Dsl.asyncHttpClient;
/**
* @description: 实现微信支付海关报关接口
*/
@Service
@Slf4j
public class DeclareServiceImpl implements DeclareService {
@Resource
private WechatPayMapper wechatPayMapper;
@Value("${wechat.mchId}")
private String mchId;
@Value("${wechat.appKeyV2}")
private String appKeyV2;
@Value("${wechat.declare.pfxPwd}")
private String pfxPwd;
@Value("${wechat.declare.pfxPath}")
private String pfxPath;
@Override
public Result declareOrder(JSONObject req){
Map<String, String> paramMap = new HashMap<>();
//订单号
String merOrderNo = req.getString("out_trade_no");
//查询订单
JSONObject orderInfo=wechatPayMapper.queryPayOrderInfo(merOrderNo,mchId,"11");
if(orderInfo==null|| orderInfo.isEmpty()){
return Result.error("订单不存在");
}
String appId=orderInfo.getString("APP_ID");
//微信订单号
String transaction_id=orderInfo.getString("CHANNEL_ORDER_ID");
String sub_order_no=req.getString("sub_order_no");
//1、查询报关流水
JSONObject declareInfo=wechatPayMapper.queryDeclareOrder(merOrderNo,sub_order_no);
String event_id=UUID.randomUUID().toString().replace("-","");
if(declareInfo!=null){
log.info("订单已报关!订单编号:{},子订单号:{}",merOrderNo,sub_order_no);
return Result.error("订单已报关成功,请勿重复报关!");
}else {
//2、插入报关流水
req.put("event_id", event_id);
req.put("transaction_id", transaction_id);
if(StringUtils.isNotEmpty(req.getString("cert_id")))
{
//身份证参数
paramMap.put("cert_type", "IDCARD");
paramMap.put("cert_id", req.getString("cert_id"));
paramMap.put("name", req.getString("cert_name"));
}else {
//身份证参数
paramMap.put("cert_type", "");
paramMap.put("cert_id", "");
paramMap.put("cert_name", "");
}
req.put("merch_id",mchId);
req.put("req_date", DateUtil.format(new Date(),"yyyyMMdd"));
req.put("req_time", DateUtil.format(new Date(),"HHmmss"));
wechatPayMapper.saveDeclareOrder(req);
}
paramMap.put("out_trade_no", merOrderNo);
//拆单参数
paramMap.put("sub_order_no", sub_order_no);
paramMap.put("transaction_id", transaction_id);
//海关参数
paramMap.put("customs", req.getString("customs"));
paramMap.put("mch_customs_no", req.getString("mch_customs_no"));
paramMap.put("fee_type", "CNY");
paramMap.put("order_fee", req.getString("order_fee"));
paramMap.put("transport_fee", req.getString("transport_fee"));
paramMap.put("product_fee", req.getString("product_fee"));
this.generateSign(paramMap,appId);
Optional<JSONObject> optionalWechatBean = sendRequest(ConstUtil.DECLARE_URL, paramMap,appId);
if(optionalWechatBean.isPresent()){
JSONObject jsonObject = optionalWechatBean.get();
jsonObject.put("event_id", event_id);
//更新报关结果
wechatPayMapper.updateDeclareOrder(jsonObject);
String state=jsonObject.getString("state");
//认为成功的状态码:UNDECLARED--未申报 SUBMITTED--申报已提交 PROCESSING--申报中 SUCCESS--申报成功
//认定失败的状态码:FAIL-- 申报失败 EXCEPT --海关接口异常
if("UNDECLARED".equals(state)||"SUBMITTED ".equals(state)||"PROCESSING".equals(state)||"SUCCESS".equals(state)){
return Result.ok(jsonObject);
}else {
return Result.res("99","报关失败",jsonObject);
}
}
return Result.error("报关错误!");
}
@Override
public Result queryDeclareOrder(JSONObject req) {
Map<String, String> paramMap = new HashMap<>();
paramMap.put("out_trade_no", req.getString("out_trade_no"));
//海关参数
paramMap.put("customs", req.getString("customs"));
//拆单参数
if(StringUtils.isNotBlank(req.getString("sub_order_no"))){
paramMap.put("sub_order_no", req.getString("sub_order_no"));
}
//查询订单
JSONObject orderInfo=wechatPayMapper.queryPayOrderInfo(req.getString("out_trade_no"),mchId,"11");
if(orderInfo==null|| orderInfo.isEmpty()){
return Result.error("订单不存在");
}
String appId=orderInfo.getString("APP_ID");
this.generateSign(paramMap,appId);
Optional<JSONObject> optionalWechatBean = sendRequest(ConstUtil.QUERY_DECLARE_URL, paramMap,appId);
if(optionalWechatBean.isPresent()){
JSONObject result = optionalWechatBean.get();
return Result.ok(result);
}
return Result.error("未查询到数据!");
}
@Override
public Result redeclareOrder(JSONObject req) {
//订单号
String merOrderNo = req.getString("out_trade_no");
//1、查询订单
JSONObject orderInfo=wechatPayMapper.queryPayOrderInfo(merOrderNo,mchId,"11");
if(orderInfo==null|| orderInfo.isEmpty()){
return Result.error("订单不存在");
}
String appId=orderInfo.getString("APP_ID");
String sub_order_no=req.getString("sub_order_no");
//2、查询报关流水
JSONObject declareInfo=wechatPayMapper.queryDeclareOrder(merOrderNo,sub_order_no);
if(declareInfo==null||declareInfo.isEmpty()){
log.info("没有提交报关或报关状态错误!订单编号:{},子订单号:{}",merOrderNo,sub_order_no);
return Result.error("没有提交报关或报关状态错误!");
}
String event_id=declareInfo.getString("EVENT_ID");
Map<String, String> paramMap = new HashMap<>();
paramMap.put("out_trade_no", req.getString("out_trade_no"));
//海关参数
paramMap.put("customs", req.getString("customs"));
paramMap.put("mch_customs_no", req.getString("mch_customs_no"));
//拆单参数
if(StringUtils.isNotBlank(req.getString("sub_order_no"))){
paramMap.put("sub_order_no", req.getString("sub_order_no"));
}
this.generateSign(paramMap,appId);
Optional<JSONObject> optionalWechatBean = sendRequest(ConstUtil.REDECLARE_URL, paramMap,appId);
if(optionalWechatBean.isPresent()){
JSONObject jsonObject = optionalWechatBean.get();
jsonObject.put("event_id", event_id);
//更新报关结果
wechatPayMapper.updateDeclareOrder(jsonObject);
return Result.ok(jsonObject);
}
return Result.error("报关重推错误!");
}
private static String sign_type = "HMAC-SHA256";
private void generateSign(Map<String, String> map,String appId) {
/**
* 设置共用参数
*/
map.put("appid", appId);
map.put("mch_id", mchId);
//设置签名
String sign = null;
try {
sign = WXPayUtil.generateSignature(map, appKeyV2,
WXPayConstants.SignType.MD5);
} catch (Exception e) {
e.printStackTrace();
}
map.put("sign", sign);
}
private Optional<JSONObject> sendRequest(String url, Map<String, String> request,String appId) {
log.info(">>>微信报关通道,开始发送http 请求");
try {
generateSign(request,appId);
String requestXml = WXPayUtil.mapToXml(request);
log.info(">>>微信报关通道,请求数据 {}", requestXml);
InputStream keyStoreStream = Files.newInputStream(new File(pfxPath).toPath());
char[] keyStorePassword = pfxPwd.toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(keyStoreStream, keyStorePassword);
char[] certificatePassword = pfxPwd.toCharArray();
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, certificatePassword);
KeyManager[] keyManagers = kmf.getKeyManagers();
TrustManager[] trustManagers = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}};
SecureRandom secureRandom = new SecureRandom();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, secureRandom);
AsyncHttpClientConfig asyncHttpClientConfig = new DefaultAsyncHttpClientConfig.Builder()
.setKeepAlive(true)
.setSslContext(new JdkSslContext(sslContext, true, ClientAuth.REQUIRE))
.setMaxConnections(102400)
.setConnectTimeout(20 * 1000)
.setReadTimeout(20 * 1000)
.build();
AsyncHttpClient asyncHttpClient = asyncHttpClient(asyncHttpClientConfig);
ListenableFuture<Response> responseListenableFuture = asyncHttpClient.preparePost(url)
.setHeader("Content-Type", "application/xml;;charset=utf-8")
.setBody(requestXml)
.execute();
Response response = responseListenableFuture.get(30, TimeUnit.SECONDS);
String result = response.getResponseBody();
log.info(">>>微信报关返回数据xml: {}", result);
if (StringUtils.isNotEmpty(result)) {
//校验返回值
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JSONObject jsonObject = objectMapper.convertValue(resultMap, JSONObject.class);
log.info("微信报关返回数据Json:{}",jsonObject);
return Optional.of(jsonObject);
}
} catch (Exception e) {
e.printStackTrace();
}
return Optional.empty();
}
}
3、总结概述
第一次发博客练练手,代码中有参杂少许业务代码,如有问题可留言,问必答