|
@@ -0,0 +1,217 @@
|
|
|
+package com.emato.cuspay.declare;
|
|
|
+
|
|
|
+import com.emato.cuspay.dao.data.MerchCusCfg;
|
|
|
+import com.emato.cuspay.dao.data.MerchPayCfg;
|
|
|
+import com.emato.cuspay.dao.data.wx.WxCbPayDoc;
|
|
|
+import com.emato.cuspay.dao.data.wx.WxPayError;
|
|
|
+import com.emato.cuspay.dao.mapper.wx.WxCbPayDocMapper;
|
|
|
+import com.emato.cuspay.dto.WxResponseMsg;
|
|
|
+import com.emato.cuspay.notify.WxMerchantNotice;
|
|
|
+import com.emato.cuspay.util.OkHttpUtils;
|
|
|
+import com.emato.cuspay.util.XmlUtils;
|
|
|
+import com.google.common.collect.Lists;
|
|
|
+import com.google.common.collect.Maps;
|
|
|
+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.beans.factory.annotation.Value;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import javax.xml.bind.JAXB;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.StringReader;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.SortedMap;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 微信海关申报
|
|
|
+ * @author zx
|
|
|
+ * @version 1.0
|
|
|
+ * 2018-05-18 11:04
|
|
|
+ */
|
|
|
+
|
|
|
+@Component
|
|
|
+public class WxCusDeclare implements CusDeclare{
|
|
|
+
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(WxCusDeclare.class);
|
|
|
+
|
|
|
+
|
|
|
+ @Value("${db.wx.notify.limit}")
|
|
|
+ private Integer limit;
|
|
|
+
|
|
|
+ @Value("${wx.payment.query.url}")
|
|
|
+ private String declareURL;
|
|
|
+
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ WxCbPayDocMapper wxCbPayDocMapper;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 海关报关接口
|
|
|
+ */
|
|
|
+ public void declare() {
|
|
|
+ limit = (limit != null && limit > 0) ? limit : 20;
|
|
|
+ List<WxCbPayDoc> wxCbPayDocs = wxCbPayDocMapper.selectBeDeclaredData(limit);
|
|
|
+
|
|
|
+ if (wxCbPayDocs == null || wxCbPayDocs.isEmpty()) {
|
|
|
+ logger.info("没有待申报微信推海关的支付数据");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //记录异常数据
|
|
|
+ List<WxCbPayDoc> docs = Lists.newArrayList();
|
|
|
+ List<WxPayError> errors = Lists.newArrayList();
|
|
|
+
|
|
|
+ wxCbPayDocs.forEach(wxCbPayDoc -> {
|
|
|
+ //获取商户信息 从缓存中去 缓存不存在 从数据库重取
|
|
|
+ MerchPayCfg merchPayCfg = null;
|
|
|
+
|
|
|
+ if (merchPayCfg == null) {
|
|
|
+ logger.error("商户编号为【"+wxCbPayDoc.getMerchSn()+"】的商户支付配置信息不存在");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (merchPayCfg.getMerchWxApiKey() == null) {
|
|
|
+ logger.error("商户编号为【"+wxCbPayDoc.getMerchSn()+"】的商户支付信息api密钥为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //组装xml 格式数据
|
|
|
+ String xml = XmlUtils.map2Xml(assemblyDeclareXml(wxCbPayDoc, merchPayCfg.getMerchWxApiKey()));
|
|
|
+
|
|
|
+ //通过http post请求 发送xml数据
|
|
|
+ Request request = OkHttpUtils.buildRequest(declareURL,
|
|
|
+ RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml));
|
|
|
+ String result = null;
|
|
|
+ try {
|
|
|
+ result = OkHttpUtils.post(request);
|
|
|
+ StringReader reader = new StringReader(result);
|
|
|
+ WxResponseMsg wxResponseMsgDto = JAXB.unmarshal(reader, WxResponseMsg.class);
|
|
|
+ logger.info("result:"+result + "wx response message pojo:"+wxResponseMsgDto);
|
|
|
+ } catch (IOException e) {
|
|
|
+ docs.add(wxCbPayDoc);
|
|
|
+ WxPayError wxPayError = new WxPayError();
|
|
|
+ errors.add(wxPayError);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ //存储异常记录
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 组装报关所需要的报关数据
|
|
|
+ * @param wxCbPayDoc 微信支付数据
|
|
|
+ * @param key 微信Api密钥
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ protected Map<String, String> assemblyDeclareXml(WxCbPayDoc wxCbPayDoc, String key) {
|
|
|
+
|
|
|
+
|
|
|
+ //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());
|
|
|
+ if (wxCbPayDoc.getDuty() != null) {
|
|
|
+ sorted.put("duty", String.valueOf(wxCbPayDoc.getDuty()));
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getActionType() != null) {
|
|
|
+ sorted.put("action_type", wxCbPayDoc.getActionType());
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getSubOrderNo() != null) {
|
|
|
+ sorted.put("sub_order_no", wxCbPayDoc.getActionType());
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getFeeType() != null) {
|
|
|
+ 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 ) {
|
|
|
+ sorted.put("cert_type", wxCbPayDoc.getCertType());
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getCertId() != null) {
|
|
|
+ sorted.put("cert_id", wxCbPayDoc.getCertId());
|
|
|
+ }
|
|
|
+ if (wxCbPayDoc.getName() != null) {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+ WxCbPayDoc wxCbPayDoc = new WxCbPayDoc();
|
|
|
+ wxCbPayDoc.setAppid("wxd678efh567hg6787");
|
|
|
+ wxCbPayDoc.setMchId("1501125641");
|
|
|
+ wxCbPayDoc.setOutTradeNo("20150806125346");
|
|
|
+ wxCbPayDoc.setTransactionId("1000320306201511078440737890");
|
|
|
+ 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());
|
|
|
+
|
|
|
+ //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("ZC4g5ryxTQelj3x556HwljSpSX4Enzsy");
|
|
|
+ //签名MD5 加密
|
|
|
+ String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
|
|
|
+
|
|
|
+ sorted.put("sign", sign);
|
|
|
+
|
|
|
+ String xml = XmlUtils.map2Xml(sorted);
|
|
|
+
|
|
|
+ //通过http post请求 发送xml数据
|
|
|
+ Request request = OkHttpUtils.buildRequest("https://api.mch.weixin.qq.com/cgi-bin/mch/customs/customdeclareorder",
|
|
|
+ RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml));
|
|
|
+ String result = null;
|
|
|
+ try {
|
|
|
+ result = OkHttpUtils.post(request);
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ StringReader reader = new StringReader(result);
|
|
|
+
|
|
|
+ WxResponseMsg wxResponseMsg = JAXB.unmarshal(reader, WxResponseMsg.class);
|
|
|
+
|
|
|
+ System.out.println(wxResponseMsg);
|
|
|
+ }
|
|
|
+}
|