✨ 完成微信转账以及demo实例
This commit is contained in:
29
ballcat-extends/ballcat-extend-pay-wx/pom.xml
Normal file
29
ballcat-extends/ballcat-extend-pay-wx/pom.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ballcat-extends</artifactId>
|
||||
<groupId>com.hccake</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ballcat-extend-pay-wx</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.hccake</groupId>
|
||||
<artifactId>ballcat-common-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,229 @@
|
||||
package com.hccake.starte.pay.wx;
|
||||
|
||||
import static com.hccake.starte.pay.wx.constants.WxPayConstant.HUNDRED;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.hccake.starte.pay.wx.constants.WxPayConstant;
|
||||
import com.hccake.starte.pay.wx.domain.DefaultWxDomain;
|
||||
import com.hccake.starte.pay.wx.domain.WxDomain;
|
||||
import com.hccake.starte.pay.wx.enums.RequestSuffix;
|
||||
import com.hccake.starte.pay.wx.enums.SignType;
|
||||
import com.hccake.starte.pay.wx.enums.TradeType;
|
||||
import com.hccake.starte.pay.wx.response.WxPayCallback;
|
||||
import com.hccake.starte.pay.wx.response.WxPayOrderQueryResponse;
|
||||
import com.hccake.starte.pay.wx.response.WxPayResponse;
|
||||
import com.hccake.starte.pay.wx.utils.WxPayUtil;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/1/26 15:54
|
||||
*/
|
||||
@Data
|
||||
public class WxPay {
|
||||
|
||||
private final String appId;
|
||||
|
||||
private final String mchId;
|
||||
|
||||
private final String mckKey;
|
||||
|
||||
private String notifyUrl;
|
||||
|
||||
private String returnUrl;
|
||||
|
||||
/**
|
||||
* 是否使用沙箱
|
||||
*/
|
||||
private boolean sandbox;
|
||||
|
||||
/**
|
||||
* 域名策略, 可是使用自定义实现
|
||||
*/
|
||||
private WxDomain domain;
|
||||
|
||||
public WxPay(String appId, String mchId, String mckKey, boolean sandbox) {
|
||||
this(appId, mchId, mckKey, sandbox, DefaultWxDomain.of(sandbox));
|
||||
}
|
||||
|
||||
public WxPay(String appId, String mchId, String mckKey, boolean sandbox, WxDomain domain) {
|
||||
this.sandbox = sandbox;
|
||||
this.domain = domain;
|
||||
this.mchId = mchId;
|
||||
this.appId = appId;
|
||||
|
||||
// 沙箱环境初始化
|
||||
if (sandbox) {
|
||||
this.mckKey = domain.sandbox(this).getSandboxSignKey();
|
||||
}
|
||||
else {
|
||||
this.mckKey = mckKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* jsApi 支付
|
||||
* @param sn 订单号
|
||||
* @param amount 支付金额, 单位 元
|
||||
* @param ip 客户端ip
|
||||
* @param body 商品描述
|
||||
*/
|
||||
public WxPayResponse jsApiPay(String sn, BigDecimal amount, String ip, String body) {
|
||||
return jsApiPay(sn, amount, ip, body, notifyUrl);
|
||||
}
|
||||
|
||||
public WxPayResponse jsApiPay(String sn, BigDecimal amount, String ip, String body, String notifyUrl) {
|
||||
return WxPayResponse.of(pay(sn, amount, ip, body, notifyUrl, TradeType.JSAPI));
|
||||
}
|
||||
|
||||
/**
|
||||
* app 支付
|
||||
* @param sn 订单号
|
||||
* @param amount 支付金额, 单位 元
|
||||
* @param ip 客户端ip
|
||||
* @param body 商品描述
|
||||
*/
|
||||
public WxPayResponse appPay(String sn, BigDecimal amount, String ip, String body) {
|
||||
return appPay(sn, amount, ip, body, notifyUrl);
|
||||
}
|
||||
|
||||
public WxPayResponse appPay(String sn, BigDecimal amount, String ip, String body, String notifyUrl) {
|
||||
return WxPayResponse.of(pay(sn, amount, ip, body, notifyUrl, TradeType.APP));
|
||||
}
|
||||
|
||||
/**
|
||||
* native 支付
|
||||
* @param sn 订单号
|
||||
* @param amount 支付金额, 单位 元
|
||||
* @param ip 客户端ip
|
||||
* @param body 商品描述
|
||||
*/
|
||||
public WxPayResponse nativePay(String sn, BigDecimal amount, String ip, String body) {
|
||||
return nativePay(sn, amount, ip, body, notifyUrl);
|
||||
}
|
||||
|
||||
public WxPayResponse nativePay(String sn, BigDecimal amount, String ip, String body, String notifyUrl) {
|
||||
return WxPayResponse.of(pay(sn, amount, ip, body, notifyUrl, TradeType.NATIVE));
|
||||
}
|
||||
|
||||
/**
|
||||
* web 支付
|
||||
* @param sn 订单号
|
||||
* @param amount 支付金额, 单位 元
|
||||
* @param ip 客户端ip
|
||||
* @param body 商品描述
|
||||
*/
|
||||
public WxPayResponse webPay(String sn, BigDecimal amount, String ip, String body) {
|
||||
return webPay(sn, amount, ip, body, notifyUrl);
|
||||
}
|
||||
|
||||
public WxPayResponse webPay(String sn, BigDecimal amount, String ip, String body, String notifyUrl) {
|
||||
return WxPayResponse.of(pay(sn, amount, ip, body, notifyUrl, TradeType.MWEB));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起支付
|
||||
* @param sn 订单号
|
||||
* @param amount 金额, 单位 元
|
||||
* @param ip ip
|
||||
* @param body 描述
|
||||
* @param notifyUrl 通知
|
||||
* @param tradeType 支付类型
|
||||
* @author lingting 2021-02-25 10:19
|
||||
*/
|
||||
public Map<String, String> pay(String sn, BigDecimal amount, String ip, String body, String notifyUrl,
|
||||
TradeType tradeType) {
|
||||
Map<String, String> params = new HashMap<>(6);
|
||||
params.put("body", body);
|
||||
params.put("out_trade_no", sn);
|
||||
params.put("total_fee", yuanToFen(amount));
|
||||
params.put("spbill_create_ip", ip);
|
||||
params.put("notify_url", notifyUrl);
|
||||
params.put("trade_type", tradeType.toString());
|
||||
|
||||
return request(params, RequestSuffix.UNIFIEDORDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单
|
||||
* @param sn 平台订单号
|
||||
* @param wxSn 微信订单号
|
||||
* @return com.hccake.starte.pay.wx.response.WxPayOrderQueryResponse
|
||||
* @author lingting 2021-02-25 15:20
|
||||
*/
|
||||
public WxPayOrderQueryResponse query(String sn, String wxSn) {
|
||||
Assert.isFalse(StrUtil.isBlank(sn) && StrUtil.isBlank(wxSn), "参数 sn 和 wxSn 不能同时为空!");
|
||||
Map<String, String> params = new HashMap<>(6);
|
||||
params.put("out_trade_no", sn);
|
||||
params.put("transaction_id", wxSn);
|
||||
return WxPayOrderQueryResponse.of(request(params, RequestSuffix.ORDERQUERY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 向微信发起请求
|
||||
* @param params 参数
|
||||
* @param rs 请求后缀
|
||||
* @author lingting 2021-01-29 18:12
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, String> request(Map<String, String> params, RequestSuffix rs) {
|
||||
Map<String, String> map = new HashMap<>(params.size() + 3);
|
||||
map.putAll(params);
|
||||
|
||||
// 添加必须参数
|
||||
map.put("appid", appId);
|
||||
map.put("mch_id", mchId);
|
||||
map.put("nonce_str", WxPayUtil.generateNonceStr());
|
||||
// 设置签名类型; 沙箱使用 md5, 正式使用 hmac sha256
|
||||
map.put(WxPayConstant.FIELD_SIGN_TYPE, sandbox ? SignType.MD5.getStr() : SignType.HMAC_SHA256.getStr());
|
||||
// 签名
|
||||
map.put(WxPayConstant.FIELD_SIGN, WxPayUtil.sign(map, mckKey));
|
||||
|
||||
return domain.request(map, rs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额单位转换, 元 转为 分
|
||||
* @param amount 支付金额, 单位 元
|
||||
* @return java.lang.String
|
||||
* @author lingting 2021-01-25 10:27
|
||||
*/
|
||||
public String yuanToFen(BigDecimal amount) {
|
||||
return amount.multiply(HUNDRED).setScale(2, RoundingMode.UP).toBigInteger().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证回调签名
|
||||
* @param callback 回调数据
|
||||
* @return java.lang.Boolean
|
||||
* @author lingting 2021-02-25 16:01
|
||||
*/
|
||||
public boolean checkSign(WxPayCallback callback) {
|
||||
Map<String, String> params = new HashMap<>(callback.getRaw().size());
|
||||
params.putAll(callback.getRaw());
|
||||
|
||||
// 存在签名类型, 直接验签
|
||||
if (params.containsKey(WxPayConstant.FIELD_SIGN_TYPE)) {
|
||||
return WxPayUtil.sign(params, mckKey).equals(callback.getSign());
|
||||
}
|
||||
|
||||
// 两种签名类型都试一次
|
||||
params.put(WxPayConstant.FIELD_SIGN_TYPE, SignType.HMAC_SHA256.getStr());
|
||||
if (WxPayUtil.sign(params, mckKey).equals(callback.getSign())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
params.put(WxPayConstant.FIELD_SIGN_TYPE, SignType.MD5.getStr());
|
||||
if (WxPayUtil.sign(params, mckKey).equals(callback.getSign())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.hccake.starte.pay.wx.constants;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/1/26 16:15
|
||||
*/
|
||||
public class WxPayConstant {
|
||||
|
||||
/**
|
||||
* 一百
|
||||
*/
|
||||
public static final BigDecimal HUNDRED = new BigDecimal("100");
|
||||
|
||||
/**
|
||||
* 签名字段名
|
||||
*/
|
||||
public static final String FIELD_SIGN = "sign";
|
||||
|
||||
/**
|
||||
* 签名类型字段名
|
||||
*/
|
||||
public static final String FIELD_SIGN_TYPE = "sign_type";
|
||||
|
||||
/**
|
||||
* 回调成功返回值
|
||||
*/
|
||||
public static String CALLBACK_SUCCESS = "<xml>\n" + " <return_code><![CDATA[SUCCESS]]></return_code>\n"
|
||||
+ " <return_msg><![CDATA[OK]]></return_msg>\n" + "</xml>";
|
||||
|
||||
/**
|
||||
* 回调验签失败返回值
|
||||
*/
|
||||
public static String CALLBACK_SIGN_ERROR = "<xml>\n" + " <return_code><![CDATA[FAIL]]></return_code>\n"
|
||||
+ " <return_msg><![CDATA[签名异常]]></return_msg>\n" + "</xml>";
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.hccake.starte.pay.wx.domain;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import com.hccake.starte.pay.wx.enums.RequestSuffix;
|
||||
import com.hccake.starte.pay.wx.utils.WxPayUtil;
|
||||
import java.util.Map;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
* 微信域名管理
|
||||
*
|
||||
* @author lingting 2021/1/26 16:05
|
||||
*/
|
||||
public class DefaultWxDomain implements WxDomain {
|
||||
|
||||
private static final String FLAG = "/";
|
||||
|
||||
/**
|
||||
* 是否使用沙箱
|
||||
*/
|
||||
private final boolean sandbox;
|
||||
|
||||
private DefaultWxDomain(boolean sandbox) {
|
||||
this.sandbox = sandbox;
|
||||
}
|
||||
|
||||
public static DefaultWxDomain of(boolean sandbox) {
|
||||
return new DefaultWxDomain(sandbox);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String sendRequest(Map<String, String> params, RequestSuffix rs) {
|
||||
// 获取请求地址
|
||||
String url = getUrl(rs.getSuffix());
|
||||
HttpRequest post = HttpRequest.post(url).header("Content-Type", "text/xml").body(WxPayUtil.mapToXml(params));
|
||||
return post.execute().body();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据微信的建议, 这里后续需要加上主备切换的功能
|
||||
* @return java.lang.String
|
||||
* @author lingting 2021-01-29 17:50
|
||||
*/
|
||||
public String getDomain() {
|
||||
return MAIN1;
|
||||
}
|
||||
|
||||
public String getUrl(String suffix) {
|
||||
if (suffix.startsWith(FLAG)) {
|
||||
suffix = suffix.substring(1);
|
||||
}
|
||||
|
||||
if (sandbox) {
|
||||
return getDomain() + "sandboxnew/pay/" + suffix;
|
||||
}
|
||||
return getDomain() + "pay/" + suffix;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.hccake.starte.pay.wx.domain;
|
||||
|
||||
import com.hccake.starte.pay.wx.WxPay;
|
||||
import com.hccake.starte.pay.wx.constants.WxPayConstant;
|
||||
import com.hccake.starte.pay.wx.enums.RequestSuffix;
|
||||
import com.hccake.starte.pay.wx.enums.SignType;
|
||||
import com.hccake.starte.pay.wx.response.WxPayResponse;
|
||||
import com.hccake.starte.pay.wx.utils.WxPayUtil;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.SneakyThrows;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/2/1 10:57
|
||||
*/
|
||||
public interface WxDomain {
|
||||
|
||||
/**
|
||||
* 主域名
|
||||
*/
|
||||
String MAIN1 = "https://api.mch.weixin.qq.com/";
|
||||
|
||||
String MAIN2 = "https://api.weixin.qq.com/";
|
||||
|
||||
/**
|
||||
* 备用域名
|
||||
*/
|
||||
String BACKUP1 = "https://api2.mch.weixin.qq.com/";
|
||||
|
||||
String BACKUP2 = "https://api2.weixin.qq.com/";
|
||||
|
||||
/**
|
||||
* 发起请求. 根据微信建议,实现类最好拥有主备域名自动切换的功能
|
||||
* @param params 参数
|
||||
* @param rs 请求后缀
|
||||
* @return java.util.Map<java.lang.String,java.lang.String>
|
||||
* @author lingting 2021-02-01 10:58
|
||||
*/
|
||||
@SneakyThrows
|
||||
default Map<String, String> request(Map<String, String> params, RequestSuffix rs) {
|
||||
String res = "";
|
||||
try {
|
||||
res = sendRequest(params, rs);
|
||||
return WxPayUtil.xmlToMap(res);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// 用于处理返回值异常情况
|
||||
LoggerFactory.getLogger(getClass()).error("微信支付请求失败!返回值:\n {}", res);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
* @param params 参数
|
||||
* @param rs 前缀
|
||||
* @return java.lang.String
|
||||
* @author lingting 2021-02-25 14:09
|
||||
*/
|
||||
@SneakyThrows
|
||||
String sendRequest(Map<String, String> params, RequestSuffix rs);
|
||||
|
||||
/**
|
||||
*
|
||||
* 获取沙箱环境密钥
|
||||
* @param wxPay 支付信息
|
||||
* @return com.hccake.starte.pay.wx.response.WxPayResponse
|
||||
* @author lingting 2021-02-25 14:49
|
||||
*/
|
||||
default WxPayResponse sandbox(WxPay wxPay) {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("mch_id", wxPay.getMchId());
|
||||
map.put("nonce_str", WxPayUtil.generateNonceStr());
|
||||
// 设置签名类型
|
||||
map.put(WxPayConstant.FIELD_SIGN_TYPE, SignType.MD5.getStr());
|
||||
// 签名
|
||||
map.put(WxPayConstant.FIELD_SIGN, WxPayUtil.sign(map, wxPay.getMckKey()));
|
||||
|
||||
return WxPayResponse.of(request(map, RequestSuffix.GETSIGNKEY));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.hccake.starte.pay.wx.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 请求后缀
|
||||
* @author lingting 2021/1/29 18:01
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RequestSuffix {
|
||||
|
||||
/**
|
||||
* 下单请求后缀
|
||||
*/
|
||||
UNIFIEDORDER("unifiedorder"),
|
||||
/**
|
||||
* 获取沙箱密钥
|
||||
*/
|
||||
GETSIGNKEY("getsignkey"),
|
||||
/**
|
||||
* 查询订单
|
||||
*/
|
||||
ORDERQUERY("orderquery"),
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* 后缀
|
||||
*/
|
||||
private final String suffix;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.hccake.starte.pay.wx.enums;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 返回code
|
||||
* @author lingting 2021/2/1 11:31
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ResponseCode {
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCESS,
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
FAIL,
|
||||
/**
|
||||
* 异常
|
||||
*/
|
||||
ERROR,
|
||||
|
||||
;
|
||||
|
||||
@JsonCreator
|
||||
public static ResponseCode of(String status) {
|
||||
switch (status) {
|
||||
case "SUCCESS":
|
||||
return SUCCESS;
|
||||
case "FAIL":
|
||||
return FAIL;
|
||||
default:
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.hccake.starte.pay.wx.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/1/29 18:14
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum SignType {
|
||||
|
||||
/**
|
||||
* 一般用于沙箱环境
|
||||
*/
|
||||
MD5("MD5"),
|
||||
/**
|
||||
* 一般用于正式环境
|
||||
*/
|
||||
HMAC_SHA256("HMAC-SHA256"),
|
||||
|
||||
;
|
||||
|
||||
private final String str;
|
||||
|
||||
public static SignType of(String str){
|
||||
for (SignType e: values()){
|
||||
if (e.str.equals(str)){
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.hccake.starte.pay.wx.enums;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/2/25 15:35
|
||||
*/
|
||||
public enum TradeState {
|
||||
|
||||
/**
|
||||
* 支付成功
|
||||
*/
|
||||
SUCCESS,
|
||||
/**
|
||||
* 转入退款
|
||||
*/
|
||||
REFUND,
|
||||
/**
|
||||
* 未支付
|
||||
*/
|
||||
NOTPAY,
|
||||
/**
|
||||
* 已关闭
|
||||
*/
|
||||
CLOSED,
|
||||
/**
|
||||
* 已撤销(刷卡支付)
|
||||
*/
|
||||
REVOKED,
|
||||
/**
|
||||
* 用户支付中
|
||||
*/
|
||||
USERPAYING,
|
||||
/**
|
||||
* 支付失败(其他原因,如银行返回失败)
|
||||
*/
|
||||
PAYERROR,
|
||||
/**
|
||||
* 已接收,等待扣款
|
||||
*/
|
||||
ACCEPT,
|
||||
/**
|
||||
* 异常
|
||||
*/
|
||||
ERROR,
|
||||
|
||||
;
|
||||
|
||||
@JsonCreator
|
||||
public static TradeState of(String status) {
|
||||
switch (status) {
|
||||
case "SUCCESS":
|
||||
return SUCCESS;
|
||||
case "REFUND":
|
||||
return REFUND;
|
||||
case "NOTPAY":
|
||||
return NOTPAY;
|
||||
case "CLOSED":
|
||||
return CLOSED;
|
||||
case "REVOKED":
|
||||
return REVOKED;
|
||||
case "USERPAYING":
|
||||
return USERPAYING;
|
||||
case "PAYERROR":
|
||||
return PAYERROR;
|
||||
case "ACCEPT":
|
||||
return ACCEPT;
|
||||
default:
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.hccake.starte.pay.wx.enums;
|
||||
|
||||
/**
|
||||
* 微信支付类型
|
||||
* @author lingting 2021/2/25 10:15
|
||||
*/
|
||||
public enum TradeType {
|
||||
|
||||
/**
|
||||
* 小程序或公众号
|
||||
*/
|
||||
JSAPI,
|
||||
|
||||
/**
|
||||
* APP
|
||||
*/
|
||||
APP,
|
||||
|
||||
/**
|
||||
* 原生
|
||||
*/
|
||||
NATIVE,
|
||||
|
||||
/**
|
||||
* h5
|
||||
*/
|
||||
MWEB,
|
||||
|
||||
;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.hccake.starte.pay.wx.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.hccake.ballcat.common.core.util.JacksonUtils;
|
||||
import com.hccake.starte.pay.wx.WxPay;
|
||||
import com.hccake.starte.pay.wx.enums.ResponseCode;
|
||||
import com.hccake.starte.pay.wx.enums.TradeType;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/2/25 15:43
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class WxPayCallback {
|
||||
|
||||
@JsonProperty("transaction_id")
|
||||
private String transactionId;
|
||||
|
||||
@JsonProperty("nonce_str")
|
||||
private String nonceStr;
|
||||
|
||||
@JsonProperty("bank_type")
|
||||
private String bankType;
|
||||
|
||||
@JsonProperty("openid")
|
||||
private String openid;
|
||||
|
||||
@JsonProperty("sign")
|
||||
private String sign;
|
||||
|
||||
@JsonProperty("fee_type")
|
||||
private String feeType;
|
||||
|
||||
@JsonProperty("mch_id")
|
||||
private String mchId;
|
||||
|
||||
@JsonProperty("cash_fee")
|
||||
private BigInteger cashFee;
|
||||
|
||||
@JsonProperty("out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
@JsonProperty("appid")
|
||||
private String appid;
|
||||
|
||||
@JsonProperty("total_fee")
|
||||
private BigInteger totalFee;
|
||||
|
||||
@JsonProperty("trade_type")
|
||||
private TradeType tradeType;
|
||||
|
||||
@JsonProperty("result_code")
|
||||
private ResponseCode resultCode;
|
||||
|
||||
@JsonProperty("time_end")
|
||||
private String timeEnd;
|
||||
|
||||
@JsonProperty("is_subscribe")
|
||||
private String isSubscribe;
|
||||
|
||||
@JsonProperty("return_code")
|
||||
private ResponseCode returnCode;
|
||||
|
||||
public static WxPayCallback of(Map<String, String> res) {
|
||||
return JacksonUtils.toObj(JacksonUtils.toJson(res), WxPayCallback.class).setRaw(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回的原始数据
|
||||
*/
|
||||
private Map<String, String> raw;
|
||||
|
||||
/**
|
||||
* 验签
|
||||
* @param wxPay 微信支付信息
|
||||
* @return boolean
|
||||
* @author lingting 2021-02-25 16:04
|
||||
*/
|
||||
public boolean checkSign(WxPay wxPay) {
|
||||
return wxPay.checkSign(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.hccake.starte.pay.wx.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.hccake.ballcat.common.core.util.JacksonUtils;
|
||||
import com.hccake.starte.pay.wx.enums.ResponseCode;
|
||||
import com.hccake.starte.pay.wx.enums.TradeState;
|
||||
import com.hccake.starte.pay.wx.enums.TradeType;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/2/25 15:19
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class WxPayOrderQueryResponse {
|
||||
|
||||
@JsonProperty("transaction_id")
|
||||
private String transactionId;
|
||||
|
||||
@JsonProperty("nonce_str")
|
||||
private String nonceStr;
|
||||
|
||||
@JsonProperty("trade_state")
|
||||
private TradeState tradeState;
|
||||
|
||||
@JsonProperty("bank_type")
|
||||
private String bankType;
|
||||
|
||||
@JsonProperty("openid")
|
||||
private String openid;
|
||||
|
||||
@JsonProperty("sign")
|
||||
private String sign;
|
||||
|
||||
@JsonProperty("return_msg")
|
||||
private String returnMsg;
|
||||
|
||||
@JsonProperty("fee_type")
|
||||
private String feeType;
|
||||
|
||||
@JsonProperty("mch_id")
|
||||
private String mchId;
|
||||
|
||||
@JsonProperty("cash_fee")
|
||||
private BigInteger cashFee;
|
||||
|
||||
@JsonProperty("out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
@JsonProperty("cash_fee_type")
|
||||
private String cashFeeType;
|
||||
|
||||
@JsonProperty("appid")
|
||||
private String appid;
|
||||
|
||||
@JsonProperty("total_fee")
|
||||
private BigInteger totalFee;
|
||||
|
||||
@JsonProperty("trade_state_desc")
|
||||
private String tradeStateDesc;
|
||||
|
||||
@JsonProperty("trade_type")
|
||||
private TradeType tradeType;
|
||||
|
||||
@JsonProperty("result_code")
|
||||
private ResponseCode resultCode;
|
||||
|
||||
@JsonProperty("attach")
|
||||
private String attach;
|
||||
|
||||
@JsonProperty("time_end")
|
||||
private String timeEnd;
|
||||
|
||||
@JsonProperty("is_subscribe")
|
||||
private String isSubscribe;
|
||||
|
||||
@JsonProperty("return_code")
|
||||
private ResponseCode returnCode;
|
||||
|
||||
public static WxPayOrderQueryResponse of(Map<String, String> res) {
|
||||
return JacksonUtils.toObj(JacksonUtils.toJson(res), WxPayOrderQueryResponse.class).setRaw(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回的原始数据
|
||||
*/
|
||||
private Map<String, String> raw;
|
||||
|
||||
/**
|
||||
* 交易是否成功 . 返回false 表示交易失败
|
||||
* @return boolean
|
||||
* @author lingting 2021-02-25 15:35
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
// 交易成功
|
||||
if (returnCode == ResponseCode.SUCCESS && resultCode == ResponseCode.SUCCESS
|
||||
&& tradeState == TradeState.SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.hccake.starte.pay.wx.response;
|
||||
|
||||
import static com.hccake.starte.pay.wx.enums.ResponseCode.SUCCESS;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.hccake.ballcat.common.core.util.JacksonUtils;
|
||||
import com.hccake.starte.pay.wx.enums.ResponseCode;
|
||||
import com.hccake.starte.pay.wx.enums.TradeType;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/2/1 11:38
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class WxPayResponse {
|
||||
|
||||
public static WxPayResponse of(Map<String, String> res) {
|
||||
return JacksonUtils.toObj(JacksonUtils.toJson(res), WxPayResponse.class).setRaw(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回状态码. 此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
|
||||
*/
|
||||
@JsonProperty("return_code")
|
||||
private ResponseCode returnCode;
|
||||
|
||||
/**
|
||||
* 返回信息. 返回信息,如非空,为错误原因: 1.签名失败 2.参数格式校验错误
|
||||
*/
|
||||
@JsonProperty("return_msg")
|
||||
private String returnMsg;
|
||||
|
||||
/**
|
||||
* 应用APPID. 调用接口提交的应用ID
|
||||
*/
|
||||
@JsonProperty("appid")
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 商户号. 调用接口提交的商户号
|
||||
*/
|
||||
@JsonProperty("mch_id")
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 设备号. 调用接口提交的终端设备号,
|
||||
*/
|
||||
@JsonProperty("device_info")
|
||||
private String deviceInfo;
|
||||
|
||||
/**
|
||||
* 随机字符串. 微信返回的随机字符串
|
||||
*/
|
||||
@JsonProperty("nonce_str")
|
||||
private String nonceStr;
|
||||
|
||||
/**
|
||||
* 签名. 微信返回的签名,详见签名算法
|
||||
*/
|
||||
@JsonProperty("sign")
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 业务结果. SUCCESS/FAIL
|
||||
*/
|
||||
@JsonProperty("result_code")
|
||||
private ResponseCode resultCode;
|
||||
|
||||
/**
|
||||
* 错误代码. 详细参见第6节错误列表
|
||||
*/
|
||||
@JsonProperty("err_code")
|
||||
private String errCode;
|
||||
|
||||
/**
|
||||
* 错误代码描述. 错误返回的信息描述
|
||||
*/
|
||||
@JsonProperty("err_code_des")
|
||||
private String errCodeDes;
|
||||
|
||||
/**
|
||||
* 交易类型. 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP,详细说明见参数规定
|
||||
*/
|
||||
@JsonProperty("trade_type")
|
||||
private TradeType tradeType;
|
||||
|
||||
/**
|
||||
* 预支付交易会话标识. 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
|
||||
*/
|
||||
@JsonProperty("prepay_id")
|
||||
private String prepayId;
|
||||
|
||||
/**
|
||||
* 沙箱专用密钥
|
||||
*/
|
||||
@JsonProperty("sandbox_signkey")
|
||||
private String sandboxSignKey;
|
||||
|
||||
/**
|
||||
* 原生支付返回的二维码
|
||||
* @see TradeType#NATIVE
|
||||
*/
|
||||
@JsonProperty("code_url")
|
||||
private String codeUrl;
|
||||
|
||||
/**
|
||||
* h5 支付
|
||||
* @see TradeType#MWEB
|
||||
*/
|
||||
@JsonProperty("mweb_url")
|
||||
private String mWebUrl;
|
||||
|
||||
/**
|
||||
* 返回的原始数据
|
||||
*/
|
||||
private Map<String, String> raw;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package com.hccake.starte.pay.wx.utils;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.hccake.starte.pay.wx.constants.WxPayConstant;
|
||||
import com.hccake.starte.pay.wx.enums.SignType;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/1/26 16:04
|
||||
*/
|
||||
@Slf4j
|
||||
public class WxPayUtil {
|
||||
|
||||
public static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
factory.setXIncludeAware(false);
|
||||
factory.setExpandEntityReferences(false);
|
||||
return factory.newDocumentBuilder();
|
||||
}
|
||||
|
||||
public static Document getDocument() throws ParserConfigurationException {
|
||||
return getDocumentBuilder().newDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
* map 转 xml 字符串
|
||||
* @param data map
|
||||
* @return java.lang.String
|
||||
* @author lingting 2021-02-01 10:22
|
||||
*/
|
||||
public static String mapToXml(Map<String, String> data) throws ParserConfigurationException, TransformerException {
|
||||
Document document = getDocument();
|
||||
Element root = document.createElement("xml");
|
||||
document.appendChild(root);
|
||||
for (String key : data.keySet()) {
|
||||
String value = data.get(key);
|
||||
if (value == null) {
|
||||
value = "";
|
||||
}
|
||||
value = value.trim();
|
||||
Element filed = document.createElement(key);
|
||||
filed.appendChild(document.createTextNode(value));
|
||||
root.appendChild(filed);
|
||||
}
|
||||
|
||||
Transformer transformer = TransformerFactory.newInstance().newTransformer();
|
||||
|
||||
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
StreamResult result = new StreamResult(writer);
|
||||
|
||||
transformer.transform(new DOMSource(document), result);
|
||||
String output = writer.getBuffer().toString();
|
||||
|
||||
try {
|
||||
writer.close();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.error("string 写入流关闭异常");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* xml字符串转map
|
||||
* @param xml xml字符串
|
||||
* @return java.util.Map<java.lang.String,java.lang.String>
|
||||
* @author lingting 2021-02-01 11:29
|
||||
*/
|
||||
public static Map<String, String> xmlToMap(String xml)
|
||||
throws ParserConfigurationException, IOException, SAXException {
|
||||
Map<String, String> data = new HashMap<>(30);
|
||||
|
||||
InputStream stream = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
Document doc = getDocumentBuilder().parse(stream);
|
||||
doc.getDocumentElement().normalize();
|
||||
NodeList nodeList = doc.getDocumentElement().getChildNodes();
|
||||
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
|
||||
Node node = nodeList.item(idx);
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element element = (Element) node;
|
||||
data.put(element.getNodeName(), element.getTextContent());
|
||||
}
|
||||
}
|
||||
try {
|
||||
stream.close();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// do nothing
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名
|
||||
* @param params 参数
|
||||
* @param mckKey 密钥
|
||||
* @return java.lang.String 签名结果
|
||||
* @author lingting 2021-01-29 18:13
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static String sign(Map<String, String> params, String mckKey) {
|
||||
SignType st = SignType.of(params.get(WxPayConstant.FIELD_SIGN_TYPE));
|
||||
Assert.isFalse(st == null, "签名类型不能为空!");
|
||||
|
||||
String[] keyArray = params.keySet().toArray(new String[0]);
|
||||
// 参数key排序
|
||||
Arrays.sort(keyArray);
|
||||
// 构建排序后的用于签名的字符串
|
||||
StringBuilder paramsStr = new StringBuilder();
|
||||
for (String k : keyArray) {
|
||||
if (k.equals(WxPayConstant.FIELD_SIGN)) {
|
||||
continue;
|
||||
}
|
||||
// 参数值为空,则不参与签名
|
||||
if (params.get(k).trim().length() > 0) {
|
||||
paramsStr.append(k).append("=").append(params.get(k).trim()).append("&");
|
||||
}
|
||||
}
|
||||
paramsStr.append("key=").append(mckKey);
|
||||
|
||||
// 签名后的字节
|
||||
byte[] bytes;
|
||||
if (st == SignType.MD5) {
|
||||
final MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
bytes = md5.digest(paramsStr.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
else {
|
||||
final Mac mac = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec sk = new SecretKeySpec(mckKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
|
||||
mac.init(sk);
|
||||
bytes = mac.doFinal(paramsStr.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
// 构建返回值
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (byte b : bytes) {
|
||||
builder.append(Integer.toHexString((b & 0xFF) | 0x100), 1, 3);
|
||||
}
|
||||
return builder.toString().toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机字符串
|
||||
* @return java.lang.String
|
||||
* @author lingting 2021-02-25 14:42
|
||||
*/
|
||||
public static String generateNonceStr() {
|
||||
return RandomUtil.randomString(16);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,5 +18,6 @@
|
||||
<module>ballcat-extend-kafka-stream</module>
|
||||
<module>ballcat-extend-pay-virtual</module>
|
||||
<module>ballcat-extend-pay-ali</module>
|
||||
<module>ballcat-extend-pay-wx</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -4,12 +4,16 @@ import cn.hutool.core.lang.Snowflake;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.hccake.starte.pay.ali.AliPay;
|
||||
import com.hccake.starte.pay.ali.domain.AliPayCallback;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/1/25 15:18
|
||||
@@ -17,7 +21,8 @@ import java.util.Map;
|
||||
@RestController
|
||||
@RequestMapping("ali")
|
||||
@RequiredArgsConstructor
|
||||
public class Controller {
|
||||
public class AliController {
|
||||
|
||||
|
||||
private final AliPay aliPay;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.hccake.sample.pay.wx;
|
||||
|
||||
import cn.hutool.core.lang.Snowflake;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.hccake.starte.pay.wx.WxPay;
|
||||
import com.hccake.starte.pay.wx.response.WxPayCallback;
|
||||
import com.hccake.starte.pay.wx.response.WxPayOrderQueryResponse;
|
||||
import com.hccake.starte.pay.wx.utils.WxPayUtil;
|
||||
import java.math.BigDecimal;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/2/25 13:55
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("wx")
|
||||
@RequiredArgsConstructor
|
||||
public class WxController {
|
||||
|
||||
private final WxPay wxPay;
|
||||
|
||||
private static final Snowflake snowflake = IdUtil.createSnowflake(1, 1);
|
||||
|
||||
@SneakyThrows
|
||||
@GetMapping
|
||||
public String notice(HttpServletRequest request) {
|
||||
String sn = snowflake.nextIdStr();
|
||||
BigDecimal val = new BigDecimal("0.01");
|
||||
String ip = "27.115.44.246";
|
||||
// WxPayResponse response = wxPay.nativePay(sn, val, ip, "商品");
|
||||
WxPayOrderQueryResponse queryResponse = wxPay.query("1364829305962557441", "");
|
||||
|
||||
String data = "**";
|
||||
WxPayCallback callback = WxPayCallback.of(WxPayUtil.xmlToMap(data));
|
||||
System.out.println(callback.checkSign(wxPay));
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,14 @@ ballcat:
|
||||
alipay-public-key: '***'
|
||||
notify-url: https://www.baidu.com
|
||||
return-url: https://www.baidu.com
|
||||
wx:
|
||||
sandbox: false
|
||||
prod:
|
||||
app-id: '****'
|
||||
mch-id: '****'
|
||||
mck-key: '***'
|
||||
notify-url: https://www.baidu.com
|
||||
return-url: https://www.baidu.com
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
<groupId>com.hccake</groupId>
|
||||
<artifactId>ballcat-extend-pay-ali</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hccake</groupId>
|
||||
<artifactId>ballcat-extend-pay-wx</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.hccake</groupId>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.hccake.starter.pay.wx;
|
||||
|
||||
import com.hccake.starte.pay.wx.WxPay;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/1/25 11:30
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnClass(WxPay.class)
|
||||
@EnableConfigurationProperties(WxPayProperties.class)
|
||||
public class WxPayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(WxPay.class)
|
||||
public WxPay wxPay(WxPayProperties properties) {
|
||||
// 获取配置
|
||||
WxPayProperties.Config config = properties.getSandbox() ? properties.getDev() : properties.getProd();
|
||||
|
||||
WxPay wxPay = new WxPay(config.getAppId(), config.getMchId(), config.getMckKey(), properties.getSandbox());
|
||||
|
||||
wxPay.setReturnUrl(config.getReturnUrl());
|
||||
wxPay.setNotifyUrl(config.getNotifyUrl());
|
||||
return wxPay;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.hccake.starter.pay.wx;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/1/25 11:29
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "ballcat.pay.wx")
|
||||
public class WxPayProperties {
|
||||
|
||||
/**
|
||||
* 是否使用沙箱, 如果为 false 则使用 prod的配置初始化支付信息, 否则使用dev配置进行初始化
|
||||
*/
|
||||
private Boolean sandbox = false;
|
||||
|
||||
/**
|
||||
* 线上环境配置
|
||||
*/
|
||||
public Config prod;
|
||||
|
||||
/**
|
||||
* 沙箱配置.
|
||||
*/
|
||||
public Config dev;
|
||||
|
||||
@Data
|
||||
public static class Config {
|
||||
|
||||
private String appId;
|
||||
|
||||
private String mchId;
|
||||
|
||||
private String mckKey;
|
||||
|
||||
private String returnUrl;
|
||||
|
||||
private String notifyUrl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.hccake.starter.pay.virtual.VirtualPayAutoConfiguration,\
|
||||
com.hccake.starter.pay.ali.AliPayAutoConfiguration
|
||||
com.hccake.starter.pay.ali.AliPayAutoConfiguration,\
|
||||
com.hccake.starter.pay.wx.WxPayAutoConfiguration
|
||||
Reference in New Issue
Block a user