|
@@ -0,0 +1,572 @@
|
|
|
+package com.kmall.admin.cuspay.biz.wx;
|
|
|
+
|
|
|
+import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
+import com.google.common.base.Strings;
|
|
|
+import com.kmall.admin.cuspay.biz.AbstractCusDeclareBiz;
|
|
|
+import com.kmall.admin.cuspay.biz.CuspayBiz;
|
|
|
+import com.kmall.admin.cuspay.common.contant.MerchNoticeDict;
|
|
|
+import com.kmall.admin.cuspay.common.contant.TablePrimaryKeyPrefix;
|
|
|
+import com.kmall.admin.cuspay.common.core.db.IdWorkerAide;
|
|
|
+import com.kmall.admin.cuspay.entity.merch.MerchNoti;
|
|
|
+import com.kmall.admin.cuspay.entity.merch.MerchPayCfg;
|
|
|
+import com.kmall.admin.cuspay.manager.snow.SnowflakeUtil;
|
|
|
+import com.kmall.admin.cuspay.service.MerchNotiService;
|
|
|
+import com.kmall.admin.cuspay.support.msg.resp.ResponseMessage;
|
|
|
+import com.kmall.admin.cuspay.support.msg.resp.ResponseStatus;
|
|
|
+import com.kmall.admin.cuspay.util.*;
|
|
|
+import com.kmall.admin.cuspay.ccnet2cuspay.common.MerchNotiBuilder;
|
|
|
+import com.kmall.admin.cuspay.ccnet2cuspay.common.wx.WxContants;
|
|
|
+import com.kmall.admin.cuspay.ccnet2cuspay.common.wx.WxDict;
|
|
|
+import com.kmall.admin.cuspay.ccnet2cuspay.dto.wx.WxQueryResponseMsg;
|
|
|
+import com.kmall.admin.cuspay.ccnet2cuspay.dto.wx.WxResponseMsg;
|
|
|
+import com.kmall.admin.cuspay.ccnet2cuspay.entity.wx.WxCbPayDoc;
|
|
|
+import com.kmall.admin.cuspay.ccnet2cuspay.entity.wx.WxPayError;
|
|
|
+import com.kmall.admin.cuspay.ccnet2cuspay.service.wx.WxCbPayDocService;
|
|
|
+import com.kmall.admin.cuspay.ccnet2cuspay.service.wx.WxPayErrorService;
|
|
|
+import com.google.common.collect.Lists;
|
|
|
+import com.google.common.collect.Maps;
|
|
|
+import com.kmall.admin.cuspay.util.jackson.JacksonUtil;
|
|
|
+import okhttp3.MediaType;
|
|
|
+import okhttp3.Request;
|
|
|
+import okhttp3.RequestBody;
|
|
|
+import org.apache.commons.codec.digest.DigestUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.context.annotation.PropertySource;
|
|
|
+import org.springframework.core.env.Environment;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.transaction.annotation.Propagation;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestMethod;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import javax.xml.bind.JAXB;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.StringReader;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 微信海关申报
|
|
|
+ *
|
|
|
+ * @author zx
|
|
|
+ * @version 1.0
|
|
|
+ * 2018-05-23 09:55
|
|
|
+ * @author zhuhh
|
|
|
+ * @version 1.1
|
|
|
+ * @date 2021年12月1日10:00:46
|
|
|
+ */
|
|
|
+@Component
|
|
|
+@PropertySource(value = {"classpath:conf/cuspay/cuspay-wx.properties"})
|
|
|
+public class WxCusDeclareBiz extends AbstractCusDeclareBiz implements CuspayBiz {
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(WxCusDeclareBiz.class);
|
|
|
+
|
|
|
+ private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private Environment environment;
|
|
|
+
|
|
|
+ private Integer limit;
|
|
|
+
|
|
|
+ private Integer count;
|
|
|
+
|
|
|
+ private String declareURL;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WxCbPayDocService wxCbPayDocService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WxPayErrorService wxPayErrorService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private MerchNotiService merchNotiService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 海关报关接口
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(propagation = Propagation.NOT_SUPPORTED)
|
|
|
+ public void biz(Map<String, Object> params) {
|
|
|
+ // 读取配置文件参数
|
|
|
+ limit = Integer.parseInt(environment.getProperty("db.wx.declare.limit"));
|
|
|
+ count = Integer.parseInt(environment.getProperty("db.wx.declare.count"));
|
|
|
+ declareURL = environment.getProperty("wx.payment.declare.url");
|
|
|
+
|
|
|
+ String appid = environment.getProperty("cus.play.wx.app-id");
|
|
|
+ String mchid = environment.getProperty("cus.play.wx.mch-id");
|
|
|
+ String customs = environment.getProperty("cus.play.wx.customs");
|
|
|
+ String platSn = environment.getProperty("merchant.plat-sn");
|
|
|
+ String platName = environment.getProperty("merchant.plat-name");
|
|
|
+ String customsCode = environment.getProperty("merchant.customs-code");
|
|
|
+
|
|
|
+ // 记录异常数据
|
|
|
+ List<WxPayError> errors = Lists.newArrayList();
|
|
|
+ // 待通知的用户
|
|
|
+ List<MerchNoti> merchNotis = Lists.newArrayList();
|
|
|
+ // 微信跨境支付单证
|
|
|
+ WxCbPayDoc wxCbPayDoc = new WxCbPayDoc();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // *********设置支付单报关参数*********
|
|
|
+ // appid
|
|
|
+ wxCbPayDoc.setAppid(appid);
|
|
|
+ // 微信支付商户号
|
|
|
+ wxCbPayDoc.setMchId(mchid);
|
|
|
+ // 商户订单号,商户系统内部订单号
|
|
|
+ wxCbPayDoc.setOutTradeNo(params.get("outTradeNo").toString());
|
|
|
+ // 微信支付订单号
|
|
|
+ wxCbPayDoc.setTransactionId(params.get("transactionId").toString());
|
|
|
+ // 报送海关
|
|
|
+ wxCbPayDoc.setCustoms(customs);
|
|
|
+ // 商户海关备案号
|
|
|
+ wxCbPayDoc.setMchCustomsNo(customsCode);
|
|
|
+ // 如有拆单必传
|
|
|
+ // 商户子订单号
|
|
|
+ if (!StringUtils.isEmpty(params.get("subOrderNo"))) {
|
|
|
+ wxCbPayDoc.setSubOrderNo(params.get("subOrderNo").toString());
|
|
|
+ }
|
|
|
+ // 币种,暂时只支持CNY
|
|
|
+ if (!StringUtils.isEmpty(params.get("feeType"))) {
|
|
|
+ wxCbPayDoc.setFeeType(params.get("feeType").toString());
|
|
|
+ }
|
|
|
+ // 子订单金额,以分为单位
|
|
|
+ if (!StringUtils.isEmpty(params.get("orderFee"))) {
|
|
|
+ wxCbPayDoc.setOrderFee(Integer.parseInt(params.get("orderFee").toString()));
|
|
|
+ }
|
|
|
+ // 物流费用,以分为单位
|
|
|
+ if (!StringUtils.isEmpty(params.get("transportFee"))) {
|
|
|
+ wxCbPayDoc.setTransportFee(Integer.parseInt(params.get("transportFee").toString()));
|
|
|
+ }
|
|
|
+ // 商品费用,以分为单位
|
|
|
+ if (!StringUtils.isEmpty(params.get("productFee"))) {
|
|
|
+ wxCbPayDoc.setProductFee(Integer.parseInt(params.get("productFee").toString()));
|
|
|
+ }
|
|
|
+ // *********设置支付单报关参数*********
|
|
|
+
|
|
|
+ // *********设置商户通知表参数*********
|
|
|
+ // 接入平台内部编号
|
|
|
+ wxCbPayDoc.setMerchErpOrderSn(SysContants.wx_pay_doc + SnowflakeUtil.getSnowNextId());
|
|
|
+ // 商户编号
|
|
|
+ wxCbPayDoc.setMerchSn(params.get("merchSn").toString());
|
|
|
+ // 商户名称
|
|
|
+ wxCbPayDoc.setMerchName(params.get("merchName").toString());
|
|
|
+ // 平台编号
|
|
|
+ wxCbPayDoc.setPlatSn(platSn);
|
|
|
+ // 平台中文名
|
|
|
+ wxCbPayDoc.setPlatName(platName);
|
|
|
+ // 第三方商户代码
|
|
|
+ wxCbPayDoc.setThirdPartyMerchCode(params.get("thirdPartyMerchCode").toString());
|
|
|
+ // 第三方商户名称
|
|
|
+ wxCbPayDoc.setThirdPartyMerchName(params.get("thirdPartyMerchName").toString());
|
|
|
+ // 微信子订单号
|
|
|
+ if (!StringUtils.isEmpty(params.get("subOrderId"))) {
|
|
|
+ wxCbPayDoc.setSubOrderId(params.get("subOrderId").toString());
|
|
|
+ }
|
|
|
+ // *********设置商户通知表参数*********
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("微信支付单设置参数失败:" + e.getMessage());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先将支付单信息写入数据库,后面直接更新状态即可
|
|
|
+ logger.info("微信支付单信息写入数据库开始");
|
|
|
+ try {
|
|
|
+ addWxCbPayDoc(wxCbPayDoc);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("微信支付单信息写入数据库失败:" + e.getMessage());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ logger.info("微信支付单信息写入数据库结束");
|
|
|
+
|
|
|
+ // merch_pay_cfg 表由手动配置数据
|
|
|
+ //获取商户信息,先从缓存中获取,缓存不存在再从数据库重取
|
|
|
+ MerchPayCfg merchPayCfg = getMerchPayCfgCache(wxCbPayDoc.getAppid());
|
|
|
+
|
|
|
+ MerchNotiBuilder builder = new MerchNotiBuilder();
|
|
|
+
|
|
|
+ // 设置支付信息
|
|
|
+ builder.wxCbPay(wxCbPayDoc);
|
|
|
+ // 设置通知次数
|
|
|
+ builder.notiCount(count);
|
|
|
+
|
|
|
+ if (merchPayCfg == null) {
|
|
|
+ logger.error("appid为【" + wxCbPayDoc.getAppid() + "】的商户支付配置信息不存在");
|
|
|
+ wxCbPayDoc.setReturnCode("FAIL");
|
|
|
+ wxCbPayDoc.setReturnMsg("appid为【" + wxCbPayDoc.getAppid() + "】的商户支付配置信息不存在");
|
|
|
+ wxCbPayDocService.updateWxCbPay(wxCbPayDoc);
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (merchPayCfg.getMerchWxApiKey() == null) {
|
|
|
+ logger.error("appid为【" + wxCbPayDoc.getAppid() + "】的商户支付信息api密钥为空");
|
|
|
+ wxCbPayDoc.setReturnCode(WxContants.ERROR_NO_INFOMATION);
|
|
|
+ wxCbPayDoc.setReturnMsg("appid为【" + wxCbPayDoc.getAppid() + "】的商户支付信息api密钥为空");
|
|
|
+ wxCbPayDocService.updateWxCbPay(wxCbPayDoc);
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //设置商户配置信息
|
|
|
+ builder.merchPay(merchPayCfg);
|
|
|
+
|
|
|
+ //组装xml 格式数据
|
|
|
+ String xml = XmlUtils.map2Xml(assemblyDeclareXml(wxCbPayDoc, merchPayCfg.getMerchWxApiKey()));
|
|
|
+ logger.info("微信支付报关请求数xml据为:" + xml);
|
|
|
+
|
|
|
+ //通过http post请求 发送xml数据
|
|
|
+ Request request = OkHttpUtils.buildRequest(declareURL,
|
|
|
+ RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml));
|
|
|
+
|
|
|
+ WxResponseMsg wxResponseMsgDto = null;
|
|
|
+ try {
|
|
|
+ // todo, 请求海关接口
|
|
|
+ // String result = OkHttpUtils.post(request);
|
|
|
+ String result = "";
|
|
|
+ StringReader reader = new StringReader(result);
|
|
|
+ wxResponseMsgDto = JAXB.unmarshal(reader, WxResponseMsg.class);
|
|
|
+ logger.info("result:" + result + "wx response message pojo:" + wxResponseMsgDto);
|
|
|
+ if (wxResponseMsgDto == null) {
|
|
|
+ throw new IOException("http请求[" + declareURL + "]响应结果为空");
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.error("数据请求异常,支付单请求数据为xml{}", xml, e);
|
|
|
+ WxPayError wxPayError = createWxPayError(wxCbPayDoc);
|
|
|
+ errors.add(wxPayError);
|
|
|
+ }
|
|
|
+
|
|
|
+ MerchNoti merchNoti = new MerchNoti();
|
|
|
+ try {
|
|
|
+ wxCbPayDoc.setResultCode(wxResponseMsgDto.getResultCode());
|
|
|
+ wxCbPayDoc.setReturnCode(wxResponseMsgDto.getReturnCode());
|
|
|
+ wxCbPayDoc.setReturnMsg(wxResponseMsgDto.getReturnMsg());
|
|
|
+ //返回这状态码为成功 业务结果成功
|
|
|
+ if (WxContants.SUCCESS.equals(wxResponseMsgDto.getReturnCode())) {
|
|
|
+ if (WxContants.SUCCESS.equals(wxResponseMsgDto.getResultCode())) {
|
|
|
+ wxCbPayDoc.setSubOrderNo(wxResponseMsgDto.getSubOrderNo());
|
|
|
+ wxCbPayDoc.setSubOrderId(wxResponseMsgDto.getSubOrderId());
|
|
|
+
|
|
|
+ //订购人与支付人校验结果
|
|
|
+ if (!StringUtils.isEmpty(wxResponseMsgDto.getCertCheckResult())) {
|
|
|
+ if ("UNCHECKED".equals(wxResponseMsgDto.getCertCheckResult())) { //未知
|
|
|
+ builder.certCheckResult(MerchNoticeDict.BuyerPayerCheckStatus.i_0.getItem());
|
|
|
+ wxCbPayDoc.setBuyerPayerCheck(MerchNoticeDict.BuyerPayerCheckStatus.i_0.getItem());
|
|
|
+ }
|
|
|
+ if ("SAME".equals(wxResponseMsgDto.getCertCheckResult())) {//一致
|
|
|
+ builder.certCheckResult(MerchNoticeDict.BuyerPayerCheckStatus.i_1.getItem());
|
|
|
+ wxCbPayDoc.setBuyerPayerCheck(MerchNoticeDict.BuyerPayerCheckStatus.i_1.getItem());
|
|
|
+ }
|
|
|
+ if ("DIFFERENT".equals(wxResponseMsgDto.getCertCheckResult())) {//不一致
|
|
|
+ builder.certCheckResult(MerchNoticeDict.BuyerPayerCheckStatus.i_2.getItem());
|
|
|
+ wxCbPayDoc.setBuyerPayerCheck(MerchNoticeDict.BuyerPayerCheckStatus.i_2.getItem());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //返回状态未申报
|
|
|
+ if (WxDict.ResponseMsgState.UNDECLARED.getItem().equals(wxResponseMsgDto.getState())) {
|
|
|
+ wxCbPayDoc.setDocStatus(WxDict.PaymentDocStatus.i_10.getItem());
|
|
|
+ merchNoti = builder.code(wxResponseMsgDto.getErrCode())
|
|
|
+ .msg(WxDict.ResponseMsgState.UNDECLARED.getItemName())
|
|
|
+ .cusDeclStatus(WxDict.MerchNoticeStatus.i_10.getItem())
|
|
|
+ .build();
|
|
|
+ } else if (WxDict.ResponseMsgState.PROCESSING.getItem().equals(wxResponseMsgDto.getState())) {//申报中
|
|
|
+ wxCbPayDoc.setDocStatus(WxDict.PaymentDocStatus.i_11.getItem());
|
|
|
+ merchNoti = builder.code(wxResponseMsgDto.getErrCode())
|
|
|
+ .msg(WxDict.ResponseMsgState.PROCESSING.getItemName())
|
|
|
+ .cusDeclStatus(WxDict.MerchNoticeStatus.i_11.getItem())
|
|
|
+ .build();
|
|
|
+ } else if (WxDict.ResponseMsgState.SUBMITTED.getItem().equals(wxResponseMsgDto.getState())) {//申报已提交
|
|
|
+ wxCbPayDoc.setDocStatus(WxDict.PaymentDocStatus.i_10.getItem());
|
|
|
+ merchNoti = builder.code(wxResponseMsgDto.getErrCode())
|
|
|
+ .msg(WxDict.ResponseMsgState.SUBMITTED.getItemName())
|
|
|
+ .cusDeclStatus(WxDict.MerchNoticeStatus.i_11.getItem())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ } else if (WxDict.ResponseMsgState.SUCCESS.getItem().equals(wxResponseMsgDto.getState())) {//申报成功
|
|
|
+ wxCbPayDoc.setDocStatus(WxDict.PaymentDocStatus.i_12.getItem());
|
|
|
+ merchNoti = builder.code(wxResponseMsgDto.getErrCode())
|
|
|
+ .msg(wxResponseMsgDto.getErrCodeDes())
|
|
|
+ .cusDeclStatus(WxDict.MerchNoticeStatus.i_12.getItem())
|
|
|
+ .build();
|
|
|
+ } else if (WxDict.ResponseMsgState.FAIL.getItem().equals(wxResponseMsgDto.getState())) {//申报失败
|
|
|
+ wxCbPayDoc.setDocStatus(WxDict.PaymentDocStatus.i_03.getItem());
|
|
|
+ merchNoti = builder.code(wxResponseMsgDto.getErrCode())
|
|
|
+ .msg(WxDict.ResponseMsgState.FAIL.getItemName())
|
|
|
+ .cusDeclStatus(WxDict.MerchNoticeStatus.i_13.getItem())
|
|
|
+ .build();
|
|
|
+ } else if (WxDict.ResponseMsgState.EXCEPT.getItem().equals(wxResponseMsgDto.getState())) {//海关接口异常
|
|
|
+ wxCbPayDoc.setDocStatus(WxDict.PaymentDocStatus.i_03.getItem());
|
|
|
+ merchNoti = builder.code(wxResponseMsgDto.getErrCode())
|
|
|
+ .msg(WxDict.ResponseMsgState.EXCEPT.getItemName())
|
|
|
+ .cusDeclStatus(WxDict.MerchNoticeStatus.i_13.getItem())
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ //返回状态码为成功 业务结果失败
|
|
|
+ wxCbPayDoc.setErrCode(wxResponseMsgDto.getErrCode());
|
|
|
+ wxCbPayDoc.setErrCodeDes(wxResponseMsgDto.getErrCodeDes());
|
|
|
+ wxCbPayDoc.setDocStatus(WxDict.PaymentDocStatus.i_13.getItem());
|
|
|
+ wxCbPayDoc.setBuyerPayerCheck(MerchNoticeDict.BuyerPayerCheckStatus.i_3.getItem());
|
|
|
+ //[35324042]您的证件号码校验异常请联系财付通客服处理的情况,修改商户通知数据为“身份校验不一致”,“申报成功”
|
|
|
+ if ("35324042".equalsIgnoreCase(wxResponseMsgDto.getErrCode())) {
|
|
|
+ builder.certCheckResult(MerchNoticeDict.BuyerPayerCheckStatus.i_2.getItem());
|
|
|
+ merchNoti = builder.code(wxResponseMsgDto.getErrCode())
|
|
|
+ .msg(wxResponseMsgDto.getErrCodeDes())
|
|
|
+ .cusDeclStatus(WxDict.MerchNoticeStatus.i_12.getItem())
|
|
|
+ .build();
|
|
|
+ } else {
|
|
|
+ builder.certCheckResult(MerchNoticeDict.BuyerPayerCheckStatus.i_3.getItem());
|
|
|
+ merchNoti = builder.code(wxResponseMsgDto.getErrCode())
|
|
|
+ .msg(wxResponseMsgDto.getErrCodeDes())
|
|
|
+ .cusDeclStatus(WxDict.MerchNoticeStatus.i_13.getItem())
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ///返回状态码为失败
|
|
|
+ if (WxContants.RETURN_CODE_FAIL.equals(wxResponseMsgDto.getReturnCode()) ||
|
|
|
+ WxContants.FAIL.equals(wxResponseMsgDto.getReturnCode())) {
|
|
|
+ wxCbPayDoc.setDocStatus(WxDict.PaymentDocStatus.i_03.getItem());
|
|
|
+
|
|
|
+ //通知商户
|
|
|
+ merchNoti = builder.code(WxContants.ERROR_FAIL)
|
|
|
+ .msg(wxResponseMsgDto.getReturnMsg())
|
|
|
+ .cusDeclStatus(WxDict.MerchNoticeStatus.i_13.getItem())
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+ wxCbPayDocService.updateWxCbPay(wxCbPayDoc);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("更新支付单证写入数据库信息状态异常", e);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ merchNotis.add(merchNoti);
|
|
|
+
|
|
|
+
|
|
|
+ //持久化商户通知数据
|
|
|
+ try {
|
|
|
+ int result = merchNotiService.insertBatch(merchNotis);
|
|
|
+ if (result == 0) {
|
|
|
+ logger.error("持久化" + result + "条商户通知数据失败data:" + merchNotis);
|
|
|
+ throw new Exception("持久化" + result + "条商户通知数据失败data:" + merchNotis);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("持久化商户通知数据异常", e);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //存储异常记录
|
|
|
+ try {
|
|
|
+ if (errors != null && !errors.isEmpty()) {
|
|
|
+ int errResult = wxPayErrorService.insertWxPayErrorBatch(errors);
|
|
|
+ if (errResult == 0) {
|
|
|
+ logger.error("持久化支付申报异常信息数据失败data:" + errors);
|
|
|
+ throw new Exception("持久化支付申报异常信息数据失败data:" + errors);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("持久化支付申报异常信息数据失败data:" + errors, e);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录异常的支付单证信息
|
|
|
+ *
|
|
|
+ * @param wxCbPayDoc
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private WxPayError createWxPayError(WxCbPayDoc wxCbPayDoc) {
|
|
|
+ WxPayError wxPayError = new WxPayError();
|
|
|
+ wxPayError.setErrorSn(TablePrimaryKeyPrefix.wx_pay_error_type + IdWorkerAide.nextId());
|
|
|
+ wxPayError.setWxPaySn(wxCbPayDoc.getTransactionId());
|
|
|
+ wxPayError.setMerchSn(wxCbPayDoc.getMerchSn());
|
|
|
+ wxPayError.setMerchName(wxCbPayDoc.getMerchName());
|
|
|
+ wxPayError.setAppid(wxCbPayDoc.getAppid());
|
|
|
+ wxPayError.setMchId(wxCbPayDoc.getMchId());
|
|
|
+ wxPayError.setOutTradeNo(wxCbPayDoc.getOutTradeNo());
|
|
|
+ wxPayError.setTransactionId(wxCbPayDoc.getTransactionId());
|
|
|
+ wxPayError.setSubOrderNo(wxCbPayDoc.getSubOrderNo());
|
|
|
+ wxPayError.setCustoms(wxCbPayDoc.getCustoms());
|
|
|
+ wxPayError.setErrCode(wxCbPayDoc.getErrCode());
|
|
|
+ wxPayError.setErrMsg(wxCbPayDoc.getReturnMsg());
|
|
|
+ wxPayError.getCreateTime(sdf.format(new Date()));
|
|
|
+ return wxPayError;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 组装报关所需要的报关数据
|
|
|
+ *
|
|
|
+ * @param wxCbPayDoc 微信支付数据
|
|
|
+ * @param key 微信Api密钥
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ protected Map<String, String> assemblyDeclareXml(WxCbPayDoc wxCbPayDoc, String key) {
|
|
|
+ // 报关请求参数文档:https://pay.weixin.qq.com/wiki/doc/api/external/declarecustom.php?chapter=18_1
|
|
|
+ //1.选择报关数据,排序
|
|
|
+ SortedMap<String, String> sorted = Maps.newTreeMap();
|
|
|
+ sorted.put("appid", wxCbPayDoc.getAppid());
|
|
|
+ sorted.put("mch_id", wxCbPayDoc.getMchId());
|
|
|
+ sorted.put("out_trade_no", wxCbPayDoc.getOutTradeNo());
|
|
|
+ sorted.put("transaction_id", wxCbPayDoc.getTransactionId());
|
|
|
+ sorted.put("customs", wxCbPayDoc.getCustoms());
|
|
|
+ sorted.put("mch_customs_no", wxCbPayDoc.getMchCustomsNo());
|
|
|
+ // 关税,以分为单位,非必填项,不会提交给海关。少数海关特殊要求上传该字段时需要
|
|
|
+ // sorted.put("duty", String.valueOf(wxCbPayDoc.getDuty()));
|
|
|
+ // 报关信息,不传,默认是新增。ADD 新增报关申请;MODIFY 修改
|
|
|
+ // sorted.put("action_type", "ADD");
|
|
|
+
|
|
|
+ // 以下为拆单必传参数
|
|
|
+ if (wxCbPayDoc.getSubOrderNo() != null && !wxCbPayDoc.getSubOrderNo().isEmpty()) {
|
|
|
+ sorted.put("sub_order_no", wxCbPayDoc.getSubOrderNo());
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getFeeType() != null && !wxCbPayDoc.getFeeType().isEmpty()) {
|
|
|
+ sorted.put("fee_type", wxCbPayDoc.getFeeType());
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getOrderFee() != null) {
|
|
|
+ sorted.put("order_fee", String.valueOf(wxCbPayDoc.getOrderFee()));
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getTransportFee() != null) {
|
|
|
+ sorted.put("transport_fee", String.valueOf(wxCbPayDoc.getTransportFee()));
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getProductFee() != null) {
|
|
|
+ sorted.put("product_fee", String.valueOf(wxCbPayDoc.getProductFee()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 用户实名信息将以微信侧的为准,推送给海关。以下字段上传后,如与微信侧的信息不一致,会反馈给商户,便于商户收集正确的信息用于订单推送,不影响报关结果。
|
|
|
+ if (wxCbPayDoc.getCertType() != null && !wxCbPayDoc.getCertType().isEmpty()) {
|
|
|
+ sorted.put("cert_type", wxCbPayDoc.getCertType());
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getCertId() != null && !wxCbPayDoc.getCertId().isEmpty()) {
|
|
|
+ sorted.put("cert_id", wxCbPayDoc.getCertId());
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getName() != null && !wxCbPayDoc.getName().isEmpty()) {
|
|
|
+ sorted.put("name", wxCbPayDoc.getName());
|
|
|
+ }
|
|
|
+
|
|
|
+ //2.生成签名
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (Map.Entry<String, String> entry : sorted.entrySet()) {
|
|
|
+ sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
|
|
|
+ }
|
|
|
+ sb.append("key").append("=").append(key);
|
|
|
+ //签名MD5 加密
|
|
|
+ String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
|
|
|
+
|
|
|
+ sorted.put("sign", sign);
|
|
|
+ return sorted;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 支付单信息写入数据库
|
|
|
+ *
|
|
|
+ * @author zhuhh
|
|
|
+ * @param wxCbPayDoc
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ protected void addWxCbPayDoc(WxCbPayDoc wxCbPayDoc) {
|
|
|
+ logger.info("微信支付单信息写入数据库接口请求开始");
|
|
|
+ if (wxCbPayDoc == null) {
|
|
|
+ throw new RuntimeException("微信支付单信息写入数据库失败:微信支付单业务数据为空!");
|
|
|
+ }
|
|
|
+ logger.info("微信支付单信息写入数据库接口请求数据:【{}】", JacksonUtil.toJson(wxCbPayDoc));
|
|
|
+
|
|
|
+ //数据校验
|
|
|
+ Map<String, Object> validate = MapBeanUtils.fromObject(wxCbPayDoc);
|
|
|
+ Map<String, Object> beVerified = Maps.newHashMap();
|
|
|
+
|
|
|
+ beVerified.put("merchSn", "商户编号");
|
|
|
+ beVerified.put("merchName", "商户名称");
|
|
|
+ beVerified.put("appid", "微信公众号ID");
|
|
|
+ beVerified.put("mchId","微信支付商户号");
|
|
|
+ beVerified.put("outTradeNo", "商户订单号");
|
|
|
+ beVerified.put("transactionId","微信支付订单号");
|
|
|
+ beVerified.put("customs","报送海关");
|
|
|
+ beVerified.put("mchCustomsNo","商户海关备案号");
|
|
|
+ beVerified.put("merchErpOrderSn","商户erp订单编号");
|
|
|
+
|
|
|
+ ResponseMessage rst = Validator.isEmpty(beVerified, validate);
|
|
|
+ if (ResponseStatus.ERROR.getItem().equals(rst.getCode())) {
|
|
|
+ throw new RuntimeException("微信支付单信息写入数据库失败:微信支付单业务数据校验失败!");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 身份证如不为空则校验身份证格式是否正确
|
|
|
+ if(StringUtils.hasText(wxCbPayDoc.getCertId()) && !Validator.isCardNo(wxCbPayDoc.getCertId())){
|
|
|
+ throw new RuntimeException("身份证格式不正确,请填写正确的号码!");
|
|
|
+ }
|
|
|
+
|
|
|
+ ResponseMessage responseMessage = wxCbPayDocService.addWxCbPayDoc(wxCbPayDoc);
|
|
|
+
|
|
|
+ // 判断返回结果
|
|
|
+ if (ResponseStatus.SUCCESS.getItem().equals(responseMessage.getCode())) {
|
|
|
+ logger.info(responseMessage.getMsg());
|
|
|
+ } else if (ResponseStatus.ERROR.getItem().equals(responseMessage.getCode())) {
|
|
|
+ logger.error(responseMessage.getMsg());
|
|
|
+ throw new RuntimeException(responseMessage.getMsg());
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+
|
|
|
+ String queryURL = "https://api.mch.weixin.qq.com/cgi-bin/mch/customs/customdeclarequery";
|
|
|
+ WxCbPayDoc wxCbPayDoc = new WxCbPayDoc();
|
|
|
+ wxCbPayDoc.setAppid("125453");
|
|
|
+ wxCbPayDoc.setMchId("1501125641");
|
|
|
+ wxCbPayDoc.setOutTradeNo("701415011050");
|
|
|
+ wxCbPayDoc.setTransactionId("4200000114201805234418784392");
|
|
|
+ wxCbPayDoc.setCustoms("SHENZHEN");
|
|
|
+ wxCbPayDoc.setMchCustomsNo("4403160Z3Y");//中网科技(深圳)有限公司
|
|
|
+
|
|
|
+
|
|
|
+ SortedMap<String, String> sorted = Maps.newTreeMap();
|
|
|
+ sorted.put("appid", wxCbPayDoc.getAppid());
|
|
|
+ sorted.put("mch_id", wxCbPayDoc.getMchId());
|
|
|
+ sorted.put("out_trade_no", wxCbPayDoc.getOutTradeNo());
|
|
|
+ sorted.put("transaction_id", wxCbPayDoc.getTransactionId());
|
|
|
+ sorted.put("customs", wxCbPayDoc.getCustoms());
|
|
|
+// sorted.put("mch_customs_no", wxCbPayDoc.getMchCustomsNo());
|
|
|
+ //sorted.put("action_type", "MODIFY ");
|
|
|
+// sorted.put("cert_type", "IDCARD");
|
|
|
+// sorted.put("cert_id", "440301198012261939");
|
|
|
+// sorted.put("name", "司晓峰");
|
|
|
+
|
|
|
+
|
|
|
+ //2.生成签名
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (Map.Entry<String, String> entry : sorted.entrySet()) {
|
|
|
+ sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
|
|
|
+ }
|
|
|
+ sb.append("key").append("=").append("Zx1245uytDLliom12345622222222222");
|
|
|
+ //签名MD5 加密
|
|
|
+ String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
|
|
|
+
|
|
|
+ sorted.put("sign", sign);
|
|
|
+
|
|
|
+ String xml = XmlUtils.map2Xml(sorted);
|
|
|
+
|
|
|
+ System.out.println("xml:" + xml);
|
|
|
+
|
|
|
+ //通过http post请求 发送xml数据
|
|
|
+ Request request = OkHttpUtils.buildRequest(queryURL,
|
|
|
+ RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml));
|
|
|
+ String result = null;
|
|
|
+ try {
|
|
|
+ result = OkHttpUtils.post(request);
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ System.out.println(result);
|
|
|
+ System.out.println(ReaderXmlForDOM4J.parse(result, 1));
|
|
|
+
|
|
|
+ StringReader reader = new StringReader(result);
|
|
|
+ WxQueryResponseMsg wxResponseMsg = JAXB.unmarshal(reader, WxQueryResponseMsg.class);
|
|
|
+
|
|
|
+ System.out.println(wxResponseMsg);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|