微信V3版本接入直连接口,自测通过

This commit is contained in:
egzosn
2021-10-04 18:45:50 +08:00
parent b731154e68
commit 1b10bbd787
21 changed files with 1025 additions and 62 deletions

View File

@@ -58,7 +58,7 @@ public abstract class BasePayService<PC extends PayConfigStorage> implements Pay
* 支付消息拦截器
*/
protected List<PayMessageInterceptor<PayMessage, PayService>> interceptors = new ArrayList<PayMessageInterceptor<PayMessage, PayService>>();
;
/**
* 设置支付配置
@@ -101,9 +101,15 @@ public abstract class BasePayService<PC extends PayConfigStorage> implements Pay
public BasePayService(PC payConfigStorage, HttpConfigStorage configStorage) {
setPayConfigStorage(payConfigStorage);
setRequestTemplateConfigStorage(configStorage);
initAfter();
}
/**
* 初始化之后执行
*/
protected void initAfter(){
}
/**
* Generate a Base64 encoded String from user , password
*

View File

@@ -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<K, V> {
}
public MapGen<K, V> keyValue(K key, V value) {
if (null == attr){
attr = new LinkedHashMap<>();
}
attr.put(key, value);
return this;
}

View File

@@ -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;
}

View File

@@ -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());
}

View File

@@ -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<WxPayMessage, PayService> {
private final Logger LOG = LoggerFactory.getLogger(DefaultPayMessageHandler.class);
@Override
public PayOutMessage handle(WxPayMessage payMessage, Map<String, Object> context, PayService payService) throws PayErrorException {
LOG.info("回调支付消息处理器,回调消息:{}", JSON.toJSONString(payMessage));
return payService.successPayOutMessage(payMessage);
}
}

View File

@@ -10,7 +10,9 @@
<context:annotation-config />
<context:component-scan base-package="com.egzosn.pay.demo" />
<context:component-scan base-package="com.egzosn.pay.demo">
<context:exclude-filter type="regex" expression="com.egzosn.pay.demo.controller.*"/>
</context:component-scan>

View File

@@ -26,15 +26,9 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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<WxPayConfigStorage> {
public WxPayAssistService getAssistService() {
if (null == wxPayAssistService) {
wxPayAssistService = new DefaultWxPayAssistService(this);
//在这预先进行初始化
wxPayAssistService.refreshCertificate();
}
return wxPayAssistService;
}
@@ -121,17 +125,26 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
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<WxPayConfigStorage> {
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<String, Object> attr = noticeParams.getAttr();
@@ -328,7 +341,21 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
*/
@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<String, Object>(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<String, List<String>> headers = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
@@ -336,15 +363,7 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
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<String, Object>(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<WxPayConfigStorage> {
*/
@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<WxPayConfigStorage> {
*/
@Override
public PayOutMessage successPayOutMessage(PayMessage payMessage) {
return PayOutMessage.XML().code("SUCCESS").content("成功").build();
return getPayOutMessage("SUCCESS", "成功");
}

View File

@@ -73,6 +73,18 @@ public enum WxTransactionType implements TransactionType {
}
}
},
/**
* H5支付
* 兼容 后期会抛弃
*/
@Deprecated
MWEB("pay{partner}/transactions/h5", MethodType.POST, true) {
@Override
public void setAttribute(Map<String, Object> parameters, PayOrder order) {
H5.setAttribute(parameters, order);
}
},
/**

View File

@@ -3,9 +3,12 @@ package com.egzosn.pay.wx.v3.bean.order;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 支付场景信息描述
* @author Egan
* <pre>
* email egzosn@gmail.com
* date 2021/8/1
* </pre>
*/
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;

View File

@@ -0,0 +1,80 @@
package com.egzosn.pay.wx.v3.bean.response;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 通知资源数据
* json格式见示例
* @author Egan
* <pre>
* email egan@egzosn.com
* date 2021/10/4
* </pre>
*/
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;
}
}

View File

@@ -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
* <pre>
* email egzosn@gmail.com
* date 2021/10/4
*</pre>
*/
public class WxNoticeParams extends NoticeParams {
/**
* 通知的唯一ID
* 示例值EV-2018022511223320873
*/
private String id;
/**
*通知创建的时间遵循rfc3339标准格式格式为YYYY-MM-DDTHH:mm:ss+TIMEZONEYYYY-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;
}
}

View File

@@ -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
* <pre>
* email egan@egzosn.com
* date 2021/10/4
* </pre>
*/
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扫码支付
* APPAPP支付
* MICROPAY付款码支付
* MWEBH5支付
* 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;
/**
* 银行类型,采用字符串类型的银行标识。
* 银行标识请参考 <a target= "_blank" href= "https://pay.weixin.qq.com/wiki/doc/apiv3/terms_definition/chapter1_1_3.shtml#part-6">《银行类型对照表》</a>
* 示例值CMC
*/
@JSONField(name = "bank_type")
private String bankType;
/**
* 附加数据在查询API和支付通知中原样返回可作为自定义参数使用
*/
private String attach;
/**
* 支付完成时间遵循rfc3339标准格式
* 格式为YYYY-MM-DDTHH:mm:ss+TIMEZONEYYYY-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> 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<PromotionDetail> getPromotionDetail() {
return promotionDetail;
}
public void setPromotionDetail(List<PromotionDetail> promotionDetail) {
this.promotionDetail = promotionDetail;
}
@Override
public BigDecimal getTotalFee() {
return BigDecimal.valueOf(getAmount().getTotal());
}
public static final WxPayMessage create(Map<String, Object> message) {
WxPayMessage payMessage = new JSONObject(message).toJavaObject(WxPayMessage.class);
// payMessage.setPayType("");
payMessage.setPayMessage(message);
return payMessage;
}
}

View File

@@ -0,0 +1,39 @@
package com.egzosn.pay.wx.v3.bean.response.order;
import java.math.BigDecimal;
/**
* 回调中的订单金额信息
* @author Egan
* <pre>
* email egan@egzosn.com
* date 2021/10/4
* </pre>
*/
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;
}
}

View File

@@ -0,0 +1,81 @@
package com.egzosn.pay.wx.v3.bean.response.order;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 单品列表信息
* @author Egan
* <pre>
* email egan@egzosn.com
* date 2021/10/4
* </pre>
*/
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;
}
}

View File

@@ -0,0 +1,24 @@
package com.egzosn.pay.wx.v3.bean.response.order;
/**
* 支付者信息
* @author Egan
* <pre>
* email egan@egzosn.com
* date 2021/10/4
* </pre>
*/
public class Payer {
/**
* 用户在直连商户appid下的唯一标识。
*/
private String openid;
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
}

View File

@@ -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;
/**
* 优惠范围
* <ul>
* <li> GLOBAL全场代金券</li>
* <li>SINGLE单品优惠</li>
* </ul>
* 示例值GLOBAL
*/
private String scope;
/**
* 优惠类型
* <ul>
* <li>CASH充值</li>
* <li>NOCASH预充值</li>
* </ul>
* 示例值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> 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<GoodsDetail> getGoodsDetail() {
return goodsDetail;
}
public void setGoodsDetail(List<GoodsDetail> goodsDetail) {
this.goodsDetail = goodsDetail;
}
}

View File

@@ -0,0 +1,54 @@
package com.egzosn.pay.wx.v3.bean.response.order;
/**
* 微信侧返回交易状态
* @author Egan
* <pre>
* email egan@egzosn.com
* date 2021/10/4
* </pre>
*/
public enum TradeState {
/**
* 支付成功
*
*
*/
SUCCESS,
/**
* 转入退款
*
*
*/
REFUND,
/**
* 未支付
*
*
*/
NOTPAY,
/**
* 已关闭
*
*
*/
CLOSED,
/**
* 已撤销(付款码支付)
*
*/
REVOKED,
/**
* 用户支付中(付款码支付)
*/
USERPAYING,
/**
* 支付失败(其他原因,如银行返回失败)
*/
PAYERROR,
/**
* 已接收,等待扣款
*
*/
ACCEPT,
}

View File

@@ -147,8 +147,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>