From 9e2e1fe12ea3cb4e425f808bf83f2005947197bc Mon Sep 17 00:00:00 2001 From: shenzhuan Date: Sun, 24 Feb 2019 17:36:19 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=94=AF=E4=BB=98=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../macro/mall/model/OmsOrderReturnApply.java | 5 + mall-portal/pom.xml | 32 +- .../mall/portal/common/CommonConstant.java | 1 + .../mall/portal/pay/AbstractPayPlatform.java | 28 ++ .../portal/pay/BypassForFreePlatform.java | 93 ++++ .../com/macro/mall/portal/pay/PayMethod.java | 27 ++ .../com/macro/mall/portal/pay/PayMethods.java | 113 +++++ .../macro/mall/portal/pay/PayPlatform.java | 61 +++ .../pay/alipay/AbstraceAlipayPlatform.java | 202 ++++++++ .../portal/pay/alipay/AlipayAppPlatform.java | 64 +++ .../pay/alipay/AlipayDirectPayPlatform.java | 39 ++ .../portal/pay/alipay/AlipayPcPlatform.java | 13 + .../portal/pay/alipay/AlipayWapPlatform.java | 13 + .../pay/alipay/constant/AlipayConstant.java | 71 +++ .../pay/alipay/util/AlipaySignUtil.java | 258 +++++++++++ .../mall/portal/pay/constant/NotifyType.java | 10 + .../mall/portal/pay/constant/PayConstant.java | 18 + .../pay/exeception/BusinessException.java | 53 +++ .../mall/portal/pay/util/FilePathUtil.java | 51 +++ .../mall/portal/pay/util/JacksonUtil.java | 160 +++++++ .../portal/pay/util/MatrixToImageWriter.java | 50 ++ .../mall/portal/pay/util/NumberUtils.java | 24 + .../mall/portal/pay/util/QRCodeUtil.java | 85 ++++ .../mall/portal/pay/util/RequestUtil.java | 114 +++++ .../portal/pay/util/SpringContextHolder.java | 39 ++ .../macro/mall/portal/pay/util/XMLUtil.java | 87 ++++ .../macro/mall/portal/pay/vo/PayNotify.java | 85 ++++ .../macro/mall/portal/pay/vo/PayOrder.java | 79 ++++ .../macro/mall/portal/pay/vo/PaySelectVo.java | 46 ++ .../macro/mall/portal/pay/vo/PayUrlVo.java | 30 ++ .../com/macro/mall/portal/pay/vo/Refund.java | 136 ++++++ .../mall/portal/pay/vo/RefundResult.java | 73 +++ .../pay/weixin/AbstraceWeixinPlatform.java | 430 ++++++++++++++++++ .../portal/pay/weixin/WeixinAppPlatform.java | 53 +++ .../pay/weixin/WeixinJsapiPlatform.java | 48 ++ .../portal/pay/weixin/WeixinWapPlatform.java | 35 ++ .../pay/weixin/WinxinNativePlatform.java | 40 ++ .../weixin/constant/WeixinPayConstant.java | 43 ++ .../portal/pay/weixin/util/WeixinPayUtil.java | 138 ++++++ .../macro/mall/portal/util/ConfigUtil.java | 106 +++++ .../com/macro/mall/portal/util/DateUtil.java | 53 +++ .../main/resources/application-dev.properties | 3 + .../src/main/resources/env_config.properties | 22 + 43 files changed, 3129 insertions(+), 2 deletions(-) create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/AbstractPayPlatform.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/BypassForFreePlatform.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/PayMethod.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/PayMethods.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/PayPlatform.java create mode 100755 mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AbstraceAlipayPlatform.java create mode 100755 mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayAppPlatform.java create mode 100755 mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayDirectPayPlatform.java create mode 100755 mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayPcPlatform.java create mode 100755 mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayWapPlatform.java create mode 100755 mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/constant/AlipayConstant.java create mode 100755 mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/util/AlipaySignUtil.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/constant/NotifyType.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/constant/PayConstant.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/exeception/BusinessException.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/util/FilePathUtil.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/util/JacksonUtil.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/util/MatrixToImageWriter.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/util/NumberUtils.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/util/QRCodeUtil.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/util/RequestUtil.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/util/SpringContextHolder.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/util/XMLUtil.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayNotify.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayOrder.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PaySelectVo.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayUrlVo.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/vo/Refund.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/vo/RefundResult.java create mode 100755 mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/AbstraceWeixinPlatform.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinAppPlatform.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinJsapiPlatform.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinWapPlatform.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WinxinNativePlatform.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/constant/WeixinPayConstant.java create mode 100755 mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/util/WeixinPayUtil.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/util/ConfigUtil.java create mode 100644 mall-portal/src/main/resources/env_config.properties diff --git a/mall-mbg/src/main/java/com/macro/mall/model/OmsOrderReturnApply.java b/mall-mbg/src/main/java/com/macro/mall/model/OmsOrderReturnApply.java index 8d09f01..9513224 100644 --- a/mall-mbg/src/main/java/com/macro/mall/model/OmsOrderReturnApply.java +++ b/mall-mbg/src/main/java/com/macro/mall/model/OmsOrderReturnApply.java @@ -1,9 +1,12 @@ package com.macro.mall.model; +import lombok.Data; + import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; +@Data public class OmsOrderReturnApply implements Serializable { private Long id; @@ -13,6 +16,8 @@ public class OmsOrderReturnApply implements Serializable { * @mbggenerated */ private Long orderId; + private String payOrderType; + /** * 收货地址表id diff --git a/mall-portal/pom.xml b/mall-portal/pom.xml index 76721f2..cef54d5 100644 --- a/mall-portal/pom.xml +++ b/mall-portal/pom.xml @@ -117,8 +117,36 @@ aliyun-sdk-oss 2.5.0 - - + + commons-configuration + commons-configuration + 1.10 + + + com.fasterxml.jackson.core + jackson-core + 2.7.2 + + + com.fasterxml.jackson.core + jackson-databind + 2.7.2 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.7.3 + + + com.google.zxing + core + 3.1.0 + + + org.bouncycastle + bcprov-jdk15on + 1.56 + diff --git a/mall-portal/src/main/java/com/macro/mall/portal/common/CommonConstant.java b/mall-portal/src/main/java/com/macro/mall/portal/common/CommonConstant.java index 3cc969e..55ce5f5 100644 --- a/mall-portal/src/main/java/com/macro/mall/portal/common/CommonConstant.java +++ b/mall-portal/src/main/java/com/macro/mall/portal/common/CommonConstant.java @@ -58,4 +58,5 @@ public class CommonConstant { public static final int VOTE_LIMITED = 301; public static final String CUSTOM_COURSE_SALT = "FITCAMP_CUSTOMCOURSE_"; + public static final String DOMAIN = ""; } diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/AbstractPayPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/AbstractPayPlatform.java new file mode 100644 index 0000000..2416f72 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/AbstractPayPlatform.java @@ -0,0 +1,28 @@ +package com.macro.mall.portal.pay; + + +import com.macro.mall.model.OmsOrder; +import com.macro.mall.portal.pay.exeception.BusinessException; +import com.macro.mall.portal.pay.vo.PayOrder.PayOrderType; +import com.macro.mall.portal.pay.vo.Refund; +import com.macro.mall.portal.pay.vo.RefundResult; + +import javax.servlet.http.HttpServletRequest; + +public abstract class AbstractPayPlatform implements PayPlatform { + + @Override + public RefundResult parseRefundNotify(HttpServletRequest request, PayMethod paymethod, PayOrderType payOrderType) { + throw new BusinessException(paymethod.name() + " not support parseRefundNotify. "); + } + + + @Override + public RefundResult queryRefund(Refund refund) { + throw new BusinessException("not supported method"); + } + + protected String getGoodsName(OmsOrder order) { + return "Mall商城-" + order.getId(); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/BypassForFreePlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/BypassForFreePlatform.java new file mode 100644 index 0000000..c83cc5c --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/BypassForFreePlatform.java @@ -0,0 +1,93 @@ +package com.macro.mall.portal.pay; + +import com.macro.mall.portal.pay.constant.NotifyType; +import com.macro.mall.portal.pay.constant.PayConstant; +import com.macro.mall.portal.pay.exeception.BusinessException; +import com.macro.mall.portal.pay.vo.PayNotify; +import com.macro.mall.portal.pay.vo.PayOrder; +import com.macro.mall.portal.pay.vo.Refund; +import com.macro.mall.portal.pay.vo.RefundResult; +import org.apache.commons.codec.digest.HmacUtils; +import org.bouncycastle.util.encoders.Hex; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.sql.Timestamp; + +@Service +public class BypassForFreePlatform extends AbstractPayPlatform { + + + private final String domainHostname; + private final String signKey; + + private String generateSignature(String orderId) { + + return Hex.toHexString(HmacUtils.hmacSha256(signKey.getBytes(), orderId.getBytes())); + } + + @Autowired + BypassForFreePlatform(@Value("${mall.domain}") String hostname, + @Value("${pay.bypass.signkey}") String signKey) { + this.domainHostname = hostname; + this.signKey = signKey; + + } + + @Override + public String generatePayUrl(PayOrder order, PayMethod paymethod) { + if (order.getPayAmount().compareTo(new BigDecimal(0)) != 0) + throw new BusinessException("pay amount is not zero"); + + String orderId = Long.toString(order.getOrderId()); + String signature = generateSignature(orderId); + + + return domainHostname + "pay/free/payNotify.html?orderId=" + orderId + "&signature=" + signature; + } + + @Override + public PayNotify parsePayNotify(HttpServletRequest request, PayMethod paymethod, PayOrder.PayOrderType payOrderType) { + String orderId = request.getParameter("orderId"); + String signature = request.getParameter("signature"); + + String desiredSignature = generateSignature(orderId); + if (!signature.toLowerCase().equals(desiredSignature.toLowerCase())) { + throw new BusinessException("无效签名"); + } + + + PayNotify notify = new PayNotify(); + notify.setPayOrderId(orderId); + String orderIdStr = payOrderType == null ? orderId : orderId.replace(payOrderType.name(), ""); + notify.setOrderId(Long.parseLong(orderIdStr)); + notify.setPaymethod(paymethod); + notify.setPaySeireId("bypass-" + orderId); + notify.setPartnerPaysuccessTime(new Timestamp(System.currentTimeMillis())); + notify.setNotifyType(NotifyType.ONLINE); + notify.setTradeAmount(new BigDecimal(0)); + return notify; + } + + @Override + public String payNotifyResponse(boolean success) { + if (success) { + return "success"; + } else { + return "failed"; + } + } + + @Override + public RefundResult refund(Refund refund, PayMethod paymethod) { + RefundResult result = new RefundResult(); + result.setOrderId(refund.getOrderId()); + result.setRefundId(refund.getRefundId()); + result.setRefundAmount(refund.getRefundAmount()); + result.setRefundStatus(PayConstant.REFUND_STATUS_FAILED); + return result; + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/PayMethod.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/PayMethod.java new file mode 100644 index 0000000..5837faa --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/PayMethod.java @@ -0,0 +1,27 @@ +package com.macro.mall.portal.pay; + + +import com.macro.mall.portal.pay.vo.PayOrder; + +/** + * Created by lanxingcan on 2017/6/22. + */ +public interface PayMethod { + String name(); + + PayPlatform getPayPlatform(); + + String getPartnerId(); + + String getAppId(); + + String getApiKey(); + + String getPublicKey(); + + String getPayNotifyUrl(PayOrder payOrder); + + String getRefundNotifyUrl(PayOrder.PayOrderType payOrderType); + + String generatePayUrl(PayOrder payOrder); +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/PayMethods.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/PayMethods.java new file mode 100644 index 0000000..5946c76 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/PayMethods.java @@ -0,0 +1,113 @@ +package com.macro.mall.portal.pay; + + +import com.macro.mall.portal.common.CommonConstant; +import com.macro.mall.portal.pay.alipay.AlipayAppPlatform; +import com.macro.mall.portal.pay.alipay.AlipayPcPlatform; +import com.macro.mall.portal.pay.alipay.AlipayWapPlatform; +import com.macro.mall.portal.pay.util.SpringContextHolder; +import com.macro.mall.portal.pay.vo.PayOrder; +import com.macro.mall.portal.pay.vo.PayOrder.PayOrderType; +import com.macro.mall.portal.pay.weixin.WeixinAppPlatform; +import com.macro.mall.portal.pay.weixin.WeixinJsapiPlatform; +import com.macro.mall.portal.pay.weixin.WeixinWapPlatform; +import com.macro.mall.portal.pay.weixin.WinxinNativePlatform; +import com.macro.mall.portal.util.ConfigUtil; + +public enum PayMethods implements PayMethod { + + alipay_wap(AlipayWapPlatform.class), + alipay_app(AlipayAppPlatform.class), + alipay_pc(AlipayPcPlatform.class), + weixin_wap(WeixinWapPlatform.class), + weixin_app(WeixinAppPlatform.class), + weixin_jsapi(WeixinJsapiPlatform.class), + weixin_native(WinxinNativePlatform.class), + exchange(null), //??? No such bean + free(BypassForFreePlatform.class); // 虚拟支付。用于减脂营兑换生成的订单 + + private Class payPlatformClass; + PayMethods(Class clazz) { + this.payPlatformClass = clazz; + } + + @Override + public PayPlatform getPayPlatform() { +// String beanName = this.name() + "Platform"; + return SpringContextHolder.getApplicationContext().getBean(payPlatformClass); +// return SpringContextHolder.getBean(beanName); + } + + /** + * 获取商户ID + * @return + */ + @Override + public String getPartnerId() { + return ConfigUtil.getString("pay." + this.name() + ".partnerId"); + } + + /** + * 获取appid,微信支付用 + * @return + */ + @Override + public String getAppId() { + return ConfigUtil.getString("pay." + this.name() + ".appId"); + } + + /** + * 获取支付key。对于支付宝来说是md5key + * @return + */ + @Override + public String getApiKey() { + return ConfigUtil.getString("pay." + this.name() + ".apiKey"); + } + + /** + * 获取rsa公钥。支付宝用 + * @return + */ + @Override + public String getPublicKey() { + return ConfigUtil.getString("pay." + this.name() + ".pubKey"); + } + + /** + * 支付成功通知url + * @return + */ + @Override + public String getPayNotifyUrl(PayOrder payOrder) { + if (payOrder.getPayOrderType() == PayOrderType.diet) { + return CommonConstant.DOMAIN + "/pay/" + payOrder.getPayOrderType().name() + "/" + this.name() + "/payNotify.html"; + } else { + if ("useNewSuccessPage".equals(payOrder.getParams())) { + return CommonConstant.DOMAIN + "/pay/" + this.name() + "/payNotifyNew.html"; + } else { + return CommonConstant.DOMAIN + "/pay/" + this.name() + "/payNotify.html"; + } + } + + } + + /** + * 退款通知接口 + * @return + */ + @Override + public String getRefundNotifyUrl(PayOrderType payOrderType) { + return CommonConstant.DOMAIN + "/pay/" + payOrderType.name() + "/" + this.name() + "/refundNotify.html"; + } + + /** + * 生成支付Url + * @param payOrder + * @return + */ + @Override + public String generatePayUrl(PayOrder payOrder) { + return getPayPlatform().generatePayUrl(payOrder, this); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/PayPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/PayPlatform.java new file mode 100644 index 0000000..d0dee38 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/PayPlatform.java @@ -0,0 +1,61 @@ +package com.macro.mall.portal.pay; + +import com.macro.mall.portal.pay.vo.PayNotify; +import com.macro.mall.portal.pay.vo.PayOrder; +import com.macro.mall.portal.pay.vo.PayOrder.PayOrderType; +import com.macro.mall.portal.pay.vo.Refund; +import com.macro.mall.portal.pay.vo.RefundResult; + +import javax.servlet.http.HttpServletRequest; + +public interface PayPlatform { + + /** + * 生成支付链接或者是支付参数 + * 可能会有网络调用,所以不能在事务中调用 + * + * @param order + * @return + */ + public String generatePayUrl(PayOrder order, PayMethod paymethod); + + /** + * 解析支付通知参数 + * + * @param request + * @return + */ + public PayNotify parsePayNotify(HttpServletRequest request, PayMethod paymethod, PayOrderType payOrderType); + + /** + * 生成支付通知的响应 + * + * @param success + * @return + */ + public String payNotifyResponse(boolean success); + + /** + * 调用支付平台的退款接口退款 + * + * @param refund + * @return + */ + public RefundResult refund(Refund refund, PayMethod paymethod); + + /** + * 解析退款通知参数 + * + * @param request + * @return + */ + public RefundResult parseRefundNotify(HttpServletRequest request, PayMethod paymethod, PayOrderType payOrderType); + + /** + * 查询退款状态 + * @param refund + * @return + */ + public RefundResult queryRefund(Refund refund); + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AbstraceAlipayPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AbstraceAlipayPlatform.java new file mode 100755 index 0000000..0d74fe8 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AbstraceAlipayPlatform.java @@ -0,0 +1,202 @@ +package com.macro.mall.portal.pay.alipay; + + +import com.macro.mall.portal.pay.AbstractPayPlatform; +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.alipay.constant.AlipayConstant; +import com.macro.mall.portal.pay.alipay.util.AlipaySignUtil; +import com.macro.mall.portal.pay.constant.NotifyType; +import com.macro.mall.portal.pay.constant.PayConstant; +import com.macro.mall.portal.pay.exeception.BusinessException; +import com.macro.mall.portal.pay.util.NumberUtils; +import com.macro.mall.portal.pay.util.RequestUtil; +import com.macro.mall.portal.pay.util.XMLUtil; +import com.macro.mall.portal.pay.vo.PayNotify; +import com.macro.mall.portal.pay.vo.PayOrder.PayOrderType; +import com.macro.mall.portal.pay.vo.Refund; +import com.macro.mall.portal.pay.vo.RefundResult; +import com.macro.mall.portal.util.DateUtil; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.time.DateFormatUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public abstract class AbstraceAlipayPlatform extends AbstractPayPlatform { + + private Logger log = LoggerFactory.getLogger(getClass()); + + /** + * 解析支付成功通知 + */ + @Override + public PayNotify parsePayNotify(HttpServletRequest request, PayMethod paymethod, PayOrderType payOrderType) { + Map paramMap = RequestUtil.getParameterMap(request); + log.info("[alipay]payNotify params:" + paramMap); + String isSuccess = paramMap.get("is_success"); // 只有页面跳转通知时才有这个参数 + // null check + for (String key : AlipayConstant.PAYSUCCESS_NOTICE_NOTNULL_FIELD_LIST) { + if (paramMap.get(key) == null) { + //log.error("[alipay]invalid pay notice. " + key + " is null. " + paramMap); + throw new BusinessException("[alipay]invalid pay notice. absence of necessary field[" + key + "]"); + } + } + if (StringUtils.isNotBlank(isSuccess) && !AlipayConstant.API_RET_SUCCESS.equals(isSuccess)) { + throw new BusinessException("[alipay]payment not success."); + } + // sign check + String sign = paramMap.get("sign"); + String signType = paramMap.get("sign_type"); + if (!AlipaySignUtil.verifySign(sign, signType, paramMap, AlipayConstant.INPUT_CHARSET, paymethod)) { + throw new BusinessException("[alipay]invalid pay notice sign." + paramMap); + } + + // biz check + if (!paramMap.get("notify_type").equals("trade_status_sync")) { + throw new BusinessException("[alipay]invalid pay notice. invalid notify_type:" + paramMap); + } + String tradeStatus = paramMap.get("trade_status"); + if (!AlipayConstant.TRADE_SUCCESS.equals(tradeStatus) && !AlipayConstant.TRADE_FINISHD.equals(tradeStatus)) { + throw new BusinessException("[alipay]invalid pay notice. invalid trade_status:" + paramMap); + } + // build ret + PayNotify notify = new PayNotify(); + String payOrderId = paramMap.get("out_trade_no"); + notify.setPayOrderId(payOrderId); + String orderIdStr = payOrderType == null ? payOrderId : payOrderId.replace(payOrderType.name(), ""); + notify.setOrderId(Long.parseLong(orderIdStr)); + notify.setPaymethod(paymethod); + notify.setPaySeireId(paramMap.get("trade_no")); + if (StringUtils.isBlank(isSuccess)) { + notify.setPartnerPaysuccessTime(Timestamp.valueOf(paramMap.get("gmt_payment"))); + } + notify.setTradeAmount(new BigDecimal(paramMap.get("total_fee"))); + notify.setParams(paramMap.get("extra_common_param")); + if (StringUtils.isNotBlank(isSuccess)) { + notify.setNotifyType(NotifyType.ONLINE); + } else { + notify.setNotifyType(NotifyType.BACK); + } + return notify; + } + + /** + * 请求支付宝退款,支付宝的退款接口是批量的,但是网关每次只提交一笔 + */ + @Override + public RefundResult refund(Refund refund, PayMethod paymethod) { + Map map = new HashMap<>(); + map.put("service", AlipayConstant.REFUND_SERVICE_NAME); + map.put("partner", paymethod.getPartnerId()); + map.put("_input_charset", AlipayConstant.INPUT_CHARSET); + map.put("sign_type", AlipayConstant.REQUEST_SIGN_TYPE); + map.put("notify_url", paymethod.getRefundNotifyUrl(PayOrderType.valueOf(refund.getPayOrderType()))); + map.put("batch_no", DateUtil.dateToStr(refund.getCreateTime(), "yyyyMMdd") + StringUtils.leftPad(""+refund.getRefundId(), 3, '0')); + map.put("refund_date", DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")); + map.put("batch_num", "1"); + map.put("detail_data", refund.getPaySeireId() + "^" + NumberUtils.formatBigDecimal(refund.getRefundAmount(), "0.00") + "^退款"); + map.put("sign", AlipaySignUtil.sign(map, AlipayConstant.INPUT_CHARSET, AlipayConstant.REQUEST_SIGN_TYPE, paymethod)); + + try + { + String paramStr = AlipaySignUtil.getParamStr(map, true, AlipayConstant.INPUT_CHARSET); + + HttpClient httpclient = HttpClients.createDefault(); + HttpGet get = new HttpGet(AlipayConstant.ALIPAY_GATEWAY + paramStr); + RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(3000).setSocketTimeout(5000).build(); + get.setConfig(config); + log.info("[alipay]refund-request:" + paramStr); + HttpResponse response = httpclient.execute(get); + String responseStr = EntityUtils.toString(response.getEntity()); + log.info("[alipay]refund-response:" + responseStr); + if (StringUtils.isBlank(responseStr)) { + throw new BusinessException("[alipay]refund return empty. refundId=" + refund.getRefundId()); + } + Map retMap = XMLUtil.doXMLParse(responseStr); + if (!AlipayConstant.API_RET_SUCCESS.equals(retMap.get("is_success"))) { + throw new BusinessException("[alipay]request refund failed. refundId=" + refund.getRefundId() + ", error=" + retMap.get("error")); + } + RefundResult result = new RefundResult(); + result.setOrderId(refund.getOrderId()); + result.setRefundId(refund.getRefundId()); + result.setRefundAmount(refund.getRefundAmount()); + result.setRefundStatus(PayConstant.REFUND_STATUS_PROCESSED); + if (refund.getPayOrderType() != null) { + result.setPayOrderType(PayOrderType.valueOf(refund.getPayOrderType())); + } + + return result; + } catch (BusinessException e) { + throw e; + } catch (Exception e) { + throw new BusinessException("[alipay]request refund error. refundId=" + refund.getRefundId(), e); + } + } + + /** + * 解析退款异步通知 + */ + @Override + public RefundResult parseRefundNotify(HttpServletRequest request, PayMethod paymethod, PayOrderType payOrderType) { + Map paramMap = RequestUtil.getParameterMap(request); + // null check + for (String key : AlipayConstant.REFUND_NOTICE_NOTNULL_FIELD_LIST) { + if (paramMap.get(key) == null) { + //log.error("[alipay]invalid refund notice. " + key + " is null. " + paramMap); + throw new BusinessException("[alipay]invalid refund notice. absence of necessary field[" + key + "]"); + } + } + // sign check + String sign = paramMap.get("sign"); + String signType = paramMap.get("sign_type"); + if (!AlipaySignUtil.verifySign(sign, signType, paramMap, AlipayConstant.INPUT_CHARSET, paymethod)) { + throw new BusinessException("[alipay]invalid refund notify, verify sign failed."); + } + // biz check + if (!paramMap.get("notify_type").equals("batch_refund_notify")) { + throw new BusinessException("[alipay]invalid refund notice. invalid notify_type:" + paramMap); + } + RefundResult result = new RefundResult(); + result.setRefundId(Long.parseLong(paramMap.get("batch_no").substring(8))); + int successNo = Integer.parseInt(paramMap.get("success_num")); + // 虽然支付宝退款接口是批量的,但是网关这边都是单笔退款 + if (successNo == 0) { + result.setRefundStatus(PayConstant.REFUND_STATUS_FAILED); + log.error("[alipy]refund failed. " + paramMap); + } else if (successNo == 1) { + result.setRefundStatus(PayConstant.REFUND_STATUS_SUCC); + } else { + throw new BusinessException("[alipay]invalid refund notify, success_num bigger than 1 : " + successNo); + } + String detail = paramMap.get("result_details"); + String[] detailArr = detail.split("\\^"); + result.setPaySeireId(detailArr[0]); +// result.setPartnerRefundId(paramMap.get("notify_id")); // 退款通知接口里面没有支付宝的退款ID,所以暂时用通知ID做退款ID + result.setRefundAmount(new BigDecimal(detailArr[1])); + if (payOrderType != null) { + result.setPayOrderType(payOrderType); + } + return result; + } + + @Override + public String payNotifyResponse(boolean success) { + if (success) { + return "success"; + } else { + return "failed"; + } + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayAppPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayAppPlatform.java new file mode 100755 index 0000000..3471c7c --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayAppPlatform.java @@ -0,0 +1,64 @@ +package com.macro.mall.portal.pay.alipay; + +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.alipay.constant.AlipayConstant; +import com.macro.mall.portal.pay.alipay.util.AlipaySignUtil; +import com.macro.mall.portal.pay.util.NumberUtils; +import com.macro.mall.portal.pay.vo.PayOrder; +import org.apache.commons.lang.StringUtils; +import org.springframework.stereotype.Service; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; + +@Service("alipay_appPlatform") +public class AlipayAppPlatform extends AbstraceAlipayPlatform { + + @Override + public String generatePayUrl(PayOrder order, PayMethod paymethod) { + // 1.创建用于签名的参数map + Map paramMap = genParamMap(order, paymethod); + // 2.生成并添加签名 + String sign = AlipaySignUtil.sign(paramMap, AlipayConstant.INPUT_CHARSET, AlipayConstant.REQUEST_SIGN_TYPE, paymethod); + try { + addQuoteStringToMap(paramMap, "sign", URLEncoder.encode(sign, "utf-8")); + } catch (UnsupportedEncodingException e) { + } + // 3.生成组合字符串 + return AlipaySignUtil.getParamStr(paramMap, false, AlipayConstant.INPUT_CHARSET); + } + + private Map genParamMap(PayOrder order, PayMethod paymethod) { + Map signParaMap = new HashMap(); + // 基本参数 + // 连接所有非空参数 + addQuoteStringToMap(signParaMap, "service", AlipayConstant.APP_SECURITYPAY_PAY_SERVICE_NAME); + addQuoteStringToMap(signParaMap, "partner", paymethod.getPartnerId()); + addQuoteStringToMap(signParaMap, "_input_charset", AlipayConstant.INPUT_CHARSET); + addQuoteStringToMap(signParaMap, "notify_url", paymethod.getPayNotifyUrl(order)); + addQuoteStringToMap(signParaMap, "sign_type", AlipaySignUtil.SIGN_TYPE_RSA); + + // 业务参数 + // 连接所有非空参数 + addQuoteStringToMap(signParaMap, "out_trade_no", order.getPayOrderId()); + addQuoteStringToMap(signParaMap, "subject", order.getGoodsName()); + addQuoteStringToMap(signParaMap, "payment_type", "1"); + addQuoteStringToMap(signParaMap, "seller_id", paymethod.getPartnerId()); + addQuoteStringToMap(signParaMap, "total_fee", NumberUtils.formatBigDecimal(order.getPayAmount(), "0.00")); + addQuoteStringToMap(signParaMap, "body", order.getGoodsName()); + + return signParaMap; + } + + private void addQuoteStringToMap(Map map, String key, String value) { + if(map!=null && StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) { + map.put(key, quote(value)); + } + } + + private String quote(String str) { + return "\"" + str + "\""; + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayDirectPayPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayDirectPayPlatform.java new file mode 100755 index 0000000..82b6b98 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayDirectPayPlatform.java @@ -0,0 +1,39 @@ +package com.macro.mall.portal.pay.alipay; + +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.alipay.constant.AlipayConstant; +import com.macro.mall.portal.pay.alipay.util.AlipaySignUtil; +import com.macro.mall.portal.pay.util.NumberUtils; +import com.macro.mall.portal.pay.vo.PayOrder; +import org.apache.commons.lang.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +public abstract class AlipayDirectPayPlatform extends AbstraceAlipayPlatform { + + protected abstract String getServiceName(); + + @Override + public String generatePayUrl(PayOrder order, PayMethod paymethod) { + Map map = new HashMap(); + map.put("service", getServiceName()); + map.put("partner", paymethod.getPartnerId()); + map.put("_input_charset", AlipayConstant.INPUT_CHARSET); + map.put("sign_type", AlipayConstant.REQUEST_SIGN_TYPE); + map.put("notify_url", paymethod.getPayNotifyUrl(order)); + map.put("return_url", paymethod.getPayNotifyUrl(order)); + map.put("out_trade_no", order.getPayOrderId()); + map.put("subject", order.getGoodsName()); + map.put("payment_type", "1"); + map.put("total_fee", NumberUtils.formatBigDecimal(order.getPayAmount(), "0.00")); + map.put("seller_id", paymethod.getPartnerId()); + if (StringUtils.isNotBlank(order.getParams())) { + map.put("passback_params", order.getParams()); + } + map.put("sign", AlipaySignUtil.sign(map, AlipayConstant.INPUT_CHARSET, AlipayConstant.REQUEST_SIGN_TYPE, paymethod)); + String paramStr = AlipaySignUtil.getParamStr(map, true, AlipayConstant.INPUT_CHARSET); + + return AlipayConstant.ALIPAY_GATEWAY + paramStr; + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayPcPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayPcPlatform.java new file mode 100755 index 0000000..6666c6f --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayPcPlatform.java @@ -0,0 +1,13 @@ +package com.macro.mall.portal.pay.alipay; + +import com.macro.mall.portal.pay.alipay.constant.AlipayConstant; +import org.springframework.stereotype.Service; + +@Service("alipay_pcPlatform") +public class AlipayPcPlatform extends AlipayDirectPayPlatform { + + @Override + public String getServiceName() { + return AlipayConstant.DIRECT_PAY_SERVICE_NAME; + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayWapPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayWapPlatform.java new file mode 100755 index 0000000..32b2b56 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/AlipayWapPlatform.java @@ -0,0 +1,13 @@ +package com.macro.mall.portal.pay.alipay; + +import com.macro.mall.portal.pay.alipay.constant.AlipayConstant; +import org.springframework.stereotype.Service; + +@Service("alipay_wapPlatform") +public class AlipayWapPlatform extends AlipayDirectPayPlatform { + + @Override + public String getServiceName() { + return AlipayConstant.WAP_CREATE_DIRECT_SERVICE_NAME; + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/constant/AlipayConstant.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/constant/AlipayConstant.java new file mode 100755 index 0000000..7434c65 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/constant/AlipayConstant.java @@ -0,0 +1,71 @@ +package com.macro.mall.portal.pay.alipay.constant; + +import com.macro.mall.portal.pay.alipay.util.AlipaySignUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class AlipayConstant { + // 支付宝网关地址,pc端、app支付、订单查询用这个 + public static String ALIPAY_GATEWAY = "https://mapi.alipay.com/gateway.do?"; + + // 请求编码,支付宝所有用到编码的地方统一用这个 + public static String INPUT_CHARSET = "utf-8"; + + // 请求的签名方式 + public static String REQUEST_SIGN_TYPE = AlipaySignUtil.SIGN_TYPE_RSA; + + // 即时到帐交易(pc)service_name + public static final String DIRECT_PAY_SERVICE_NAME = "create_direct_pay_by_user"; + // 客户支付service_name + public static final String APP_SECURITYPAY_PAY_SERVICE_NAME = "mobile.securitypay.pay"; + // WAP即时到帐交易service_name + public static final String WAP_CREATE_DIRECT_SERVICE_NAME = "alipay.wap.create.direct.pay.by.user"; + // 单笔订单查询service_name + public static String CHARGE_QUERY_SERVICE_NAME = "single_trade_query"; + // 无密退款接口service_name + public static String REFUND_SERVICE_NAME = "refund_fastpay_by_platform_nopwd"; + + // 支付宝交易状态 + public static String TRADE_SUCCESS = "TRADE_SUCCESS"; + public static String TRADE_FINISHD = "TRADE_FINISHED"; + + // 退款成功 + public static String REFUND_SUCCESS = "REFUND_SUCCESS"; + + /** 支付成功通知接口中必须字段 */ + @SuppressWarnings("serial") + public static List PAYSUCCESS_NOTICE_NOTNULL_FIELD_LIST = Collections + .unmodifiableList(new ArrayList() { + { + add("notify_type"); + add("notify_id"); + add("sign_type"); + add("sign"); + add("out_trade_no"); + add("trade_no"); + add("trade_status"); + // add("gmt_payment"); + } + }); + + /** 支付成功通知接口中必须字段 */ + @SuppressWarnings("serial") + public static List REFUND_NOTICE_NOTNULL_FIELD_LIST = Collections + .unmodifiableList(new ArrayList() { + { + add("notify_type"); + add("notify_id"); + add("sign_type"); + add("sign"); + add("batch_no"); + add("success_num"); + add("result_details"); + } + }); + + // 订单查询 + public static String API_RET_SUCCESS = "T"; + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/util/AlipaySignUtil.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/util/AlipaySignUtil.java new file mode 100755 index 0000000..4295758 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/alipay/util/AlipaySignUtil.java @@ -0,0 +1,258 @@ +package com.macro.mall.portal.pay.alipay.util; + + +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.exeception.BusinessException; +import com.macro.mall.portal.util.ConfigUtil; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.crypto.Cipher; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.*; + +public class AlipaySignUtil { + + private static Log log = LogFactory.getLog(AlipaySignUtil.class); + + private static final String SIGN_ALGORITHMS = "SHA1WithRSA"; + + public static String SIGN_TYPE_RSA = "RSA"; + public static String SIGN_TYPE_MD5 = "MD5"; + + public static String sign(Map paramMap, String charset, String signType, PayMethod paymethod) { + if (SIGN_TYPE_RSA.equals(signType)) { + return getRsaSign(paramMap, charset); + } else if (SIGN_TYPE_MD5.equals(signType)) { + return getMd5Sign(paramMap, charset, paymethod); + } else { + throw new BusinessException("[alipay]invalid sign type : " + signType); + } + } + + /** + * 验证签名,支持RSA和md5 + * + * @param sign + * @param signType + * @param paramMap + * @param charset + * @return + */ + public static boolean verifySign(String sign, String signType, Map paramMap, String charset, PayMethod paymethod) { + if (SIGN_TYPE_RSA.equals(signType)) { + return verifyRsaSign(sign, paramMap, charset, paymethod); + } else if (SIGN_TYPE_MD5.equals(signType)) { + return verifyMd5Sign(sign, paramMap, charset, paymethod); + } else { + throw new BusinessException("[alipay]invalid sign type : " + signType); + } + } + + /** + * RSA签名 + * + * @param paramMap + * @param charset + * @return + */ + private static String getRsaSign(Map paramMap, String charset) { + Map map = new HashMap(paramMap); + map.remove("sign"); + map.remove("sign_type"); + String source = getParamStr(map, false, charset); + + PrivateKey priKey; + try { + priKey = getPrivateKey(); + Signature signature = Signature.getInstance(SIGN_ALGORITHMS); + + signature.initSign(priKey); + signature.update( source.getBytes(charset) ); + + byte[] signed = signature.sign(); + String signStr = StringUtils.remove(Base64.encodeBase64String(signed), System.getProperty("line.separator")); + + log.info("[alipay-sign-rsa]source=" + source + ", sign=" + signStr); + return signStr; + } + catch (Exception e) { + log.fatal("[alipay-sign-rsa]sign error. source=" + source, e); + } + return null; + } + + /** + * RSA验签 + * + * @param sign + * @param paramMap + * @param charset + * @return + */ + private static boolean verifyRsaSign(String sign, Map paramMap, String charset, PayMethod paymethod) { + Map map = new HashMap(paramMap); + map.remove("sign"); + map.remove("sign_type"); + if (sign == null) { + return false; + } + String source = getParamStr(map, false, charset); + + try + { + PublicKey pubKey = getPubKey(paymethod); + Signature signature = Signature.getInstance(SIGN_ALGORITHMS); + + signature.initVerify(pubKey); + signature.update(source.getBytes(charset)); + + boolean bverify = signature.verify(Base64.decodeBase64(sign)); + log.info("[alipay-verifySign-rsa]source=" + source + ", reqSign=" + sign + ", result=" + bverify); + return bverify; + } + catch (Exception e) { + log.fatal("[alipay-verifySign-rsa]source=" + source + ", reqSign=" + sign, e); + } + return false; + } + + /** + * MD5签名 + * + * @param paramMap + * @param charset + * @return + */ + private static String getMd5Sign(Map paramMap, String charset, PayMethod paymethod) { + Map map = new HashMap(paramMap); + map.remove("sign"); + map.remove("sign_type"); + String source = getParamStr(map, false, null) + paymethod.getApiKey(); + String sign = null; + try { + sign = DigestUtils.md5Hex(source.getBytes(charset)); + } + catch (UnsupportedEncodingException e) { + } + log.info("[alipay-sign-ms5]source=" + source + ", sign=" + sign); + return sign; + } + + /** + * MD5验签 + * + * @param sign + * @param paramMap + * @param charset + * @return + */ + private static boolean verifyMd5Sign(String sign, Map paramMap, String charset, PayMethod paymethod) { + if (sign == null) { + return false; + } + return sign.equals(getMd5Sign(paramMap, charset, paymethod)); + } + + /** + * 解密 + * @param content 密文 + * @return 解密后的字符串 + */ + public static String decrypt(String content, String charset) throws Exception { + PrivateKey prikey = getPrivateKey(); + + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, prikey); + + InputStream ins = new ByteArrayInputStream(Base64.decodeBase64(content)); + ByteArrayOutputStream writer = new ByteArrayOutputStream(); + //rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密 + byte[] buf = new byte[128]; + int bufl; + + while ((bufl = ins.read(buf)) != -1) { + byte[] block = null; + + if (buf.length == bufl) { + block = buf; + } else { + block = new byte[bufl]; + for (int i = 0; i < bufl; i++) { + block[i] = buf[i]; + } + } + + writer.write(cipher.doFinal(block)); + } + + return new String(writer.toByteArray(), charset); + } + + public static String getParamStr(Map paramMap, boolean encodeValue, String charset) { + List keyList = new ArrayList<>(paramMap.keySet()); + Collections.sort(keyList); + StringBuilder sb = new StringBuilder(); + for (String key : keyList) { + sb.append(key).append("="); + try { + String value = encodeValue ? URLEncoder.encode(paramMap.get(key), charset) : paramMap.get(key); + //value = value.replace("+", "%20"); + sb.append(value).append("&"); + } + catch (UnsupportedEncodingException e){} + } + if (sb.length() > 3) { + sb.deleteCharAt(sb.length() - 1); + } + + return sb.toString(); + } + + /** + * 得到支付网关私钥 + * @throws Exception + */ + private static PrivateKey getPrivateKey() throws Exception { + + String prikey = ConfigUtil.getString("fittime.privateKey"); + byte[] keyBytes = Base64.decodeBase64(prikey); + + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + + return privateKey; + } + + /** + * 获取支付宝的公钥 + * @return + * @throws Exception + */ + private static PublicKey getPubKey(PayMethod paymethod) throws Exception { + String pubkeyStr = paymethod.getPublicKey(); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + byte[] encodedKey = Base64.decodeBase64(pubkeyStr); + PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); + return pubKey; + } + + public static void main(String[] args) { + String s = DigestUtils.md5Hex("Fittime1991"); + System.out.println(s); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/constant/NotifyType.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/constant/NotifyType.java new file mode 100644 index 0000000..9963c43 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/constant/NotifyType.java @@ -0,0 +1,10 @@ +package com.macro.mall.portal.pay.constant; + +/** + * 支付平台支付成功通知的类型 + */ +public enum NotifyType { + + BACK, // 服务器端异步通知 + ONLINE // 前台页面跳转通知 +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/constant/PayConstant.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/constant/PayConstant.java new file mode 100644 index 0000000..b87d222 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/constant/PayConstant.java @@ -0,0 +1,18 @@ +package com.macro.mall.portal.pay.constant; + +public class PayConstant { + + /******************************* + * 充值状态 TB_REFUND.REFUND_STATUS + * 0 初始化 + * 1 请求成功,未明确返回 + * 2 失败需重发 + * 3 成功 + * 4 失败需人工处理 + */ + public static final int REFUND_STATUS_INITIAL = 0; + public static final int REFUND_STATUS_PROCESSED = 1; + public static final int REFUND_STATUS_FAILED_NEED_RESEND = 2; + public static final int REFUND_STATUS_SUCC = 3; + public static final int REFUND_STATUS_FAILED = 4; +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/exeception/BusinessException.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/exeception/BusinessException.java new file mode 100644 index 0000000..c4e2b6f --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/exeception/BusinessException.java @@ -0,0 +1,53 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.pay.exeception; + +public class BusinessException extends RuntimeException { + private static final long serialVersionUID = 2874510430549463213L; + private int returnCode; + + public BusinessException() { + } + + public BusinessException(String message, Throwable cause) { + super(message, cause); + } + + public BusinessException(String message) { + super(message); + } + + public BusinessException(Throwable cause) { + super(cause); + } + + public BusinessException(int returnCode) { + this.returnCode = returnCode; + } + + public BusinessException(Exception e, int returnCode) { + super(e); + this.returnCode = returnCode; + } + + public BusinessException(String message, int returnCode) { + super(message); + this.returnCode = returnCode; + } + + public BusinessException(String message, Exception e, int returnCode) { + super(message, e); + this.returnCode = returnCode; + } + + public int getReturnCode() { + return this.returnCode; + } + + public void setReturnCode(int returnCode) { + this.returnCode = returnCode; + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/util/FilePathUtil.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/FilePathUtil.java new file mode 100644 index 0000000..4746484 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/FilePathUtil.java @@ -0,0 +1,51 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.pay.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; + +public class FilePathUtil { + private static Log log = LogFactory.getLog(FilePathUtil.class); + + public FilePathUtil() { + } + + public static String getAbsolutePathOfWebRoot() { + String result = null; + result = getAbsolutePathOfClassPath(); + result = result.replace(File.separatorChar + "WEB-INF" + File.separatorChar + "classes", ""); + log.debug("Absolute Path Of WebRoot:" + result); + return result; + } + + public static String getAbsolutePathOfClassPath() { + String result = null; + + try { + File file = new File(getURLOfClassPath().toURI()); + result = file.getAbsolutePath(); + log.debug("Absolute Path Of ClassPath:" + result); + } catch (URISyntaxException var2) { + log.error(var2); + } + + return result; + } + + private static URL getURLOfClassPath() { + return getClassLoader().getResource(""); + } + + private static ClassLoader getClassLoader() { + log.debug("classLoader:" + Thread.currentThread().getContextClassLoader()); + return Thread.currentThread().getContextClassLoader(); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/util/JacksonUtil.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/JacksonUtil.java new file mode 100644 index 0000000..886e69c --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/JacksonUtil.java @@ -0,0 +1,160 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.pay.util; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonParser.Feature; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.*; +import java.util.Map.Entry; + +public class JacksonUtil { + private static Logger log = LoggerFactory.getLogger(JacksonUtil.class); + private static ObjectMapper objectMapper = new ObjectMapper(); + private static XmlMapper xmlMapper; + + public JacksonUtil() { + } + + public static String map2xml(Map map) { + try { + return xmlMapper.writeValueAsString(map); + } catch (JsonProcessingException var2) { + log.error("map2xml error. map=" + map, var2); + return null; + } + } + + public static String obj2xml(Object obj) { + try { + return xmlMapper.writeValueAsString(obj); + } catch (JsonProcessingException var2) { + log.error("obj2xml error. obj=" + ToStringBuilder.reflectionToString(obj), var2); + return null; + } + } + + public static String obj2json(Object obj) { + try { + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException var2) { + log.error("obj2json error. obj=" + ToStringBuilder.reflectionToString(obj), var2); + return null; + } + } + + public static T xml2pojo(String xmlStr, Class clazz) { + try { + return xmlMapper.readValue(xmlStr, clazz); + } catch (IOException var3) { + log.error("xml2pojo error. xml=" + xmlStr + "clazz=" + clazz, var3); + return null; + } + } + + public static T json2pojo(String jsonStr, Class clazz) { + try { + return objectMapper.readValue(jsonStr, clazz); + } catch (IOException var3) { + log.error("json2pojo error. json=" + jsonStr + ", clazz=" + clazz, var3); + return null; + } + } + + public static Map xml2map(String xmlStr) { + try { + return (Map)xmlMapper.readValue(xmlStr, Map.class); + } catch (IOException var2) { + log.error("xml2map error. xml=" + xmlStr, var2); + return null; + } + } + + public static Map json2map(String jsonStr) { + try { + return (Map)objectMapper.readValue(jsonStr, Map.class); + } catch (IOException var2) { + log.error("json2map error, json=" + jsonStr, var2); + return null; + } + } + + public static Map json2map(String jsonStr, Class clazz) { + try { + Map> map = (Map)objectMapper.readValue(jsonStr, new TypeReference>() { + }); + Map result = new HashMap(); + Iterator var4 = map.entrySet().iterator(); + + while(var4.hasNext()) { + Entry> entry = (Entry)var4.next(); + result.put(entry.getKey(), objectMapper.convertValue(entry.getValue(), clazz)); + } + + return result; + } catch (Exception var6) { + log.error("json2map error, json=" + jsonStr + ", clazz=" + clazz, var6); + return null; + } + } + + public static List json2list(String jsonArrayStr, Class clazz) throws Exception { + List> list = (List)objectMapper.readValue(jsonArrayStr, new TypeReference>() { + }); + List result = new ArrayList(); + Iterator var4 = list.iterator(); + + while(var4.hasNext()) { + Map map = (Map)var4.next(); + result.add(map2pojo(map, clazz)); + } + + return result; + } + + public static T map2pojo(Map map, Class clazz) { + return objectMapper.convertValue(map, clazz); + } + + public static String json2xml(String jsonStr) throws Exception { + JsonNode root = objectMapper.readTree(jsonStr); + String xml = xmlMapper.writeValueAsString(root); + return xml; + } + + public static String xml2json(String xml) throws Exception { + StringWriter w = new StringWriter(); + JsonParser jp = xmlMapper.getFactory().createParser(xml); + JsonGenerator jg = objectMapper.getFactory().createGenerator(w); + + while(jp.nextToken() != null) { + jg.copyCurrentEvent(jp); + } + + jp.close(); + jg.close(); + return w.toString(); + } + + static { + objectMapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + xmlMapper = new XmlMapper(); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/util/MatrixToImageWriter.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/MatrixToImageWriter.java new file mode 100644 index 0000000..da23937 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/MatrixToImageWriter.java @@ -0,0 +1,50 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.pay.util; + +import com.google.zxing.common.BitMatrix; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; + +public final class MatrixToImageWriter { + private static final int BLACK = -16777216; + private static final int WHITE = -1; + + private MatrixToImageWriter() { + } + + public static BufferedImage toBufferedImage(BitMatrix matrix) { + int width = matrix.getWidth(); + int height = matrix.getHeight(); + BufferedImage image = new BufferedImage(width, height, 1); + + for(int x = 0; x < width; ++x) { + for(int y = 0; y < height; ++y) { + image.setRGB(x, y, matrix.get(x, y) ? -16777216 : -1); + } + } + + return image; + } + + public static void writeToFile(BitMatrix matrix, String format, File file) throws IOException { + BufferedImage image = toBufferedImage(matrix); + if (!ImageIO.write(image, format, file)) { + throw new IOException("Could not write an image of format " + format + " to " + file); + } + } + + public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) throws IOException { + BufferedImage image = toBufferedImage(matrix); + if (!ImageIO.write(image, format, stream)) { + throw new IOException("Could not write an image of format " + format); + } + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/util/NumberUtils.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/NumberUtils.java new file mode 100644 index 0000000..83bd1d4 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/NumberUtils.java @@ -0,0 +1,24 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.pay.util; + +import java.math.BigDecimal; +import java.text.DecimalFormat; + +public class NumberUtils { + public NumberUtils() { + } + + public static String formatBigDecimal(BigDecimal num, String pattern) { + DecimalFormat format = new DecimalFormat(pattern); + return format.format(num); + } + + public static void main(String[] args) { + BigDecimal b = new BigDecimal(124.34D); + System.out.println(formatBigDecimal(b, "#,##0.00")); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/util/QRCodeUtil.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/QRCodeUtil.java new file mode 100644 index 0000000..adcf70c --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/QRCodeUtil.java @@ -0,0 +1,85 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.pay.util; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.common.BitMatrix; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class QRCodeUtil { + private static Logger log = LoggerFactory.getLogger(QRCodeUtil.class); + + public QRCodeUtil() { + } + + public static void buildQRCode(String content, OutputStream out) { + try { + MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); + Map hints = new HashMap(); + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); + BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 400, 400, hints); + MatrixToImageWriter.writeToStream(bitMatrix, "jpg", out); + } catch (Exception var5) { + log.error("build qrCode error. content=" + content, var5); + } + + } + + public static void buildQRCode(String content, HttpServletResponse response) { + try { + buildQRCode(content, (OutputStream)response.getOutputStream()); + } catch (Exception var3) { + log.error("build qrCode error. content=" + content, var3); + } + + } + + public static void buildQrCodeWithLogo(String content, String logoPath, OutputStream out) { + try { + MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); + Map hints = new HashMap(); + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); + BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 400, 400, hints); + BufferedImage qrcode = MatrixToImageWriter.toBufferedImage(bitMatrix); + addLogo(qrcode, logoPath); + ImageIO.write(qrcode, "jpg", out); + } catch (Exception var7) { + log.error("build qrCode error. content=" + content, var7); + } + + } + + private static void addLogo(BufferedImage qrcode, String logoPath) { + try { + BufferedImage logo = ImageIO.read(new File(logoPath)); + int widthLogo = logo.getWidth((ImageObserver)null) > qrcode.getWidth() * 13 / 100 ? qrcode.getWidth() * 13 / 100 : logo.getWidth((ImageObserver)null); + int heightLogo = logo.getHeight((ImageObserver)null) > qrcode.getHeight() * 13 / 100 ? qrcode.getHeight() * 13 / 100 : logo.getHeight((ImageObserver)null); + int x = (qrcode.getWidth() - widthLogo) / 2; + int y = (qrcode.getHeight() - heightLogo) / 2; + Graphics2D g = qrcode.createGraphics(); + g.drawImage(logo, x, y, widthLogo, heightLogo, (ImageObserver)null); + g.dispose(); + logo.flush(); + qrcode.flush(); + } catch (IOException var8) { + log.error("add logo to qrcode error. logoPath=" + logoPath); + } + + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/util/RequestUtil.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/RequestUtil.java new file mode 100644 index 0000000..3a5d168 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/RequestUtil.java @@ -0,0 +1,114 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.pay.util; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class RequestUtil { + public RequestUtil() { + } + + public static Map getParameterMap(HttpServletRequest request) { + Map retMap = new HashMap(); + Map rawMap = request.getParameterMap(); + Iterator it = rawMap.keySet().iterator(); + + while(true) { + String key; + Object valueObj; + do { + if (!it.hasNext()) { + return retMap; + } + + key = (String)it.next(); + valueObj = rawMap.get(key); + } while(valueObj == null); + + String value = ""; + if (!(valueObj instanceof String[])) { + value = (String)valueObj; + } else { + String[] var7 = (String[])((String[])valueObj); + int var8 = var7.length; + + for(int var9 = 0; var9 < var8; ++var9) { + String v = var7[var9]; + value = value + v + ","; + } + + value = value.substring(0, value.length() - 1); + } + + retMap.put(key, value); + } + } + + public static String getPostData(HttpServletRequest request) { + ServletInputStream is = null; + + BufferedReader br; + try { + String buffer; + try { + is = request.getInputStream(); + if (is != null) { + br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + buffer = null; + StringBuffer sb = new StringBuffer(); + + while((buffer = br.readLine()) != null) { + sb.append(buffer); + } + + String var5 = sb.toString(); + return var5; + } + + br = null; + } catch (IOException var16) { + buffer = null; + return buffer; + } + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException var15) { + var15.printStackTrace(); + } + } + + } + + return null; + } + + public static String getRemoteIp(HttpServletRequest request) { + String rip = request.getRemoteAddr(); + String xff = request.getHeader("X-Forwarded-For"); + String ip; + if (xff != null && xff.length() != 0) { + int px = xff.lastIndexOf(44); + if (px != -1) { + ip = xff.substring(px + 1); + } else { + ip = xff; + } + } else { + ip = rip; + } + + return ip.trim(); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/util/SpringContextHolder.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/SpringContextHolder.java new file mode 100644 index 0000000..7835d28 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/SpringContextHolder.java @@ -0,0 +1,39 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.pay.util; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +@Component +public class SpringContextHolder implements ApplicationContextAware { + private static ApplicationContext applicationContext; + + public SpringContextHolder() { + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + applicationContext = applicationContext; + } + + public static ApplicationContext getApplicationContext() { + checkApplicationContext(); + return applicationContext; + } + + public static T getBean(String name) { + checkApplicationContext(); + return (T) applicationContext.getBean(name); + } + + private static void checkApplicationContext() { + if (applicationContext == null) { + throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextUtil"); + } + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/util/XMLUtil.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/XMLUtil.java new file mode 100644 index 0000000..a9347cb --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/util/XMLUtil.java @@ -0,0 +1,87 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.pay.util; + +import com.macro.mall.portal.pay.exeception.BusinessException; +import org.jdom.Document; +import org.jdom.Element; +import org.jdom.JDOMException; +import org.jdom.input.SAXBuilder; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class XMLUtil { + public XMLUtil() { + } + + public static Map doXMLParse(String strxml) throws JDOMException, IOException { + strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); + if (null != strxml && !"".equals(strxml)) { + Map m = new HashMap(); + InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); + SAXBuilder builder = new SAXBuilder(); + Document doc = builder.build(in); + Element root = doc.getRootElement(); + List list = root.getChildren(); + + String k; + String v; + for(Iterator it = list.iterator(); it.hasNext(); m.put(k, v)) { + Element e = (Element)it.next(); + k = e.getName(); + v = ""; + List children = e.getChildren(); + if (children.isEmpty()) { + v = e.getTextNormalize(); + } else { + v = getChildrenText(children); + } + } + + in.close(); + return m; + } else { + throw new BusinessException("error when parse notify postData, invalid format. " + strxml); + } + } + + public static String getChildrenText(List children) { + StringBuffer sb = new StringBuffer(); + if (!children.isEmpty()) { + Iterator it = children.iterator(); + + while(it.hasNext()) { + Element e = (Element)it.next(); + String name = e.getName(); + String value = e.getTextNormalize(); + List list = e.getChildren(); + sb.append("<" + name + ">"); + if (!list.isEmpty()) { + sb.append(getChildrenText(list)); + } + + sb.append(value); + sb.append(""); + } + } + + return sb.toString(); + } + + public static String getXMLEncoding(String strxml) throws JDOMException, IOException { + InputStream in = new ByteArrayInputStream(strxml.getBytes()); + SAXBuilder builder = new SAXBuilder(); + Document doc = builder.build(in); + in.close(); + return (String)doc.getProperty("encoding"); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayNotify.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayNotify.java new file mode 100644 index 0000000..3612efc --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayNotify.java @@ -0,0 +1,85 @@ +package com.macro.mall.portal.pay.vo; + + +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.constant.NotifyType; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +public class PayNotify { + + private String payOrderId; + private Long orderId; // 本地订单ID + private PayMethod paymethod; // 支付方式 + private String paySeireId; // 支付流水号 + private BigDecimal tradeAmount; // 支付金额 + private Timestamp partnerPaysuccessTime; // 支付成功时间 + private NotifyType notifyType; + private String params; // 支付时传递的参数,原样回传 + + public String getPayOrderId() { + return payOrderId; + } + + public void setPayOrderId(String payOrderId) { + this.payOrderId = payOrderId; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public PayMethod getPaymethod() { + return paymethod; + } + + public void setPaymethod(PayMethod paymethod) { + this.paymethod = paymethod; + } + + public String getPaySeireId() { + return paySeireId; + } + + public void setPaySeireId(String paySeireId) { + this.paySeireId = paySeireId; + } + + public BigDecimal getTradeAmount() { + return tradeAmount; + } + + public void setTradeAmount(BigDecimal tradeAmount) { + this.tradeAmount = tradeAmount; + } + + public Timestamp getPartnerPaysuccessTime() { + return partnerPaysuccessTime; + } + + public void setPartnerPaysuccessTime(Timestamp partnerPaysuccessTime) { + this.partnerPaysuccessTime = partnerPaysuccessTime; + } + + public NotifyType getNotifyType() { + return notifyType; + } + + public void setNotifyType(NotifyType notifyType) { + this.notifyType = notifyType; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayOrder.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayOrder.java new file mode 100644 index 0000000..d4f1ccd --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayOrder.java @@ -0,0 +1,79 @@ +package com.macro.mall.portal.pay.vo; + +import java.math.BigDecimal; + +public class PayOrder { + + private long orderId; + private String goodsName; + private BigDecimal payAmount; + private String userIp; + private String weixinOpenId; + private PayOrderType payOrderType; + private String params; // 自定义参数 + + public enum PayOrderType { + shop, diet + } + + public String getPayOrderId() { + return payOrderType.name() + orderId; + } + + public long getOrderId() { + return orderId; + } + + public void setOrderId(long orderId) { + this.orderId = orderId; + } + + public String getGoodsName() { + return goodsName; + } + + public void setGoodsName(String goodsName) { + this.goodsName = goodsName; + } + + public BigDecimal getPayAmount() { + return payAmount; + } + + public void setPayAmount(BigDecimal payAmount) { + this.payAmount = payAmount; + } + + public String getUserIp() { + return userIp; + } + + public void setUserIp(String userIp) { + this.userIp = userIp; + } + + public String getWeixinOpenId() { + return weixinOpenId; + } + + public void setWeixinOpenId(String weixinOpenId) { + this.weixinOpenId = weixinOpenId; + } + + public PayOrderType getPayOrderType() { + return payOrderType; + } + + public void setPayOrderType(PayOrderType payOrderType) { + this.payOrderType = payOrderType; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PaySelectVo.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PaySelectVo.java new file mode 100644 index 0000000..b1e6736 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PaySelectVo.java @@ -0,0 +1,46 @@ +package com.macro.mall.portal.pay.vo; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +public class PaySelectVo { + + private List payUrlList = new ArrayList<>(); + private BigDecimal payAmount; + private long closeTime; + private long orderId; + + public List getPayUrlList() { + return payUrlList; + } + + public void setPayUrlList(List payUrlList) { + this.payUrlList = payUrlList; + } + + public BigDecimal getPayAmount() { + return payAmount; + } + + public void setPayAmount(BigDecimal payAmount) { + this.payAmount = payAmount; + } + + public long getCloseTime() { + return closeTime; + } + + public void setCloseTime(long closeTime) { + this.closeTime = closeTime; + } + + public long getOrderId() { + return orderId; + } + + public void setOrderId(long orderId) { + this.orderId = orderId; + } + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayUrlVo.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayUrlVo.java new file mode 100644 index 0000000..dd5294d --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/PayUrlVo.java @@ -0,0 +1,30 @@ +package com.macro.mall.portal.pay.vo; + +public class PayUrlVo { + + private String type; + private String payUrl; + + public PayUrlVo(){} + + public PayUrlVo(String type, String payUrl) { + this.type = type; + this.payUrl = payUrl; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getPayUrl() { + return payUrl; + } + + public void setPayUrl(String payUrl) { + this.payUrl = payUrl; + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/Refund.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/Refund.java new file mode 100644 index 0000000..c6d270f --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/Refund.java @@ -0,0 +1,136 @@ +package com.macro.mall.portal.pay.vo; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; + +public class Refund implements Serializable { + + private static final long serialVersionUID = 8148843465555401098L; + + private long refundId; + private long orderId; + private String paymethod; // 支付方式,冗余 + private String paySeireId; // 支付流水号,支付宝退款的时候需要 + private BigDecimal refundAmount; + private BigDecimal payAmount; // 订单支付金额,微信退款的时候需要 + private String refundReason; + private int refundStatus; + private String note; + private Timestamp createTime; + private Timestamp updateTime; + private String payOrderType; + + public Refund() {} + + public Refund(long orderId, String paymethod, String paySeireId, BigDecimal refundAmount, + int refundStatus, String refundReason, String note, BigDecimal payAmount, String payOrderType) { + super(); + this.orderId = orderId; + this.paymethod = paymethod; + this.paySeireId = paySeireId; + this.refundAmount = refundAmount; + this.refundStatus = refundStatus; + this.refundReason = refundReason; + this.note = note; + this.payAmount = payAmount; + this.payOrderType = payOrderType; + } + + public long getRefundId() { + return refundId; + } + + public void setRefundId(long refundId) { + this.refundId = refundId; + } + + public long getOrderId() { + return orderId; + } + + public void setOrderId(long orderId) { + this.orderId = orderId; + } + + public String getPaymethod() { + return paymethod; + } + + public void setPaymethod(String paymethod) { + this.paymethod = paymethod; + } + + public BigDecimal getRefundAmount() { + return refundAmount; + } + + public void setRefundAmount(BigDecimal refundAmount) { + this.refundAmount = refundAmount; + } + + public String getRefundReason() { + return refundReason; + } + + public void setRefundReason(String refundReason) { + this.refundReason = refundReason; + } + + public int getRefundStatus() { + return refundStatus; + } + + public void setRefundStatus(int refundStatus) { + this.refundStatus = refundStatus; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } + + public Timestamp getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Timestamp updateTime) { + this.updateTime = updateTime; + } + + public String getPaySeireId() { + return paySeireId; + } + + public void setPaySeireId(String paySeireId) { + this.paySeireId = paySeireId; + } + + public BigDecimal getPayAmount() { + return payAmount; + } + + public void setPayAmount(BigDecimal payAmount) { + this.payAmount = payAmount; + } + + public String getPayOrderType() { + return payOrderType; + } + + public void setPayOrderType(String payOrderType) { + this.payOrderType = payOrderType; + } + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/RefundResult.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/RefundResult.java new file mode 100644 index 0000000..0d5bd78 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/vo/RefundResult.java @@ -0,0 +1,73 @@ +package com.macro.mall.portal.pay.vo; + +import com.macro.mall.portal.pay.vo.PayOrder.PayOrderType; + +import java.math.BigDecimal; + +public class RefundResult { + + private long orderId; + private long refundId; + private String paySeireId; // 订单支付流水号 + private String refundSeireId; + private int refundStatus; + private BigDecimal refundAmount; + private PayOrderType payOrderType; + + public long getOrderId() { + return orderId; + } + + public void setOrderId(long orderId) { + this.orderId = orderId; + } + + public long getId() { + return refundId; + } + + public void setRefundId(long refundId) { + this.refundId = refundId; + } + + public String getPaySeireId() { + return paySeireId; + } + + public void setPaySeireId(String paySeireId) { + this.paySeireId = paySeireId; + } + + public String getRefundSeireId() { + return refundSeireId; + } + + public void setRefundSeireId(String refundSeireId) { + this.refundSeireId = refundSeireId; + } + + public int getRefundStatus() { + return refundStatus; + } + + public void setRefundStatus(int refundStatus) { + this.refundStatus = refundStatus; + } + + public BigDecimal getRefundAmount() { + return refundAmount; + } + + public void setRefundAmount(BigDecimal refundAmount) { + this.refundAmount = refundAmount; + } + + public PayOrderType getPayOrderType() { + return payOrderType; + } + + public void setPayOrderType(PayOrderType payOrderType) { + this.payOrderType = payOrderType; + } + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/AbstraceWeixinPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/AbstraceWeixinPlatform.java new file mode 100755 index 0000000..d0b3839 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/AbstraceWeixinPlatform.java @@ -0,0 +1,430 @@ +package com.macro.mall.portal.pay.weixin; + + +import com.macro.mall.portal.pay.AbstractPayPlatform; +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.PayMethods; +import com.macro.mall.portal.pay.constant.NotifyType; +import com.macro.mall.portal.pay.constant.PayConstant; +import com.macro.mall.portal.pay.exeception.BusinessException; +import com.macro.mall.portal.pay.util.JacksonUtil; +import com.macro.mall.portal.pay.util.NumberUtils; +import com.macro.mall.portal.pay.util.RequestUtil; +import com.macro.mall.portal.pay.vo.PayNotify; +import com.macro.mall.portal.pay.vo.PayOrder; +import com.macro.mall.portal.pay.vo.PayOrder.PayOrderType; +import com.macro.mall.portal.pay.vo.Refund; +import com.macro.mall.portal.pay.vo.RefundResult; +import com.macro.mall.portal.pay.weixin.constant.WeixinPayConstant; +import com.macro.mall.portal.pay.weixin.util.WeixinPayUtil; +import com.macro.mall.portal.util.DateUtil; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLContext; +import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +public abstract class AbstraceWeixinPlatform extends AbstractPayPlatform { + + private Logger log = LoggerFactory.getLogger(getClass()); + + protected abstract String getTradeType(); + + protected String getSceneInfo() { + return "{\"h5_info\":{\"type\":\"Wap\",\"wap_url\":\"https://mall.rjfittime.com\",\"wap_name\":\"Mall商城\"}}"; + } + + protected Map prepay(PayOrder order, PayMethod paymethod) { + try { + Map param = new HashMap(); + param.put("appid", paymethod.getAppId()); + System.out.println("appid " + paymethod.getAppId()); + param.put("mch_id", paymethod.getPartnerId()); + System.out.println("mch_id " + paymethod.getPartnerId()); + param.put("nonce_str", WeixinPayUtil.CreateNoncestr(WeixinPayConstant.NONCESTR_LENGTH)); + System.out.println("nonce_str " + WeixinPayUtil.CreateNoncestr(WeixinPayConstant.NONCESTR_LENGTH)); + param.put("body", order.getGoodsName()); + System.out.println("body " + order.getGoodsName()); + param.put("out_trade_no", order.getPayOrderId()); + System.out.println("out_trade_no " + order.getPayOrderId()); + param.put("total_fee", NumberUtils.formatBigDecimal(order.getPayAmount().multiply(new BigDecimal("100")), "#")); + System.out.println("total_fee " + NumberUtils.formatBigDecimal(order.getPayAmount().multiply(new BigDecimal("100")), "#")); + param.put("spbill_create_ip", order.getUserIp()); + System.out.println("spbill_create_ip " + order.getUserIp()); + param.put("notify_url", paymethod.getPayNotifyUrl(order)); + System.out.println("notify_url " + paymethod.getPayNotifyUrl(order)); + String tradeType = getTradeType(); + param.put("trade_type", tradeType); + System.out.println("trade_type " + tradeType); + if (tradeType.equals(WeixinPayConstant.TRADE_TYPE_JSAPI)) { + param.put("openid", order.getWeixinOpenId()); + } + System.out.println("openid " + order.getWeixinOpenId()); + if (tradeType.equals(WeixinPayConstant.TRADE_TYPE_WAP)) { + param.put("scene_info", getSceneInfo()); + } + System.out.println("scene_info " + getSceneInfo()); + if (StringUtils.isNotBlank(order.getParams())) { + param.put("attach", order.getParams()); + } + System.out.println("attach " + order.getParams()); + String signStr = WeixinPayUtil.formatUnSignParaMap(param, false, false) + "&key=" + paymethod.getApiKey(); + System.out.println("signStr " + signStr); + String sign = DigestUtils.md5Hex(signStr).toUpperCase(); + param.put("sign", sign); + System.out.println("sign " + sign); + + String xml = JacksonUtil.obj2xml(param).replace("HashMap", "xml"); + log.info("[weixinNew]requestToAddCharge-request: " + xml); + System.out.println("xml " + xml); + + HttpClient httpclient = HttpClients.createDefault(); + HttpPost post = new HttpPost(WeixinPayConstant.UNIFIED_ORDER_URL); + RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(3000).setSocketTimeout(5000).build(); + post.setConfig(config); + HttpEntity entity = new StringEntity(xml, "utf-8"); + post.setEntity(entity); + + int retryTimes = 1; + boolean retry = true; + while (retry) { + retry = false; + HttpResponse response = httpclient.execute(post); + String retxml = EntityUtils.toString(response.getEntity(), "utf-8"); + log.info("[weixinNew]requestToAddCharge-response:" + retxml); + if (StringUtils.isBlank(retxml)) { + log.error("[weixinNew]prepay response is empty. orderId=" + order.getPayOrderId()); + throw new BusinessException("prepay response is empty. orderId=" + order.getPayOrderId()); + } + @SuppressWarnings("unchecked") + Map retMap = JacksonUtil.xml2pojo(retxml, HashMap.class); + // 调用是否成功 + String retCode = retMap.get("return_code"); + if (!WeixinPayConstant.RETURN_CODE_SUCCESS.equals(retCode)) { + log.error("[weixinNew]prepay failed. orderId=" + order.getPayOrderId() + ", return_code=" + retCode + ", return_msg=" + retMap.get("return_msg")); + throw new BusinessException("prepay failed. orderId=" + order.getPayOrderId()); + } + // 身份校验 + if (!paymethod.getPartnerId().equals(retMap.get("mch_id")) || !paymethod.getAppId().equals(retMap.get("appid"))) { + log.error("[weixinNew]prepay error. invalid partner. paymethod=" + paymethod.name() + ", retxml=" + retxml); + throw new BusinessException("prepay error. invalid partner."); + } + // 签名 + String retSign = retMap.remove("sign"); + signStr = WeixinPayUtil.formatUnSignParaMap(retMap, false, false) + "&key=" + paymethod.getApiKey(); + sign = DigestUtils.md5Hex(signStr).toUpperCase(); + if (!sign.equals(retSign)) { + log.error("[weixinNew]prepay error. invalid response sign. response=" + retxml + ", sign=" + sign); + throw new BusinessException("prepay error. invalid response sign"); + } + + // 交易结果校验 + String resultCode = retMap.get("result_code"); + if (WeixinPayConstant.RETURN_CODE_SUCCESS.equals(resultCode)) { + // 预下单成功 + String prepayId = retMap.get("prepay_id"); + } else { + String errCode = retMap.get("err_code"); + // 微信系统错误,需要重试 + if (WeixinPayConstant.SYSTEM_ERROR.equals(errCode)) { + if (retryTimes < 3) { + retryTimes++; + retry = true; + continue; + } else { + log.error("[weixinNew]partner system error when requestToAddCharge, and retry exceed maxTimes, retxml: " + retxml); + throw new BusinessException("partner system error when requestToAddCharge, and retry exceed maxTimes, " + retMap.get("err_code_des")); + } + } else if (WeixinPayConstant.ERROR_PAID.equals(errCode)) { + // 订单已经支付 + log.warn("already paid. orderId=" + order.getPayOrderId()); + throw new BusinessException("already paid. orderId=" + order.getPayOrderId()); + } else { + // 其他错误 + log.error("[weixinNew]prepay failed. orderId=" + order.getPayOrderId() + ", err_code=" + errCode + ", err_code_des=" + retMap.get("err_code_des")); + throw new BusinessException("prepay failed. orderId=" + order.getPayOrderId() + ", err_code=" + errCode + ", err_code_des=" + retMap.get("err_code_des")); + } + } + return retMap; + } + } catch (Exception e) { + e.printStackTrace(); + log.error("error when requestToAddCharge, " + e.getMessage(), e); + throw new BusinessException("requestToAddCharge error. orderId=" + order.getPayOrderId(), e); + } + return null; + } + + @Override + public PayNotify parsePayNotify(HttpServletRequest request, PayMethod paymethod, PayOrderType payOrderType) { + String xml = RequestUtil.getPostData(request); + log.info("[weixinNew]payNotify : " + xml); + if (StringUtils.isBlank(xml)) { + log.error("[weixinNew]payNotify content is empty. paymethod=" + paymethod.name()); + throw new BusinessException("[weixinNew]payNotify content is empty. paymethod=" + paymethod.name()); + } + @SuppressWarnings("unchecked") + Map map = JacksonUtil.xml2pojo(xml, HashMap.class); + String returnCode = map.get("return_code"); + if (!WeixinPayConstant.RETURN_CODE_SUCCESS.equals(returnCode)) { + log.error("[weixinNew]payNotify error. return failed. paymethod=" + paymethod.name() + ", return_code=" + returnCode + ", return_msg=" + map.get("return_msg") + ", xml=" + xml); + throw new BusinessException("payNotify error. return failed. paymethod=" + paymethod.name() + ", return_code=" + returnCode + ", return_msg=" + map.get("return_msg") + ", xml=" + xml); + } + + // 身份校验 + if (!paymethod.getPartnerId().equals(map.get("mch_id")) || !paymethod.getAppId().equals(map.get("appid"))) { + log.error("[weixinNew]payNotify error. invalid partner. paymethod=" + paymethod.name() + ", xml=" + xml); + throw new BusinessException("payNotify error. invalid partner. paymethod=" + paymethod.name()); + } + // 签名校验 + String notifySign = map.remove("sign"); + String signStr = WeixinPayUtil.formatUnSignParaMap(map, false, false) + "&key=" + paymethod.getApiKey(); + String sign = DigestUtils.md5Hex(signStr).toUpperCase(); + if (!sign.equals(notifySign)) { + log.error("[weixinNew]invalid payNotify sign. paymethod=" + paymethod.name() + ", xml=" + xml + ", sign=" + sign); + throw new BusinessException("invalid payNotify sign. paymethod=" + paymethod.name() + ", xml=" + xml + ", sign=" + sign); + } + + // 业务结果 + String resultCode = map.get("result_code"); + if (!WeixinPayConstant.RETURN_CODE_SUCCESS.equals(resultCode)) { + log.error("[weixinNew]payNotify error. paymethod=" + paymethod.name() + ", err_code=" + map.get("err_code") + ", err_code_des=" + map.get("err_code_des")); + throw new BusinessException("payNotify error. paymethod=" + paymethod.name() + ", err_code=" + map.get("err_code") + ", err_code_des=" + map.get("err_code_des")); + } + PayNotify notify = new PayNotify(); + String payOrderId = map.get("out_trade_no"); + notify.setPayOrderId(payOrderId); + String orderIdStr = payOrderType == null ? payOrderId : payOrderId.replace(payOrderType.name(), ""); + notify.setOrderId(Long.parseLong(orderIdStr)); + notify.setPaymethod(paymethod); + notify.setPaySeireId(map.get("transaction_id")); + notify.setTradeAmount(new BigDecimal(map.get("total_fee")).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN)); + notify.setPartnerPaysuccessTime(new Timestamp(DateUtil.strToDate(map.get("time_end"), "yyyyMMddHHmmss").getTime())); + notify.setNotifyType(NotifyType.BACK); // 微信只有后台通知 + notify.setParams(map.get("attach")); // 公共回传参数 + return notify; + } + + @Override + public RefundResult refund(Refund refund, PayMethod paymethod) { + RefundResult result = new RefundResult(); + try { + Map map = new HashMap(); + map.put("appid", paymethod.getAppId()); + map.put("mch_id", paymethod.getPartnerId()); + map.put("nonce_str", WeixinPayUtil.CreateNoncestr(WeixinPayConstant.NONCESTR_LENGTH)); + String outTradeNo = StringUtils.isBlank(refund.getPayOrderType()) ? refund.getOrderId()+"" : (refund.getPayOrderType() + refund.getOrderId()); + map.put("out_trade_no", outTradeNo); + map.put("out_refund_no", "" + refund.getRefundId()); + map.put("total_fee", NumberUtils.formatBigDecimal(refund.getPayAmount().multiply(new BigDecimal("100")), "#")); + map.put("refund_fee", NumberUtils.formatBigDecimal(refund.getPayAmount().multiply(new BigDecimal("100")), "#")); + map.put("op_user_id", paymethod.getPartnerId()); + String signStr = WeixinPayUtil.formatUnSignParaMap(map, false, false) + "&key=" + paymethod.getApiKey(); + String sign = DigestUtils.md5Hex(signStr).toUpperCase(); + map.put("sign", sign); + + String xml = JacksonUtil.obj2xml(map).replace("HashMap", "xml"); + log.info("[weixinNew]requestToRefund-request: " + xml); + SSLContext sslContext = WeixinPayUtil.getSSLContext(paymethod); + HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build(); + HttpPost post = new HttpPost(WeixinPayConstant.REFUND_URL); + StringEntity entity = new StringEntity(xml); + post.setEntity(entity); + HttpResponse response = httpClient.execute(post); + String retxml = EntityUtils.toString(response.getEntity(), "utf-8"); + log.info("[weixinNew]requestToRefund-response : " + retxml); + + if (StringUtils.isBlank(retxml)) { + log.error("[weixinNew]requestToRefund error. response is empty. refundId=" + refund.getRefundId()); + throw new BusinessException("requestToRefund error. response is empty. refundid=" + refund.getRefundId()); + } + @SuppressWarnings("unchecked") + Map retMap = JacksonUtil.xml2pojo(retxml, HashMap.class); + String returnCode = retMap.get("return_code"); + if (!WeixinPayConstant.RETURN_CODE_SUCCESS.equals(returnCode)) { + log.error("[weixinNew]requestToRefund failed. refundId=" + refund.getRefundId() + "return_code=" + returnCode + ", return_msg=" + retMap.get("return_msg")); + throw new BusinessException("requestToRefund failed. refundId=" + refund.getRefundId() + "return_code=" + returnCode + ", return_msg=" + retMap.get("return_msg")); + } + // 身份校验 + if (!paymethod.getPartnerId().equals(retMap.get("mch_id")) || !paymethod.getAppId().equals(retMap.get("appid"))) { + log.error("[weixinNew]requestToRefund error. invalid partner. paymethod=" + paymethod.name() + ", xml=" + retxml); + throw new BusinessException("requestToRefund error. invalid partner. paymethod=" + paymethod.name()); + } + // 签名校验 + String notifySign = retMap.remove("sign"); + signStr = WeixinPayUtil.formatUnSignParaMap(retMap, false, false) + "&key=" + paymethod.getApiKey(); + sign = DigestUtils.md5Hex(signStr).toUpperCase(); + if (!sign.equals(notifySign)) { + log.error("[weixinNew]requestToRefund error. invalid response sign. paymethod=" + paymethod.name() + ", xml=" + retxml + ", sign=" + sign); + throw new BusinessException("requestToRefund error. invalid response sign. paymethod=" + paymethod.name()); + } + // 业务校验 + if(StringUtils.isNotBlank(refund.getPayOrderType()) && retMap.get("out_trade_no") != null) { + retMap.put("out_trade_no", retMap.get("out_trade_no").replace(refund.getPayOrderType(), "")); + } + checkRefundResult(refund, retMap, paymethod); + // 请求完成,并返回了结果 + result.setOrderId(Long.parseLong(retMap.get("out_trade_no"))); + result.setRefundId(refund.getRefundId()); + result.setRefundAmount(new BigDecimal(retMap.get("refund_fee")).divide(new BigDecimal(100))); + result.setRefundStatus(PayConstant.REFUND_STATUS_PROCESSED); + if (StringUtils.isNotBlank(refund.getPayOrderType())) { + result.setPayOrderType(PayOrderType.valueOf(refund.getPayOrderType())); + } + } catch (BusinessException e) { + throw e; + } catch (Exception e) { + // 请求抛出异常,合作方接口问题 + log.error("[Weixin] error when requestToRefund, " + e.getMessage(), e); + throw new BusinessException(e.getMessage(), e); + } + + return result; + } + + private void checkRefundResult(Refund refund, Map retMap, PayMethod paymethod) { + String resultCode = retMap.get("result_code"); + if (!WeixinPayConstant.RETURN_CODE_SUCCESS.equals(resultCode)) { + throw new BusinessException("refund failed. refundId=" + refund.getRefundId() + ", err_code=" + retMap.get("err_code") + ", err_code_des=" + retMap.get("err_code_des")); + } + if (!String.valueOf(refund.getOrderId()).equals(retMap.get("out_trade_no"))) { + throw new BusinessException("invalid ret out_trade_no " + retMap.get("out_trade_no") + "|" + refund.getOrderId()); + } + if (!String.valueOf(refund.getRefundId()).equals(retMap.get("out_refund_no"))) { + throw new BusinessException("invalid ret out_refund_no " + retMap.get("out_refund_no") + "|" + refund.getRefundId()); + } + if (StringUtils.isBlank(retMap.get("transaction_id"))) { + throw new BusinessException("invalid refund response, transaction_id is empty. refundId=" + refund.getRefundId()); + } + if (StringUtils.isBlank(retMap.get("refund_id"))) { + throw new BusinessException("invalid refund response. refund_id is empty. refundId=" + refund.getRefundId()); + } + String refundFeeStr = NumberUtils.formatBigDecimal(refund.getRefundAmount().multiply(new BigDecimal("100")), "#"); + if (!refundFeeStr.equals(retMap.get("refund_fee"))) { + throw new BusinessException("invalid ret refund_fee. refundId=" + refund.getRefundId() + ", " + retMap.get("refund_fee") + "|" + refundFeeStr); + } + String totalFeeStr = NumberUtils.formatBigDecimal(refund.getPayAmount().multiply(new BigDecimal("100")), "#"); + if (!totalFeeStr.equals(retMap.get("total_fee"))) { + throw new BusinessException("invalid ret total_fee. refundId=" + refund.getRefundId() + ", " + retMap.get("total_fee") + "|" + totalFeeStr); + } + } + + public RefundResult queryRefund(Refund refund) { + RefundResult result = new RefundResult(); + try { + PayMethods paymethod = PayMethods.valueOf(refund.getPaymethod()); + Map map = new HashMap(); + map.put("appid", paymethod.getAppId()); + map.put("mch_id", paymethod.getPartnerId()); + map.put("nonce_str", WeixinPayUtil.CreateNoncestr(WeixinPayConstant.NONCESTR_LENGTH)); + map.put("out_refund_no", "" + refund.getRefundId()); + String signStr = WeixinPayUtil.formatUnSignParaMap(map, false, false) + "&key=" + paymethod.getApiKey(); + String sign = DigestUtils.md5Hex(signStr).toUpperCase(); + map.put("sign", sign); + + String xml = JacksonUtil.obj2xml(map).replace("HashMap", "xml"); + HttpClient httpclient = HttpClients.createDefault(); + HttpPost post = new HttpPost(WeixinPayConstant.UNIFIED_ORDER_URL); + RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(3000).setSocketTimeout(5000).build(); + post.setConfig(config); + HttpEntity entity = new StringEntity(xml, "utf-8"); + post.setEntity(entity); + HttpResponse response = httpclient.execute(post); + String retxml = EntityUtils.toString(response.getEntity()); + if (StringUtils.isBlank(retxml)) { + throw new BusinessException("query refund error. response is empty. refundId=" + refund.getRefundId()); + } + @SuppressWarnings("unchecked") + Map retMap = JacksonUtil.xml2pojo(retxml, HashMap.class); + String returnCode = retMap.get("return_code"); + if (!WeixinPayConstant.RETURN_CODE_SUCCESS.equals(returnCode)) { + throw new BusinessException("query charge failed. return_code=" + returnCode + ", return_msg=" + retMap.get("return_msg")); + } + // 身份校验 + if (!paymethod.getPartnerId().equals(retMap.get("mch_id")) || !paymethod.getAppId().equals(retMap.get("appid"))) { + log.error("[weixinNew]refund failed. invalid partner. paymethod=" + paymethod.name() + ", xml=" + retxml); + throw new BusinessException("[weixinNew]payNotify error. invalid partner. paymethod=" + paymethod.name()); + } + // 签名校验 + String notifySign = retMap.remove("sign"); + signStr = WeixinPayUtil.formatUnSignParaMap(retMap, false, false) + "&key=" + paymethod.getApiKey(); + sign = DigestUtils.md5Hex(signStr).toUpperCase(); + if (!sign.equals(notifySign)) { + throw new BusinessException("[weixinNew]invalid refund sign. paymethod=" + paymethod.name()); + } + if(refund.getPayOrderType() != null) { + retMap.put("out_trade_no", retMap.get("out_trade_no").replace(refund.getPayOrderType(), "")); + } + String resultCode = retMap.get("result_code"); + if (!WeixinPayConstant.RETURN_CODE_SUCCESS.equals(resultCode)) { + throw new BusinessException("query refund failed. refundId=" + refund.getRefundId() + ", err_code=" + retMap.get("err_code") + ", err_code_des=" + retMap.get("err_code")); + } + if (!String.valueOf(refund.getOrderId()).equals(retMap.get("out_trade_no"))) { + throw new BusinessException("invalid ret out_trade_no " + retMap.get("out_trade_no") + "|" + refund.getOrderId()); + } + if (!String.valueOf(refund.getRefundId()).equals(retMap.get("out_refund_no_0"))) { // 如果用订单Id查询可能会有多笔退款,所以参数名会有编号 + throw new BusinessException("invalid ret out_refund_no " + retMap.get("out_refund_no_0") + "|" + refund.getRefundId()); + } + if (StringUtils.isBlank(retMap.get("transaction_id"))) { + throw new BusinessException("invalid refund response, transaction_id is empty. refundId=" + refund.getRefundId()); + } + if (StringUtils.isBlank(retMap.get("refund_id_0"))) { + throw new BusinessException("invalid refund response. refund_id is empty. refundId=" + refund.getRefundId()); + } + String refundFeeStr = NumberUtils.formatBigDecimal(refund.getRefundAmount().multiply(new BigDecimal("100")), "#"); + if (!refundFeeStr.equals(retMap.get("refund_fee_0"))) { + throw new BusinessException("invalid ret refund_fee. refundId=" + refund.getRefundId() + ", " + retMap.get("refund_fee_0") + "|" + refundFeeStr); + } + String totalFeeStr = NumberUtils.formatBigDecimal(refund.getPayAmount().multiply(new BigDecimal("100")), "#"); + if (!totalFeeStr.equals(retMap.get("total_fee"))) { + throw new BusinessException("invalid ret total_fee. refundId=" + refund.getRefundId() + ", " + retMap.get("total_fee") + "|" + totalFeeStr); + } + + result.setOrderId(Long.parseLong(retMap.get("out_trade_no"))); + result.setRefundId(refund.getRefundId()); + result.setRefundAmount(new BigDecimal(retMap.get("refund_fee")).divide(new BigDecimal(100))); + String refundStatus = retMap.get("refund_status_0"); + if (WeixinPayConstant.REFUND_SUCCESS.equals(refundStatus)) { + result.setRefundStatus(PayConstant.REFUND_STATUS_SUCC); + } else if (WeixinPayConstant.REFUND_FAIL.equals(refundStatus)) { + result.setRefundStatus(PayConstant.REFUND_STATUS_FAILED_NEED_RESEND); + } else if (WeixinPayConstant.REFUND_PROCESSING.equals(refundStatus)) { + result.setRefundStatus(PayConstant.REFUND_STATUS_SUCC); + } else if (WeixinPayConstant.REFUND_NOTSURE.equals(refundStatus)) { + result.setRefundStatus(PayConstant.REFUND_STATUS_FAILED_NEED_RESEND); + } else if (WeixinPayConstant.REFUND_CHANGE.equals(refundStatus)) { + result.setRefundStatus(PayConstant.REFUND_STATUS_FAILED); + } else { + throw new BusinessException("invalid refund status :" + refundStatus + ", refundId=" + refund.getRefundId()); + } + return result; + } catch (BusinessException e) { + throw e; + } catch (Exception e) { + throw new BusinessException("query refund error. refundId=" + refund.getRefundId(), e); + } + } + + @Override + public String payNotifyResponse(boolean success) { + if (success) { + return WeixinPayConstant.PAYNOTIFY_RESPONSE_SUCCESS; + } else { + return WeixinPayConstant.PAYNOTIFY_RESPONSE_FAIL; + } + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinAppPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinAppPlatform.java new file mode 100644 index 0000000..9e46a23 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinAppPlatform.java @@ -0,0 +1,53 @@ +package com.macro.mall.portal.pay.weixin; + + +import com.macro.mall.portal.pay.vo.PayOrder; +import com.macro.mall.portal.pay.weixin.constant.WeixinPayConstant; +import com.macro.mall.portal.pay.weixin.util.WeixinPayUtil; +import com.macro.mall.portal.pay.PayMethod; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.stereotype.Service; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; + +@Service("weixin_appPlatform") +public class WeixinAppPlatform extends AbstraceWeixinPlatform{ + + @Override + protected String getTradeType() { + return WeixinPayConstant.TRADE_TYPE_APP; + } + + @Override + public String generatePayUrl(PayOrder order, PayMethod paymethod) { + Map preOrderRet = prepay(order, paymethod); + + if (preOrderRet == null) { + return null; + } + String prepayId = preOrderRet.get("prepay_id"); + HashMap map = new HashMap(); + map.put("appid", paymethod.getAppId()); + map.put("partnerid", paymethod.getPartnerId()); + map.put("noncestr", WeixinPayUtil.CreateNoncestr(WeixinPayConstant.NONCESTR_LENGTH)); + map.put("timestamp", String.valueOf((System.currentTimeMillis() / 1000))); + map.put("prepayid", prepayId); + map.put("package", "Sign=WXPay"); + + String signStr = WeixinPayUtil.formatUnSignParaMap(map, false, false) + "&key=" + paymethod.getApiKey(); + String sign = DigestUtils.md5Hex(signStr).toUpperCase(); + map.put("sign", sign); + + String str1 = ""; + try { + str1 = "appId=" + paymethod.getAppId() + "&partnerId=" + paymethod.getPartnerId() + "&nonceStr=" + map.get("noncestr") + "&timeStamp=" + map.get("timestamp") + "&prepayId=" + map.get("prepayid") + "&package=" + URLEncoder.encode(map.get("package"), "utf-8") + "&sign=" + sign + "&orderId=" + order.getPayOrderId(); + } catch (UnsupportedEncodingException e){} + + String deepLink = "weixin://app/" + paymethod.getAppId() + "/pay/?" + str1; + + return deepLink;//JacksonUtil.obj2json(map); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinJsapiPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinJsapiPlatform.java new file mode 100644 index 0000000..182cc3d --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinJsapiPlatform.java @@ -0,0 +1,48 @@ +package com.macro.mall.portal.pay.weixin; + + +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.util.JacksonUtil; +import com.macro.mall.portal.pay.vo.PayOrder; +import com.macro.mall.portal.pay.weixin.constant.WeixinPayConstant; +import com.macro.mall.portal.pay.weixin.util.WeixinPayUtil; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + + +@Service("weixin_jsapiPlatform") +public class WeixinJsapiPlatform extends AbstraceWeixinPlatform { + + @Override + public String generatePayUrl(PayOrder order, PayMethod paymethod) { + Map preOrderRet = prepay(order, paymethod); + + if (preOrderRet == null) { + return null; + } + String prepayId = preOrderRet.get("prepay_id"); + + HashMap map = new HashMap(); + map.put("appId", paymethod.getAppId()); + map.put("nonceStr", WeixinPayUtil.CreateNoncestr(WeixinPayConstant.NONCESTR_LENGTH)); + map.put("timeStamp", String.valueOf((System.currentTimeMillis() / 1000))); + map.put("package", "prepay_id=" + prepayId); + map.put("signType", "MD5"); + + String signStr = WeixinPayUtil.formatUnSignParaMap(map, false, false) + "&key=" + paymethod.getApiKey(); + System.out.println(signStr); + String sign = DigestUtils.md5Hex(signStr).toUpperCase(); + map.put("paySign", sign); + + return JacksonUtil.obj2json(map); + } + + @Override + protected String getTradeType() { + return WeixinPayConstant.TRADE_TYPE_JSAPI; + } + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinWapPlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinWapPlatform.java new file mode 100644 index 0000000..56b4f95 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WeixinWapPlatform.java @@ -0,0 +1,35 @@ +package com.macro.mall.portal.pay.weixin; + +import com.macro.mall.portal.common.CommonConstant; +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.vo.PayOrder; +import com.macro.mall.portal.pay.weixin.constant.WeixinPayConstant; +import org.springframework.stereotype.Service; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Map; + +@Service("weixin_wapPlatform") +public class WeixinWapPlatform extends AbstraceWeixinPlatform{ + + @Override + protected String getTradeType() { + return WeixinPayConstant.TRADE_TYPE_WAP; + } + + @Override + public String generatePayUrl(PayOrder order, PayMethod paymethod) { + Map preOrderRet = prepay(order, paymethod); + + if (preOrderRet == null) { + return null; + } + String returl = ""; + try { + returl = URLEncoder.encode(CommonConstant.DOMAIN + "/pay/success.html?orderId=" + order.getOrderId(), "utf-8"); + } catch (UnsupportedEncodingException e) { + } + return preOrderRet.get("mweb_url") + "&redirect_url=" + returl; + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WinxinNativePlatform.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WinxinNativePlatform.java new file mode 100644 index 0000000..9e3b9c6 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/WinxinNativePlatform.java @@ -0,0 +1,40 @@ +package com.macro.mall.portal.pay.weixin; + + +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.util.FilePathUtil; +import com.macro.mall.portal.pay.util.QRCodeUtil; +import com.macro.mall.portal.pay.vo.PayOrder; +import com.macro.mall.portal.pay.weixin.constant.WeixinPayConstant; +import org.apache.commons.codec.binary.Base64; +import org.springframework.stereotype.Service; + +import java.io.ByteArrayOutputStream; +import java.util.Map; + +@Service("weixin_nativePlatform") +public class WinxinNativePlatform extends AbstraceWeixinPlatform{ + + @Override + public String generatePayUrl(PayOrder order, PayMethod paymethod) { + Map preOrderRet = prepay(order, paymethod); + + if (preOrderRet == null) { + return null; + } + String codeUrl = preOrderRet.get("code_url"); + + String wxlogo = FilePathUtil.getAbsolutePathOfClassPath() + "/wxlogo.jpg"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + QRCodeUtil.buildQrCodeWithLogo(codeUrl, wxlogo, out); + String qrcodeStr = Base64.encodeBase64String(out.toByteArray()); + + return "data:image/jpeg;base64," + qrcodeStr; + } + + @Override + protected String getTradeType() { + return WeixinPayConstant.TRADE_TYPE_NATIVE; + } + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/constant/WeixinPayConstant.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/constant/WeixinPayConstant.java new file mode 100644 index 0000000..ec36648 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/constant/WeixinPayConstant.java @@ -0,0 +1,43 @@ +package com.macro.mall.portal.pay.weixin.constant; + +public class WeixinPayConstant { + + public static final int NONCESTR_LENGTH = 16; // 随机串长度 + + // **********************以下常量为新接口用****************** + // 统一下单接口 + public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; + // 订单查询接口 + public static final String QUERY_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery"; + // 退款接口 + public static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund"; + // 退款查询接口 + public static final String QUERY_REFUND_URL = "https://api.mch.weixin.qq.com/pay/refundquery"; + + // 微信支付交易类型 + public static final String TRADE_TYPE_WAP = "MWEB"; // wap支付(H5支付) + public static final String TRADE_TYPE_JSAPI = "JSAPI"; // 公众号支付 + public static final String TRADE_TYPE_APP = "APP"; // app支付 + public static final String TRADE_TYPE_NATIVE = "NATIVE"; // 二维码支付 + + // 接口返回码-成功 + public static final String RETURN_CODE_SUCCESS = "SUCCESS"; + + // 错误码 + public static final String ERROR_PAID = "ORDERPAID"; // 订单已支付 + public static final String SYSTEM_ERROR = "SYSTEMERROR"; // 微信系统内部异常,需要重试 + + // 订单状态 + public static final String TRADE_SUCCESS = "SUCCESS"; + public static final String TRADE_REFUND = "REFUND"; + // 退款状态 + public static final String REFUND_SUCCESS = "SUCCESS"; // 退款成功 + public static final String REFUND_FAIL = "FAIL"; // 退款失败,需要用相同单号重新提交退款 + public static final String REFUND_PROCESSING = "PROCESSING"; // 处理中,代表请求已经提交给银行,但还没有到账。可以当成功处理 + public static final String REFUND_NOTSURE = "NOTSURE"; // 未确定,需要用相同退款单号重新请求 + public static final String REFUND_CHANGE = "CHANGE"; // 原路退回失败,可能是银行卡作废或者冻结了,需要手动转账退款 + + // 支付成功接口的返回值 + public static final String PAYNOTIFY_RESPONSE_SUCCESS = "SUCCESSOK"; + public static final String PAYNOTIFY_RESPONSE_FAIL = "FAILFAIL"; +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/util/WeixinPayUtil.java b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/util/WeixinPayUtil.java new file mode 100755 index 0000000..61e9046 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/pay/weixin/util/WeixinPayUtil.java @@ -0,0 +1,138 @@ +package com.macro.mall.portal.pay.weixin.util; + + +import com.macro.mall.portal.pay.PayMethod; +import com.macro.mall.portal.pay.exeception.BusinessException; +import org.apache.commons.lang.StringUtils; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.util.*; + +public class WeixinPayUtil { + + /** + * 生成指定长度的随机串 + * + * @param length + * @return + */ + public static String CreateNoncestr(int length) { + String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + String res = ""; + Random rd = new Random(); + for (int i = 0; i < length; i++) { + res += chars.charAt(rd.nextInt(chars.length() - 1)); + } + return res; + } + + /** + * + * @param paraMap + * @param urlencode + * 是否把每个参数做urlencode ,utf-8 + * @param valueNull + * value 是空,是否做签名 + * @return + * @throws + */ + public static String formatUnSignParaMap(Map paraMap, boolean urlencode, boolean valueNull) { + + StringBuffer buff = new StringBuffer(); + try { + List> infoIds = new ArrayList>(paraMap.entrySet()); + + Collections.sort(infoIds, new Comparator>() { + @Override + public int compare(Map.Entry o1, Map.Entry o2) { + return (o1.getKey()).toString().compareTo(o2.getKey()); + } + }); + + for (int i = 0; i < infoIds.size(); i++) { + Map.Entry item = infoIds.get(i); + if (StringUtils.isNotBlank(item.getKey())) { + + String key = item.getKey(); + String val = item.getValue(); + + // value为空,是否进行排序 + if (valueNull) { + buff.append(key); + buff.append("="); + if (urlencode) { + val = urlEncode(val); + } + buff.append(val); + buff.append("&"); + } else { + if (StringUtils.isNotBlank(val)) { + buff.append(key); + buff.append("="); + if (urlencode) { + val = urlEncode(val); + } + buff.append(val); + buff.append("&"); + } + } + } + } + + if (buff.length() > 0) { + buff.delete(buff.length() - 1, buff.length()); + } + } catch (Exception e) { + throw new BusinessException("FormatUnSignParaMap exception: " + e.getMessage()); + } + return buff.toString(); + } + + // 特殊字符处理 + public static String urlEncode(String src) throws UnsupportedEncodingException { + return URLEncoder.encode(src, "utf-8").replace("+", "%20"); + } + + /** + * 获取HTTP调用的SSLContext 微信新版用 + * + * @return + * @throws Exception + */ + public static SSLContext getSSLContext(PayMethod paymethod) throws Exception { + return getSSLContext(paymethod.getPartnerId()); + } + + /** + * 获取HTTP调用的SSLContext 微信新版用 + * + * @return + * @throws Exception + */ + public static SSLContext getSSLContext(String partnerId) throws Exception { + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + InputStream instream = WeixinPayUtil.class.getResourceAsStream("/cert/weixin/apiclient_cert_" + partnerId + ".p12"); + try { + keyStore.load(instream, partnerId.toCharArray()); + } finally { + instream.close(); + } + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, partnerId.toCharArray()); + + KeyManager[] kms = kmf.getKeyManagers(); + SSLContext sslContext = null; + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kms, null, new SecureRandom()); + return sslContext; + } + +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/util/ConfigUtil.java b/mall-portal/src/main/java/com/macro/mall/portal/util/ConfigUtil.java new file mode 100644 index 0000000..811ec13 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/util/ConfigUtil.java @@ -0,0 +1,106 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.macro.mall.portal.util; + +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class ConfigUtil { + private static CompositeConfiguration config = new CompositeConfiguration(); + + public ConfigUtil() { + } + + public static String getString(String configXPath) { + return config.getString(configXPath, (String)null); + } + + public static String getString(String configXPath, String defaultValue) { + return config.getString(configXPath, defaultValue); + } + + public static double getDouble(String configXPath, double defaultValue) { + return config.getDouble(configXPath, defaultValue); + } + + public static float getFloat(String configXPath, float defaultValue) { + return config.getFloat(configXPath, defaultValue); + } + + public static int getInt(String configXPath, int defaultValue) { + return config.getInt(configXPath, defaultValue); + } + + public static long getLong(String configXPath, long defaultValue) { + return config.getLong(configXPath, defaultValue); + } + + public static boolean getBoolean(String configXPath, boolean defaultValue) { + return config.getBoolean(configXPath, defaultValue); + } + + public static List getList(String configXPath) { + List values = new ArrayList(); + Iterator var2 = config.getList(configXPath).iterator(); + + while(var2.hasNext()) { + Object o = var2.next(); + values.add(o.toString()); + } + + return values; + } + + public static void setString(String configXPath, String value) { + config.setProperty(configXPath, value); + } + + public static void setInt(String configXPath, int value) { + config.setProperty(configXPath, value); + } + + public static void setFloat(String configXPath, float value) { + config.setProperty(configXPath, value); + } + + public static void setLong(String configXPath, long value) { + config.setProperty(configXPath, value); + } + + public static void setBoolean(String configXPath, boolean value) { + config.setProperty(configXPath, value); + } + + public static void remove(String configXPath) { + config.clearProperty(configXPath); + } + + public static void add(String configXPath, Object value) { + config.addProperty(configXPath, value); + } + + static { + try { + URL commonConfigUrl = ConfigUtil.class.getResource("/common_config.properties"); + PropertiesConfiguration commonConfig = new PropertiesConfiguration(commonConfigUrl); + URL envConfigUrl = ConfigUtil.class.getResource("/env_config.properties"); + PropertiesConfiguration envConfig = new PropertiesConfiguration(envConfigUrl); + + config.addConfiguration(commonConfig); + config.addConfiguration(envConfig); + + } catch (ConfigurationException var8) { + var8.printStackTrace(); + } + + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/util/DateUtil.java b/mall-portal/src/main/java/com/macro/mall/portal/util/DateUtil.java index 68ae96c..21f83cc 100644 --- a/mall-portal/src/main/java/com/macro/mall/portal/util/DateUtil.java +++ b/mall-portal/src/main/java/com/macro/mall/portal/util/DateUtil.java @@ -1,5 +1,7 @@ package com.macro.mall.portal.util; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; @@ -32,4 +34,55 @@ public class DateUtil { calendar.set(Calendar.DAY_OF_MONTH, 1); return calendar.getTime(); } + public static String dateToStr(Date date, String formatStr) { + SimpleDateFormat format = new SimpleDateFormat(formatStr); + String s = ""; + + try { + s = format.format(date); + } catch (Exception var5) { + var5.printStackTrace(); + } + + return s; + } + + public static Date strToDate(String str, String formatStr) { + SimpleDateFormat format = new SimpleDateFormat(formatStr); + Date date = null; + + try { + date = format.parse(str); + } catch (ParseException var5) { + var5.printStackTrace(); + } + + return date; + } + + public static String getWeekMonday() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Calendar calendar = Calendar.getInstance(); + calendar.setFirstDayOfWeek(2); + calendar.setTime(new Date()); + calendar.set(7, calendar.getFirstDayOfWeek()); + return format.format(calendar.getTime()); + } + + public static String getWeekFriday() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Calendar calendar = Calendar.getInstance(); + calendar.setFirstDayOfWeek(2); + calendar.set(7, calendar.getFirstDayOfWeek() + 4); + return format.format(calendar.getTime()); + } + + public static String getLastWeekSaturday() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Calendar calendar = Calendar.getInstance(); + int dayOfWeek = calendar.get(7) - 1; + int offset2 = 6 - dayOfWeek; + calendar.add(5, offset2 - 7); + return format.format(calendar.getTime()); + } } diff --git a/mall-portal/src/main/resources/application-dev.properties b/mall-portal/src/main/resources/application-dev.properties index 6b7bb35..bfacf69 100644 --- a/mall-portal/src/main/resources/application-dev.properties +++ b/mall-portal/src/main/resources/application-dev.properties @@ -6,6 +6,9 @@ logging.level.org.springframework.data.mongodb.core=info logging.level.com.macro.mall.mapper=debug logging.level.com.macro.mall.portal.dao=debug #===logging end=== + +mall.domain = 123 +pay.bypass.signkey= #===datasource start=== spring.datasource.url=jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root diff --git a/mall-portal/src/main/resources/env_config.properties b/mall-portal/src/main/resources/env_config.properties new file mode 100644 index 0000000..04680da --- /dev/null +++ b/mall-portal/src/main/resources/env_config.properties @@ -0,0 +1,22 @@ + + +#weixin login +weixin.login.appid=wx3eb36a29d84d0edd +weixin.login.appsecret=8c385ec137f6740926b1787b6898b201 + +weibo.login.appid=2645776991 +weibo.login.appsecret=785818577abc810dfac71fa7c59d1957 + +#weixin pay +pay.weixin_jsapi.appId=wx3eb36a29d84d0edd +pay.weixin_jsapi.partnerId=1480193082 +pay.weixin_jsapi.apiKey=63885ad1faf8b9ea845615e9cefd62c1 +pay.weixin_jsapi.appsecret=8c385ec137f6740926b1787b6898b201 + +pay.weixin_wap.appId=wx3eb36a29d84d0edd +pay.weixin_wap.partnerId=1480193082 +pay.weixin_wap.apiKey=63885ad1faf8b9ea845615e9cefd62c1 +pay.weixin_wap.appsecret=8c385ec137f6740926b1787b6898b201 + + + -- Gitee From 2d5c18d5e694b0969548470d41addd27a55f1807 Mon Sep 17 00:00:00 2001 From: shenzhuan Date: Mon, 25 Feb 2019 19:22:19 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E5=85=B6?= =?UTF-8?q?=E4=BB=96=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a89f740..3ba62e3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,7 @@ target/ # Eclipse # .settings/ .classpath -.project \ No newline at end of file +.project +mall-admin-web/ +mall-front-vue/ +wechatapp/ \ No newline at end of file -- Gitee