diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 028fbcf..4e6d1b5 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -58,7 +58,7 @@ public abstract class BasePayService implements Pay * 支付消息拦截器 */ protected List> interceptors = new ArrayList>(); - ; + /** * 设置支付配置 @@ -101,9 +101,15 @@ public abstract class BasePayService implements Pay public BasePayService(PC payConfigStorage, HttpConfigStorage configStorage) { setPayConfigStorage(payConfigStorage); setRequestTemplateConfigStorage(configStorage); + initAfter(); } + /** + * 初始化之后执行 + */ + protected void initAfter(){ + } /** * Generate a Base64 encoded String from user , password * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java index 4953c14..545dae8 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java @@ -1,5 +1,7 @@ package com.egzosn.pay.common.util; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -21,6 +23,9 @@ public class MapGen { } public MapGen keyValue(K key, V value) { + if (null == attr){ + attr = new LinkedHashMap<>(); + } attr.put(key, value); return this; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java index 79a94e6..9bba1a2 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java @@ -153,7 +153,7 @@ public class StringUtils { public static String joining(String separator, String... str) { StringBuilder builder = new StringBuilder(); for (String s : str) { - if (StringUtils.isEmpty(s)) { + if (null == s) { continue; } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 4d65fc2..8adc70e 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -17,12 +17,11 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.egzosn.pay.common.bean.CertStoreType; -import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.demo.request.QueryOrder; -import com.egzosn.pay.demo.service.handler.WxPayMessageHandler; +import com.egzosn.pay.demo.service.handler.WxV3PayMessageHandler; import com.egzosn.pay.web.support.HttpRequestNoticeParams; import com.egzosn.pay.wx.bean.WxBank; import com.egzosn.pay.wx.bean.WxTransferType; @@ -48,31 +47,27 @@ public class WxV3PayController { - public WxV3PayController() { - - } - - @PostConstruct + @PostConstruct //没有证书的情况下注释掉,避免启动报错 public void init() { - + System.out.println("v3 init"); WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); - wxPayConfigStorage.setAppId("wxc7b993ff15a9f271"); - wxPayConfigStorage.setMchId("1602947765"); -// wxPayConfigStorage.setKeyPublic("转账公钥,转账时必填"); + wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c"); + wxPayConfigStorage.setMchId("1602947766"); //V3密钥 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml - wxPayConfigStorage.setSecretKey("V3密钥"); - wxPayConfigStorage.setNotifyUrl("https://pay.egzosn.com/payback"); - wxPayConfigStorage.setReturnUrl("https://pay.egzosn.com/payback"); + wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f56"); + wxPayConfigStorage.setNotifyUrl("http://sailinmu.iok.la/wxV3/payBack.json"); + wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3/payBack.json"); wxPayConfigStorage.setInputCharset("utf-8"); + //使用证书时设置为true wxPayConfigStorage.setCertSign(true); //商户API证书 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml - wxPayConfigStorage.setApiClientKeyP12("商户API证书.p12"); + wxPayConfigStorage.setApiClientKeyP12("E:\\Documents\\gitee\\支付\\yifenli_mall.p12"); wxPayConfigStorage.setCertStoreType(CertStoreType.PATH); service = new WxPayService(wxPayConfigStorage); //设置回调消息处理 //TODO {@link com.egzosn.pay.demo.controller.WxPayController#payBack} - service.setPayMessageHandler(new WxPayMessageHandler(null)); + service.setPayMessageHandler(new WxV3PayMessageHandler()); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3PayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3PayMessageHandler.java new file mode 100644 index 0000000..64e9381 --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3PayMessageHandler.java @@ -0,0 +1,29 @@ +package com.egzosn.pay.demo.service.handler; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.fastjson.JSON; +import com.egzosn.pay.common.api.DefaultPayMessageHandler; +import com.egzosn.pay.common.api.PayMessageHandler; +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.wx.v3.bean.response.WxPayMessage; + +/** + * 微信支付回调处理器 + * Created by ZaoSheng on 2016/6/1. + */ +public class WxV3PayMessageHandler implements PayMessageHandler { + + private final Logger LOG = LoggerFactory.getLogger(DefaultPayMessageHandler.class); + + @Override + public PayOutMessage handle(WxPayMessage payMessage, Map context, PayService payService) throws PayErrorException { + LOG.info("回调支付消息处理器,回调消息:{}", JSON.toJSONString(payMessage)); + return payService.successPayOutMessage(payMessage); + } +} diff --git a/pay-java-demo/src/main/resources/applicationContext.xml b/pay-java-demo/src/main/resources/applicationContext.xml index 1bea38a..3352d53 100644 --- a/pay-java-demo/src/main/resources/applicationContext.xml +++ b/pay-java-demo/src/main/resources/applicationContext.xml @@ -10,7 +10,9 @@ - + + + diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index a0d1aa0..7389396 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -26,15 +26,9 @@ javax.servlet javax.servlet-api - provided 3.1.0 - - javax.servlet - jsp-api - 2.0 - provided - + diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index 4e8d7e3..a6d0e1b 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -162,7 +162,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { String associatedData = encryptCertificate.getString("associated_data"); String nonce = encryptCertificate.getString("nonce"); String ciphertext = encryptCertificate.getString("ciphertext"); - String publicKey = AntCertificationUtil.decryptToString(associatedData, nonce, ciphertext, payConfigStorage.getSecretKey(), payConfigStorage.getInputCharset()); + String publicKey = AntCertificationUtil.decryptToString(associatedData, nonce, ciphertext, payConfigStorage.getV3ApiKey(), payConfigStorage.getInputCharset()); ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8)); AntCertificationUtil.loadCertificate(certificate.getString("serial_no"), inputStream); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java index c765a91..66ccf53 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java @@ -51,6 +51,10 @@ public class WxPayConfigStorage extends BasePayConfigStorage { * 微信支付分配的子商户号,开发者模式下必填 合作者id */ private String subMchId; + /** + * V2 Api密钥 + */ + private String apiKey; /** * 商户API证书 @@ -111,19 +115,33 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setMchId(String mchId) { this.mchId = mchId; + addAttr("mchId", mchId); } /** * 为商户平台设置的密钥key * - * @return 微信密钥 + * @return 微信v2密钥 */ - public String getSecretKey() { - return getKeyPrivate(); + public String getApiKey() { + return apiKey; } - public void setSecretKey(String secretKey) { - setKeyPrivate(secretKey); + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + addAttr("apiKey", apiKey); + } + + public void setV3ApiKey(String v3ApiKey) { + setKeyPrivate(v3ApiKey); + } + /** + * 为商户平台设置的密钥key + * + * @return 微信v3密钥 + */ + public String getV3ApiKey() { + return getKeyPrivate(); } public void setAppId(String appId) { @@ -147,6 +165,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setSubAppId(String subAppId) { this.subAppId = subAppId; + addAttr("subAppId", subAppId); } public String getSpAppId() { @@ -155,6 +174,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setSpAppId(String spAppId) { this.spAppId = spAppId; + addAttr("spAppId", spAppId); } public String getSpMchId() { @@ -163,6 +183,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setSpMchId(String spMchId) { this.spMchId = spMchId; + addAttr("spMchId", spMchId); } /** @@ -180,6 +201,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { @Deprecated public void setSubAppid(String subAppid) { this.subAppId = subAppid; + addAttr("subAppId", subAppId); } @@ -189,6 +211,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setSubMchId(String subMchId) { this.subMchId = subMchId; + addAttr("subMchId", subMchId); } public Object getApiClientKeyP12() { @@ -197,6 +220,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setApiClientKeyP12(Object apiClientKeyP12) { this.apiClientKeyP12 = apiClientKeyP12; + addAttr("apiClientKeyP12", apiClientKeyP12); } public CertStoreType getCertStoreType() { @@ -205,6 +229,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setCertStoreType(CertStoreType certStoreType) { this.certStoreType = certStoreType; + addAttr("certStoreType", certStoreType); } public CertEnvironment getCertEnvironment() { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index a5bd1a3..a72958d 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -20,7 +20,6 @@ import static com.egzosn.pay.wx.api.WxConst.RETURN_CODE; import static com.egzosn.pay.wx.api.WxConst.RETURN_MSG_CODE; import static com.egzosn.pay.wx.api.WxConst.SANDBOXNEW; import static com.egzosn.pay.wx.api.WxConst.SUCCESS; -import static com.egzosn.pay.wx.v3.utils.AntCertificationUtil.getCertificate; import static com.egzosn.pay.wx.v3.utils.WxConst.FAILURE; import com.alibaba.fastjson.JSON; @@ -52,14 +51,17 @@ import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.WxPayError; -import com.egzosn.pay.wx.bean.WxPayMessage; +import com.egzosn.pay.wx.v3.bean.response.WxPayMessage; import com.egzosn.pay.wx.bean.WxTransferType; import com.egzosn.pay.wx.v3.bean.WxAccountType; import com.egzosn.pay.wx.v3.bean.WxBillType; import com.egzosn.pay.wx.v3.bean.WxTransactionType; import com.egzosn.pay.wx.v3.bean.order.Amount; import com.egzosn.pay.wx.v3.bean.order.RefundAmount; +import com.egzosn.pay.wx.v3.bean.response.Resource; +import com.egzosn.pay.wx.v3.bean.response.WxNoticeParams; import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; +import com.egzosn.pay.wx.v3.utils.AntCertificationUtil; import com.egzosn.pay.wx.v3.utils.WxConst; /** @@ -94,6 +96,8 @@ public class WxPayService extends BasePayService { public WxPayAssistService getAssistService() { if (null == wxPayAssistService) { wxPayAssistService = new DefaultWxPayAssistService(this); + //在这预先进行初始化 + wxPayAssistService.refreshCertificate(); } return wxPayAssistService; } @@ -121,17 +125,26 @@ public class WxPayService extends BasePayService { super(payConfigStorage, configStorage); } + /** - * 设置支付配置 - * - * @param payConfigStorage 支付配置 + * 初始化之后执行 */ @Override - public BasePayService setPayConfigStorage(WxPayConfigStorage payConfigStorage) { - payConfigStorage.loadCertEnvironment(); - this.payConfigStorage = payConfigStorage; - wxParameterStructure = new WxParameterStructure(payConfigStorage); - return this; + protected void initAfter() { + new Thread(() -> { + payConfigStorage.loadCertEnvironment(); + wxParameterStructure = new WxParameterStructure(payConfigStorage); + //在这预先进行初始化 + try { + Thread.sleep(10); + } + catch (InterruptedException e) { + + } + getAssistService(); + }).start(); + + } /** @@ -184,15 +197,15 @@ public class WxPayService extends BasePayService { public boolean verify(NoticeParams noticeParams) { //当前使用的微信平台证书序列号 - String serial = noticeParams.getHeader("Wechatpay-Serial"); + String serial = noticeParams.getHeader("wechatpay-serial"); //微信服务器的时间戳 - String timestamp = noticeParams.getHeader("Wechatpay-Timestamp"); + String timestamp = noticeParams.getHeader("wechatpay-timestamp"); //微信服务器提供的随机串 - String nonce = noticeParams.getHeader("Wechatpay-Nonce"); + String nonce = noticeParams.getHeader("wechatpay-nonce"); //微信平台签名 - String signature = noticeParams.getHeader("Wechatpay-Signature"); + String signature = noticeParams.getHeader("wechatpay-signature"); - Certificate certificate = getCertificate(serial); + Certificate certificate = getAssistService().getCertificate(serial); Map attr = noticeParams.getAttr(); @@ -328,7 +341,21 @@ public class WxPayService extends BasePayService { */ @Override public NoticeParams getNoticeParams(NoticeRequest request) { - NoticeParams noticeParams = new NoticeParams(); + WxNoticeParams noticeParams = null; + try (InputStream is = request.getInputStream()) { + String body = IOUtils.toString(is); + noticeParams = JSON.parseObject(body, WxNoticeParams.class); + noticeParams.setAttr(new MapGen(WxConst.RESP_BODY, body).getAttr()); + Resource resource = noticeParams.getResource(); + String associatedData = resource.getAssociatedData(); + String nonce = resource.getNonce(); + String ciphertext = resource.getCiphertext(); + String data = AntCertificationUtil.decryptToString(associatedData, nonce, ciphertext, payConfigStorage.getV3ApiKey(), payConfigStorage.getInputCharset()); + noticeParams.setBody(JSON.parseObject(data)); + } + catch (IOException e) { + LOG.error("获取回调参数异常", e); + } Map> headers = new HashMap<>(); Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { @@ -336,15 +363,7 @@ public class WxPayService extends BasePayService { headers.put(name, Collections.list(request.getHeaders(name))); } noticeParams.setHeaders(headers); - try (InputStream is = request.getInputStream()) { - String body = IOUtils.toString(is); - noticeParams.setAttr(new MapGen(WxConst.RESP_BODY, body).getAttr()); - noticeParams.setBody(JSON.parseObject(body)); - } - catch (IOException e) { - LOG.error("获取回调参数异常", e); - } - return super.getNoticeParams(request); + return noticeParams; } /** @@ -356,7 +375,7 @@ public class WxPayService extends BasePayService { */ @Override public PayOutMessage getPayOutMessage(String code, String message) { - return PayOutMessage.XML().code(code.toUpperCase()).content(message).build(); + return PayOutMessage.JSON().content("code", code).content("message", message).build(); } @@ -369,7 +388,7 @@ public class WxPayService extends BasePayService { */ @Override public PayOutMessage successPayOutMessage(PayMessage payMessage) { - return PayOutMessage.XML().code("SUCCESS").content("成功").build(); + return getPayOutMessage("SUCCESS", "成功"); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java index 7c94876..70a6ee8 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java @@ -73,6 +73,18 @@ public enum WxTransactionType implements TransactionType { } } + }, + /** + * H5支付 + * 兼容 后期会抛弃 + */ + @Deprecated + MWEB("pay{partner}/transactions/h5", MethodType.POST, true) { + @Override + public void setAttribute(Map parameters, PayOrder order) { + H5.setAttribute(parameters, order); + } + }, /** diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java index 055cf35..952a3c9 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java @@ -3,9 +3,12 @@ package com.egzosn.pay.wx.v3.bean.order; import com.alibaba.fastjson.annotation.JSONField; /** + * 支付场景信息描述 * @author Egan + *
  * email egzosn@gmail.com
  * date 2021/8/1
+ * 
*/ public class SceneInfo { @@ -15,7 +18,7 @@ public class SceneInfo { @JSONField(name = "payer_client_ip") private String payerClientIp; /** - * 商户端设备号 + * 商户端设备号(门店号或收银设备ID)。 */ @JSONField(name = "device_id") private String deviceId; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/Resource.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/Resource.java new file mode 100644 index 0000000..a6910de --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/Resource.java @@ -0,0 +1,80 @@ +package com.egzosn.pay.wx.v3.bean.response; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 通知资源数据 + * json格式,见示例 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class Resource { + + + /** + * 对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM。 + */ + private String algorithm; + /** + * Base64编码后的开启/停用结果数据密文。 + */ + private String ciphertext; + /** + * 附加数据。 + */ + @JSONField(name = "associated_data") + private String associatedData; + + /** + * 原始回调类型。 + */ + @JSONField(name = "original_type") + private String originalType; + /** + * 加密使用的随机串。 + */ + private String nonce; + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getCiphertext() { + return ciphertext; + } + + public void setCiphertext(String ciphertext) { + this.ciphertext = ciphertext; + } + + public String getAssociatedData() { + return associatedData; + } + + public void setAssociatedData(String associatedData) { + this.associatedData = associatedData; + } + + public String getOriginalType() { + return originalType; + } + + public void setOriginalType(String originalType) { + this.originalType = originalType; + } + + public String getNonce() { + return nonce; + } + + public void setNonce(String nonce) { + this.nonce = nonce; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxNoticeParams.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxNoticeParams.java new file mode 100644 index 0000000..9c7ab19 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxNoticeParams.java @@ -0,0 +1,108 @@ +package com.egzosn.pay.wx.v3.bean.response; + +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.NoticeParams; + +/** + * 微信通知参数 + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/10/4
+ *
+ */ +public class WxNoticeParams extends NoticeParams { + + + /** + * 通知的唯一ID + * 示例值:EV-2018022511223320873 + */ + private String id; + + /** + *通知创建的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + */ + @JSONField(name = "create_time") + private String createTime; + /** + * 通知的类型: + * TRANSACTION.SUCCESS 支付成功通知 + * REFUND.SUCCESS:退款成功通知 + * REFUND.ABNORMAL:退款异常通知 + * REFUND.CLOSED:退款关闭通知 + * 示例值:REFUND.SUCCESS + */ + @JSONField(name = "event_type") + private String eventType; + + + + /** + * 通知的资源数据类型,支付成功通知为encrypt-resource + * 示例值:encrypt-resource + */ + @JSONField(name = "resource_type") + private String resourceType; + + /** + * 通知资源数据 + * json格式,见示例 + */ + private Resource resource; + /** + * 通知简要说明 + * 示例值:退款成功 + * 示例值:支付成功 + */ + private String summary; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getResourceType() { + return resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public Resource getResource() { + return resource; + } + + public void setResource(Resource resource) { + this.resource = resource; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java new file mode 100644 index 0000000..8aa4008 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java @@ -0,0 +1,311 @@ +package com.egzosn.pay.wx.v3.bean.response; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.bean.order.SceneInfo; +import com.egzosn.pay.wx.v3.bean.response.order.Amount; +import com.egzosn.pay.wx.v3.bean.response.order.Payer; +import com.egzosn.pay.wx.v3.bean.response.order.PromotionDetail; +import com.egzosn.pay.wx.v3.bean.response.order.TradeState; + +/** + * 支付回调消息 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class WxPayMessage extends PayMessage { + + + + + /** + * 直连模式应用ID,服务商模式请解析spAppid + */ + private String appid; + /** + * 直连模式商户号,服务商模式请解析spMchid + */ + private String mchid; + /** + * 服务商模式服务商APPID + */ + @JSONField(name = "sp_appid") + private String spAppid; + /** + * 服务商模式服务商户号 + */ + @JSONField(name = "sp_mchid") + private String spMchid; + /** + * 服务商模式-子商户appid + */ + @JSONField(name = "sub_appid") + private String subAppid; + /** + * 服务商模式-子商户商户id + */ + @JSONField(name = "sub_mchid") + private String subMchid; + + /** + * 商户订单号 + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。 + * 示例值:1217752501201407033233368018 + */ + @JSONField(name = "out_trade_no") + private String outTradeNo; + + /** + * 微信支付订单号 + * 微信支付系统生成的订单号。 + * 示例值:1217752501201407033233368018 + */ + @JSONField(name = "transaction_id") + private String transactionId; + + /** + * 交易类型,枚举值: + * JSAPI:公众号支付 + * NATIVE:扫码支付 + * APP:APP支付 + * MICROPAY:付款码支付 + * MWEB:H5支付 + * FACEPAY:刷脸支付 + * 示例值:MICROPAY + */ + @JSONField(name = "trade_type") + private WxTransactionType tradeType; + + + /** + * 交易状态,枚举值: + * SUCCESS:支付成功 + * REFUND:转入退款 + * NOTPAY:未支付 + * CLOSED:已关闭 + * REVOKED:已撤销(付款码支付) + * USERPAYING:用户支付中(付款码支付) + * PAYERROR:支付失败(其他原因,如银行返回失败) + */ + @JSONField(name = "trade_state") + private TradeState tradeState; + + + /** + * 交易状态描述 + * 示例值:支付成功 + */ + @JSONField(name = "trade_state_desc") + private String tradeStateDesc; + /** + * 银行类型,采用字符串类型的银行标识。 + * 银行标识请参考 《银行类型对照表》 + * 示例值:CMC + */ + @JSONField(name = "bank_type") + private String bankType; + + /** + * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 + */ + private String attach; + + /** + * 支付完成时间,遵循rfc3339标准格式, + * 格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒, + * TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。 + * 例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + */ + @JSONField(name = "success_time", format = "yyyy-MM-dd'T'HH:mm:ssXXX") + private Date successTime; + /** + * 支付者信息 + */ + private Payer payer; + + + /** + * 订单金额 + */ + private Amount amount; + /** + * 支付场景信息描述 + */ + @JSONField(name = "scene_info") + private SceneInfo sceneInfo; + + /** + * 优惠功能,享受优惠时返回该字段。 + */ + @JSONField(name = "promotion_detail") + private List promotionDetail; + + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMchid() { + return mchid; + } + + public void setMchid(String mchid) { + this.mchid = mchid; + } + + public String getSpAppid() { + return spAppid; + } + + public void setSpAppid(String spAppid) { + this.spAppid = spAppid; + } + + public String getSpMchid() { + return spMchid; + } + + public void setSpMchid(String spMchid) { + this.spMchid = spMchid; + } + + public String getSubAppid() { + return subAppid; + } + + public void setSubAppid(String subAppid) { + this.subAppid = subAppid; + } + + public String getSubMchid() { + return subMchid; + } + + public void setSubMchid(String subMchid) { + this.subMchid = subMchid; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public WxTransactionType getTradeType() { + return tradeType; + } + + public void setTradeType(WxTransactionType tradeType) { + this.tradeType = tradeType; + } + + public TradeState getTradeState() { + return tradeState; + } + + public void setTradeState(TradeState tradeState) { + this.tradeState = tradeState; + } + + public String getTradeStateDesc() { + return tradeStateDesc; + } + + public void setTradeStateDesc(String tradeStateDesc) { + this.tradeStateDesc = tradeStateDesc; + } + + public String getBankType() { + return bankType; + } + + public void setBankType(String bankType) { + this.bankType = bankType; + } + + public String getAttach() { + return attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public Date getSuccessTime() { + return successTime; + } + + public void setSuccessTime(Date successTime) { + this.successTime = successTime; + } + + public Payer getPayer() { + return payer; + } + + public void setPayer(Payer payer) { + this.payer = payer; + } + + public Amount getAmount() { + return amount; + } + + public void setAmount(Amount amount) { + this.amount = amount; + } + + public SceneInfo getSceneInfo() { + return sceneInfo; + } + + public void setSceneInfo(SceneInfo sceneInfo) { + this.sceneInfo = sceneInfo; + } + + public List getPromotionDetail() { + return promotionDetail; + } + + public void setPromotionDetail(List promotionDetail) { + this.promotionDetail = promotionDetail; + } + + @Override + public BigDecimal getTotalFee() { + return BigDecimal.valueOf(getAmount().getTotal()); + } + + + public static final WxPayMessage create(Map message) { + WxPayMessage payMessage = new JSONObject(message).toJavaObject(WxPayMessage.class); +// payMessage.setPayType(""); + payMessage.setPayMessage(message); + return payMessage; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java new file mode 100644 index 0000000..c6cf950 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java @@ -0,0 +1,39 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +import java.math.BigDecimal; + +/** + * 回调中的订单金额信息 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class Amount extends com.egzosn.pay.wx.v3.bean.order.Amount{ + + /** + * 用户支付金额,单位为分。 + */ + private BigDecimal payerTotal; + /** + * 用户支付币种 + */ + private String payerCurrency; + + public BigDecimal getPayerTotal() { + return payerTotal; + } + + public void setPayerTotal(BigDecimal payerTotal) { + this.payerTotal = payerTotal; + } + + public String getPayerCurrency() { + return payerCurrency; + } + + public void setPayerCurrency(String payerCurrency) { + this.payerCurrency = payerCurrency; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/GoodsDetail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/GoodsDetail.java new file mode 100644 index 0000000..d48675c --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/GoodsDetail.java @@ -0,0 +1,81 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 单品列表信息 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class GoodsDetail { + + /** + * 商品编码 + */ + @JSONField(name = "goods_id") + private String goodsId; + /** + * 商品数量 + */ + @JSONField(name = "quantity") + private Long quantity; + /** + * 商品单价 + */ + @JSONField(name = "unit_price") + private Long unitPrice; + /** + * 商品优惠金额,单位【分】 + */ + @JSONField(name = "discount_amount") + private Long discountAmount; + /** + * 商品备注 + */ + @JSONField(name = "goods_remark") + private String goodsRemark; + + + public String getGoodsId() { + return goodsId; + } + + public void setGoodsId(String goodsId) { + this.goodsId = goodsId; + } + + public Long getQuantity() { + return quantity; + } + + public void setQuantity(Long quantity) { + this.quantity = quantity; + } + + public Long getUnitPrice() { + return unitPrice; + } + + public void setUnitPrice(Long unitPrice) { + this.unitPrice = unitPrice; + } + + public Long getDiscountAmount() { + return discountAmount; + } + + public void setDiscountAmount(Long discountAmount) { + this.discountAmount = discountAmount; + } + + public String getGoodsRemark() { + return goodsRemark; + } + + public void setGoodsRemark(String goodsRemark) { + this.goodsRemark = goodsRemark; + } +} \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java new file mode 100644 index 0000000..623b97d --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java @@ -0,0 +1,24 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +/** + * 支付者信息 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class Payer { + /** + * 用户在直连商户appid下的唯一标识。 + */ + private String openid; + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } +} \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java new file mode 100644 index 0000000..697355e --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java @@ -0,0 +1,176 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +import java.util.List; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 优惠功能 + * + * @author felord.cn + * @since 1.0.0.RELEASE + */ + +public class PromotionDetail { + /** + * 券ID + */ + @JSONField(name = "coupon_id") + private String couponId; + + /** + * 优惠名称 + */ + private String name; + + /** + * 优惠范围 + *
    + *
  • GLOBAL:全场代金券
  • + *
  • SINGLE:单品优惠
  • + *
+ * 示例值:GLOBAL + */ + private String scope; + + /** + * 优惠类型 + *
    + *
  • CASH:充值
  • + *
  • NOCASH:预充值
  • + *
+ * 示例值:CASH + */ + private String type; + /** + * 优惠券面额,单位【分】 + */ + private Long amount; + + + + /** + * 活动ID + */ + @JSONField(name = "stock_id") + private String stockId; + + /** + * 微信出资,单位为分 + */ + @JSONField(name = "wechatpay_contribute") + private Long wechatpayContribute; + + /** + * 商户出资,单位为分 + */ + @JSONField(name = "merchant_contribute") + private Long merchantContribute; + + /** + * 其他出资,单位为分 + */ + @JSONField(name = "other_contribute") + private Long otherContribute; + + /** + * 优惠币种, + * CNY:人民币,境内商户号仅支持人民币。 + * 示例值:CNY + */ + private String currency; + /** + * 单品列表信息 + */ + @JSONField(name = "goods_detail") + private List goodsDetail; + + + public String getCouponId() { + return couponId; + } + + public void setCouponId(String couponId) { + this.couponId = couponId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Long getAmount() { + return amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } + + public String getStockId() { + return stockId; + } + + public void setStockId(String stockId) { + this.stockId = stockId; + } + + public Long getWechatpayContribute() { + return wechatpayContribute; + } + + public void setWechatpayContribute(Long wechatpayContribute) { + this.wechatpayContribute = wechatpayContribute; + } + + public Long getMerchantContribute() { + return merchantContribute; + } + + public void setMerchantContribute(Long merchantContribute) { + this.merchantContribute = merchantContribute; + } + + public Long getOtherContribute() { + return otherContribute; + } + + public void setOtherContribute(Long otherContribute) { + this.otherContribute = otherContribute; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public List getGoodsDetail() { + return goodsDetail; + } + + public void setGoodsDetail(List goodsDetail) { + this.goodsDetail = goodsDetail; + } +} \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java new file mode 100644 index 0000000..6b6afe6 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java @@ -0,0 +1,54 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +/** + * 微信侧返回交易状态 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public enum TradeState { + /** + * 支付成功 + * + * + */ + SUCCESS, + /** + * 转入退款 + * + * + */ + REFUND, + /** + * 未支付 + * + * + */ + NOTPAY, + /** + * 已关闭 + * + * + */ + CLOSED, + /** + * 已撤销(付款码支付) + * + */ + REVOKED, + /** + * 用户支付中(付款码支付) + */ + USERPAYING, + /** + * 支付失败(其他原因,如银行返回失败) + */ + PAYERROR, + /** + * 已接收,等待扣款 + * + */ + ACCEPT, +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 44f98e1..a1d07ce 100644 --- a/pom.xml +++ b/pom.xml @@ -147,8 +147,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.7 - 1.7 + 1.8 + 1.8 utf-8