支付
This commit is contained in:
parent
8ac386242d
commit
f6f847e41c
58
src/main/java/com/ycwl/basic/pay/PayFactory.java
Normal file
58
src/main/java/com/ycwl/basic/pay/PayFactory.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package com.ycwl.basic.pay;
|
||||||
|
|
||||||
|
import com.ycwl.basic.pay.adapter.IPayAdapter;
|
||||||
|
import com.ycwl.basic.pay.adapter.WxMpPayAdapter;
|
||||||
|
import com.ycwl.basic.pay.enums.PayAdapterType;
|
||||||
|
import com.ycwl.basic.pay.exceptions.PayUndefinedException;
|
||||||
|
import com.ycwl.basic.pay.exceptions.PayUnsupportedException;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class PayFactory {
|
||||||
|
|
||||||
|
public static IPayAdapter getAdapter(String typeName) {
|
||||||
|
PayAdapterType adapterEnum;
|
||||||
|
try {
|
||||||
|
adapterEnum = PayAdapterType.valueOf(typeName);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new PayUnsupportedException("不支持的Adapter类型");
|
||||||
|
}
|
||||||
|
return getAdapter(adapterEnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IPayAdapter getAdapter(PayAdapterType type) {
|
||||||
|
switch (type) {
|
||||||
|
case WX_MP_PAY:
|
||||||
|
return new WxMpPayAdapter();
|
||||||
|
default:
|
||||||
|
throw new PayUnsupportedException("不支持的Adapter类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Map<String, IPayAdapter> namedAdapter = new HashMap<>();
|
||||||
|
protected static IPayAdapter defaultAdapter = null;
|
||||||
|
|
||||||
|
public static void register(String name, IPayAdapter adapter) {
|
||||||
|
namedAdapter.put(name, adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IPayAdapter use(String name) {
|
||||||
|
IPayAdapter adapter = namedAdapter.get(name);
|
||||||
|
if (adapter == null) {
|
||||||
|
throw new PayUndefinedException("未定义的支付方式:"+name);
|
||||||
|
}
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IPayAdapter use() {
|
||||||
|
if (defaultAdapter == null) {
|
||||||
|
throw new PayUndefinedException("未定义默认的支付方式");
|
||||||
|
}
|
||||||
|
return defaultAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setDefault(String defaultName) {
|
||||||
|
PayFactory.defaultAdapter = use(defaultName);
|
||||||
|
}
|
||||||
|
}
|
42
src/main/java/com/ycwl/basic/pay/adapter/IPayAdapter.java
Normal file
42
src/main/java/com/ycwl/basic/pay/adapter/IPayAdapter.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package com.ycwl.basic.pay.adapter;
|
||||||
|
|
||||||
|
// 假设request对象所在的包,可根据实际情况修改
|
||||||
|
import com.ycwl.basic.pay.entity.CancelOrderRequest;
|
||||||
|
import com.ycwl.basic.pay.entity.CreateOrderRequest;
|
||||||
|
import com.ycwl.basic.pay.entity.CreateOrderResponse;
|
||||||
|
import com.ycwl.basic.pay.entity.PayResponse;
|
||||||
|
import com.ycwl.basic.pay.entity.RefundResponse;
|
||||||
|
import com.ycwl.basic.pay.entity.RefundOrderRequest;
|
||||||
|
import com.ycwl.basic.pay.entity.RefundOrderResponse;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
// 将接口改为抽象类
|
||||||
|
public interface IPayAdapter {
|
||||||
|
// 下单方法
|
||||||
|
CreateOrderResponse createOrder(CreateOrderRequest request);
|
||||||
|
|
||||||
|
// 获取支付参数方法
|
||||||
|
Map<String, Object> getPaymentParams(CreateOrderResponse response);
|
||||||
|
|
||||||
|
// 处理回调信息方法
|
||||||
|
PayResponse handleCallback(HttpServletRequest request) throws IOException;
|
||||||
|
|
||||||
|
// 查询订单状态方法
|
||||||
|
PayResponse queryOrder(String orderNo);
|
||||||
|
|
||||||
|
// 退款方法
|
||||||
|
RefundOrderResponse refund(RefundOrderRequest request);
|
||||||
|
|
||||||
|
// 处理退款回调方法
|
||||||
|
RefundResponse handleRefundCallback(HttpServletRequest request) throws IOException;
|
||||||
|
|
||||||
|
// 查询退款订单状态方法
|
||||||
|
RefundResponse checkRefundStatus(String refundNo);
|
||||||
|
|
||||||
|
void loadConfig(Map<String, String> config);
|
||||||
|
|
||||||
|
void cancelOrder(CancelOrderRequest request);
|
||||||
|
}
|
353
src/main/java/com/ycwl/basic/pay/adapter/WxMpPayAdapter.java
Normal file
353
src/main/java/com/ycwl/basic/pay/adapter/WxMpPayAdapter.java
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
package com.ycwl.basic.pay.adapter;
|
||||||
|
|
||||||
|
import com.wechat.pay.java.core.Config;
|
||||||
|
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||||
|
import com.wechat.pay.java.core.notification.NotificationConfig;
|
||||||
|
import com.wechat.pay.java.core.notification.NotificationParser;
|
||||||
|
import com.wechat.pay.java.core.notification.RequestParam;
|
||||||
|
import com.wechat.pay.java.core.util.PemUtil;
|
||||||
|
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
|
||||||
|
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
|
||||||
|
import com.wechat.pay.java.service.payments.jsapi.model.CloseOrderRequest;
|
||||||
|
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
|
||||||
|
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
|
||||||
|
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
|
||||||
|
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
|
||||||
|
import com.wechat.pay.java.service.payments.model.Transaction;
|
||||||
|
import com.wechat.pay.java.service.refund.RefundService;
|
||||||
|
import com.wechat.pay.java.service.refund.model.AmountReq;
|
||||||
|
import com.wechat.pay.java.service.refund.model.CreateRequest;
|
||||||
|
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
|
||||||
|
import com.wechat.pay.java.service.refund.model.Refund;
|
||||||
|
import com.wechat.pay.java.service.refund.model.RefundNotification;
|
||||||
|
import com.ycwl.basic.constant.NumberConstant;
|
||||||
|
import com.ycwl.basic.pay.entity.CancelOrderRequest;
|
||||||
|
import com.ycwl.basic.pay.entity.CreateOrderRequest;
|
||||||
|
import com.ycwl.basic.pay.entity.CreateOrderResponse;
|
||||||
|
import com.ycwl.basic.pay.entity.PayResponse;
|
||||||
|
import com.ycwl.basic.pay.entity.RefundResponse;
|
||||||
|
import com.ycwl.basic.pay.entity.RefundOrderRequest;
|
||||||
|
import com.ycwl.basic.pay.entity.RefundOrderResponse;
|
||||||
|
import com.ycwl.basic.pay.entity.WxMpPayConfig;
|
||||||
|
import com.ycwl.basic.pay.exceptions.PayWrongConfigException;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_NONCE;
|
||||||
|
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SERIAL;
|
||||||
|
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SIGNATURE;
|
||||||
|
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_TIMESTAMP;
|
||||||
|
import static com.wechat.pay.java.service.refund.model.Status.SUCCESS;
|
||||||
|
|
||||||
|
public class WxMpPayAdapter implements IPayAdapter {
|
||||||
|
private WxMpPayConfig config;
|
||||||
|
public static final String WECHAT_PAY_SIGNATURE_TYPE = "Wechatpay-Signature-Type";
|
||||||
|
public WxMpPayAdapter() {
|
||||||
|
|
||||||
|
}
|
||||||
|
public WxMpPayAdapter(WxMpPayConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void loadConfig(Map<String, String> _config) {
|
||||||
|
this.config = new WxMpPayConfig();
|
||||||
|
if (_config != null) {
|
||||||
|
this.config.setMerchantId(_config.get("merchantId"));
|
||||||
|
this.config.setAppId(_config.get("appId"));
|
||||||
|
this.config.setPrivateKey(_config.get("privateKey"));
|
||||||
|
this.config.setSerialNumber(_config.get("serialNumber"));
|
||||||
|
this.config.setApiV3Key(_config.get("apiV3Key"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Config clientConfig;
|
||||||
|
|
||||||
|
private Config getConfig() {
|
||||||
|
if (clientConfig == null) {
|
||||||
|
clientConfig = new RSAAutoCertificateConfig.Builder()
|
||||||
|
.merchantId(config.getMerchantId())
|
||||||
|
.privateKey(config.getPrivateKey())
|
||||||
|
.merchantSerialNumber(config.getSerialNumber())
|
||||||
|
.apiV3Key(config.getApiV3Key())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return clientConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CreateOrderResponse createOrder(CreateOrderRequest request) {
|
||||||
|
CreateOrderResponse resp = new CreateOrderResponse();
|
||||||
|
if (request.getPrice() <= 0) {
|
||||||
|
resp.setSkipPay(true);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
Config wxConfig = getConfig();
|
||||||
|
JsapiService service = new JsapiService.Builder().config(wxConfig).build();
|
||||||
|
PrepayRequest prepayRequest = new PrepayRequest();
|
||||||
|
Amount amount = new Amount();
|
||||||
|
amount.setTotal(request.getPrice());
|
||||||
|
prepayRequest.setAmount(amount);
|
||||||
|
prepayRequest.setAppid(config.getAppId());
|
||||||
|
prepayRequest.setMchid(config.getMerchantId());
|
||||||
|
prepayRequest.setDescription(request.getDescription());
|
||||||
|
prepayRequest.setNotifyUrl(request.getNotifyUrl());
|
||||||
|
prepayRequest.setOutTradeNo(request.getOrderNo());
|
||||||
|
Payer payer = new Payer();
|
||||||
|
payer.setOpenid(request.getUserIdentify());
|
||||||
|
prepayRequest.setPayer(payer);
|
||||||
|
PrepayResponse response = service.prepay(prepayRequest);
|
||||||
|
resp.setSuccess(true);
|
||||||
|
resp.setSkipPay(false);
|
||||||
|
resp.setOrderNo(response.getPrepayId());
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getPaymentParams(CreateOrderResponse response) {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
if (response.isSkipPay()) {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
Long timeStamp = System.currentTimeMillis() / NumberConstant.THOUSAND;
|
||||||
|
params.put("appId", config.getAppId());
|
||||||
|
params.put("timeStamp", timeStamp);
|
||||||
|
String nonce = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
|
||||||
|
params.put("nonceStr", nonce);
|
||||||
|
String signStr = Stream.of(config.getAppId(), String.valueOf(timeStamp), nonce, "prepay_id=" + response.getOrderNo())
|
||||||
|
.collect(Collectors.joining("\n", "", "\n"));
|
||||||
|
String sign;
|
||||||
|
try {
|
||||||
|
sign = getSign(signStr, config.getPrivateKey());
|
||||||
|
} catch (InvalidKeyException | SignatureException | NoSuchAlgorithmException e) {
|
||||||
|
throw new PayWrongConfigException("配置错误");
|
||||||
|
}
|
||||||
|
params.put("paySign", sign);
|
||||||
|
params.put("signType", "RSA-SHA256");
|
||||||
|
params.put("prepayId", "prepay_id=" + response.getOrderNo());
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayResponse handleCallback(HttpServletRequest request) throws IOException {
|
||||||
|
ServletInputStream inputStream = request.getInputStream();
|
||||||
|
StringBuffer body = new StringBuffer();
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
String s;
|
||||||
|
// 读取回调请求体
|
||||||
|
while ((s = bufferedReader.readLine()) != null) {
|
||||||
|
body.append(s);
|
||||||
|
}
|
||||||
|
PayResponse resp = new PayResponse();
|
||||||
|
|
||||||
|
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
|
||||||
|
String nonce = request.getHeader(WECHAT_PAY_NONCE);
|
||||||
|
String signType = request.getHeader(WECHAT_PAY_SIGNATURE_TYPE);
|
||||||
|
String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
|
||||||
|
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
|
||||||
|
NotificationConfig config = (NotificationConfig) getConfig();
|
||||||
|
NotificationParser parser = new NotificationParser(config);
|
||||||
|
RequestParam requestParam = new RequestParam.Builder()
|
||||||
|
.serialNumber(serialNo)
|
||||||
|
.nonce(nonce)
|
||||||
|
.signature(signature)
|
||||||
|
.timestamp(timestamp)
|
||||||
|
// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
|
||||||
|
.signType(signType)
|
||||||
|
.body(body.toString())
|
||||||
|
.build();
|
||||||
|
Transaction parse = parser.parse(requestParam, Transaction.class);
|
||||||
|
resp.setValid(true);
|
||||||
|
resp.setOrderNo(parse.getOutTradeNo());
|
||||||
|
if (parse.getAmount() != null) {
|
||||||
|
resp.setOrderPrice(parse.getAmount().getTotal());
|
||||||
|
resp.setPayPrice(parse.getAmount().getPayerTotal());
|
||||||
|
}
|
||||||
|
switch (parse.getTradeState()) {
|
||||||
|
case SUCCESS:
|
||||||
|
resp.setState(PayResponse.PAY_STATE.SUCCESS);
|
||||||
|
break;
|
||||||
|
case NOTPAY:
|
||||||
|
case CLOSED:
|
||||||
|
case REVOKED:
|
||||||
|
resp.setState(PayResponse.PAY_STATE.FAIL);
|
||||||
|
break;
|
||||||
|
case REFUND:
|
||||||
|
resp.setState(PayResponse.PAY_STATE.REFUND);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
resp.setState(PayResponse.PAY_STATE.CANCEL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
resp.setPayTime(parse.getSuccessTime());
|
||||||
|
resp.setOriginalResponse(parse);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayResponse queryOrder(String orderNo) {
|
||||||
|
Config wxConfig = getConfig();
|
||||||
|
JsapiService service = new JsapiService.Builder().config(wxConfig).build();
|
||||||
|
QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
|
||||||
|
queryRequest.setMchid(config.getMerchantId());
|
||||||
|
queryRequest.setOutTradeNo(orderNo);
|
||||||
|
PayResponse resp = new PayResponse();
|
||||||
|
Transaction result = service.queryOrderByOutTradeNo(queryRequest);
|
||||||
|
resp.setValid(true);
|
||||||
|
resp.setOrderNo(result.getOutTradeNo());
|
||||||
|
if (result.getAmount() != null) {
|
||||||
|
resp.setOrderPrice(result.getAmount().getTotal());
|
||||||
|
resp.setPayPrice(result.getAmount().getPayerTotal());
|
||||||
|
}
|
||||||
|
switch (result.getTradeState()) {
|
||||||
|
case SUCCESS:
|
||||||
|
resp.setState(PayResponse.PAY_STATE.SUCCESS);
|
||||||
|
break;
|
||||||
|
case NOTPAY:
|
||||||
|
case CLOSED:
|
||||||
|
case REVOKED:
|
||||||
|
resp.setState(PayResponse.PAY_STATE.FAIL);
|
||||||
|
break;
|
||||||
|
case REFUND:
|
||||||
|
resp.setState(PayResponse.PAY_STATE.REFUND);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
resp.setState(PayResponse.PAY_STATE.CANCEL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
resp.setPayTime(result.getSuccessTime());
|
||||||
|
resp.setOriginalResponse(result);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RefundOrderResponse refund(RefundOrderRequest request) {
|
||||||
|
RefundOrderResponse resp = new RefundOrderResponse();
|
||||||
|
Config wxConfig = getConfig();
|
||||||
|
RefundService service = new RefundService.Builder().config(wxConfig).build();
|
||||||
|
CreateRequest createRequest = new CreateRequest();
|
||||||
|
createRequest.setOutTradeNo(request.getOrderNo());
|
||||||
|
createRequest.setOutRefundNo(request.getRefundNo());
|
||||||
|
AmountReq amountReq = new AmountReq();
|
||||||
|
amountReq.setTotal(Long.valueOf(request.getPrice()));
|
||||||
|
amountReq.setRefund(Long.valueOf(request.getRefundPrice()));
|
||||||
|
amountReq.setCurrency("CNY");
|
||||||
|
createRequest.setAmount(amountReq);
|
||||||
|
createRequest.setNotifyUrl(request.getNotifyUrl());
|
||||||
|
Refund refund = service.create(createRequest);
|
||||||
|
if (refund.getStatus() == SUCCESS) {
|
||||||
|
resp.setSuccess(true);
|
||||||
|
resp.setRefundNo(refund.getOutRefundNo());
|
||||||
|
} else {
|
||||||
|
resp.setSuccess(false);
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RefundResponse handleRefundCallback(HttpServletRequest request) throws IOException {
|
||||||
|
ServletInputStream inputStream = request.getInputStream();
|
||||||
|
StringBuffer body = new StringBuffer();
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
String s;
|
||||||
|
// 读取回调请求体
|
||||||
|
while ((s = bufferedReader.readLine()) != null) {
|
||||||
|
body.append(s);
|
||||||
|
}
|
||||||
|
RefundResponse resp = new RefundResponse();
|
||||||
|
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
|
||||||
|
String nonce = request.getHeader(WECHAT_PAY_NONCE);
|
||||||
|
String signType = request.getHeader(WECHAT_PAY_SIGNATURE_TYPE);
|
||||||
|
String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
|
||||||
|
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
|
||||||
|
NotificationConfig config = (NotificationConfig) getConfig();
|
||||||
|
NotificationParser parser = new NotificationParser(config);RequestParam requestParam = new RequestParam.Builder()
|
||||||
|
.serialNumber(serialNo)
|
||||||
|
.nonce(nonce)
|
||||||
|
.signature(signature)
|
||||||
|
.timestamp(timestamp)
|
||||||
|
// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
|
||||||
|
.signType(signType)
|
||||||
|
.body(body.toString())
|
||||||
|
.build();
|
||||||
|
RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
|
||||||
|
resp.setValid(true);
|
||||||
|
resp.setOriginalResponse(parse);
|
||||||
|
if (parse.getRefundStatus() == SUCCESS) {
|
||||||
|
//退款成功
|
||||||
|
resp.setSuccess();
|
||||||
|
resp.setRefundTime(parse.getSuccessTime());
|
||||||
|
resp.setOrderNo(parse.getOutTradeNo());
|
||||||
|
resp.setRefundNo(parse.getRefundId());
|
||||||
|
if (parse.getAmount() != null) {
|
||||||
|
resp.setRefundPrice(Math.toIntExact(parse.getAmount().getPayerRefund()));
|
||||||
|
resp.setOrderPrice(Math.toIntExact(parse.getAmount().getTotal()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//退款失败
|
||||||
|
resp.setFail();
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RefundResponse checkRefundStatus(String refundNo) {
|
||||||
|
Config wxConfig = getConfig();
|
||||||
|
RefundService service = new RefundService.Builder().config(wxConfig).build();
|
||||||
|
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
|
||||||
|
request.setOutRefundNo(refundNo);
|
||||||
|
RefundResponse resp = new RefundResponse();
|
||||||
|
Refund result = service.queryByOutRefundNo(request);
|
||||||
|
resp.setValid(true);
|
||||||
|
resp.setOriginalResponse(result);
|
||||||
|
if (result.getStatus() == SUCCESS) {
|
||||||
|
//退款成功
|
||||||
|
resp.setSuccess();
|
||||||
|
resp.setRefundTime(result.getSuccessTime());
|
||||||
|
resp.setOrderNo(result.getOutTradeNo());
|
||||||
|
resp.setRefundNo(result.getRefundId());
|
||||||
|
if (result.getAmount() != null) {
|
||||||
|
resp.setRefundPrice(Math.toIntExact(result.getAmount().getPayerRefund()));
|
||||||
|
resp.setOrderPrice(Math.toIntExact(result.getAmount().getTotal()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//退款失败
|
||||||
|
resp.setFail();
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelOrder(CancelOrderRequest request) {
|
||||||
|
CloseOrderRequest closeOrderRequest = new CloseOrderRequest();
|
||||||
|
closeOrderRequest.setOutTradeNo(request.getOrderNo());
|
||||||
|
closeOrderRequest.setMchid(config.getMerchantId());
|
||||||
|
Config config = getConfig();
|
||||||
|
JsapiService service = new JsapiService.Builder().config(config).build();
|
||||||
|
service.closeOrder(closeOrderRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSign(String signatureStr,String privateKey) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException {
|
||||||
|
String replace = privateKey.replace("\\n", "\n");
|
||||||
|
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromString(replace);
|
||||||
|
Signature sign = Signature.getInstance("SHA256withRSA");
|
||||||
|
sign.initSign(merchantPrivateKey);
|
||||||
|
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Base64Utils.encodeToString(sign.sign());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.ycwl.basic.pay.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class CancelOrderRequest {
|
||||||
|
private String orderNo;
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.ycwl.basic.pay.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class CreateOrderRequest {
|
||||||
|
/**
|
||||||
|
* 价格,单位为分
|
||||||
|
*/
|
||||||
|
private Integer price;
|
||||||
|
private String goodsName;
|
||||||
|
private String orderNo;
|
||||||
|
private String description;
|
||||||
|
private String userIdentify;
|
||||||
|
private String notifyUrl;
|
||||||
|
|
||||||
|
|
||||||
|
public BigDecimal getPriceInYuan() {
|
||||||
|
return new BigDecimal(BigInteger.valueOf(price), 2);
|
||||||
|
}
|
||||||
|
public CreateOrderRequest setPriceInCents(Integer priceInCents) {
|
||||||
|
this.price = priceInCents;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CreateOrderRequest setPriceInYuan(BigDecimal priceInYuan) {
|
||||||
|
this.price = priceInYuan.multiply(new BigDecimal(100)).intValue();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.ycwl.basic.pay.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CreateOrderResponse {
|
||||||
|
private boolean success;
|
||||||
|
private boolean skipPay;
|
||||||
|
private String orderNo;
|
||||||
|
}
|
34
src/main/java/com/ycwl/basic/pay/entity/PayResponse.java
Normal file
34
src/main/java/com/ycwl/basic/pay/entity/PayResponse.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.ycwl.basic.pay.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PayResponse {
|
||||||
|
private boolean valid;
|
||||||
|
private String orderNo;
|
||||||
|
private Object originalResponse;
|
||||||
|
private Integer orderPrice;
|
||||||
|
private Integer payPrice;
|
||||||
|
private PAY_STATE state;
|
||||||
|
private String payTime;
|
||||||
|
|
||||||
|
public boolean isPay() {
|
||||||
|
return state == PAY_STATE.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCancel() {
|
||||||
|
return state == PAY_STATE.CANCEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRefund() {
|
||||||
|
return state == PAY_STATE.REFUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PAY_STATE {
|
||||||
|
SUCCESS,
|
||||||
|
CANCEL,
|
||||||
|
REFUND,
|
||||||
|
FAIL,
|
||||||
|
UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.ycwl.basic.pay.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class RefundOrderRequest {
|
||||||
|
private Integer price;
|
||||||
|
private Integer refundPrice;
|
||||||
|
private String orderNo;
|
||||||
|
private String refundNo;
|
||||||
|
private String notifyUrl;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.ycwl.basic.pay.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RefundOrderResponse {
|
||||||
|
private boolean success;
|
||||||
|
private String refundNo;
|
||||||
|
}
|
28
src/main/java/com/ycwl/basic/pay/entity/RefundResponse.java
Normal file
28
src/main/java/com/ycwl/basic/pay/entity/RefundResponse.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package com.ycwl.basic.pay.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RefundResponse {
|
||||||
|
private boolean valid;
|
||||||
|
private String orderNo;
|
||||||
|
private String refundNo;
|
||||||
|
private Object originalResponse;
|
||||||
|
private Integer orderPrice;
|
||||||
|
private Integer refundPrice;
|
||||||
|
private PAY_STATE state;
|
||||||
|
private String refundTime;
|
||||||
|
|
||||||
|
public void setSuccess() {
|
||||||
|
state = PAY_STATE.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFail() {
|
||||||
|
state = PAY_STATE.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PAY_STATE {
|
||||||
|
SUCCESS,
|
||||||
|
FAIL
|
||||||
|
}
|
||||||
|
}
|
12
src/main/java/com/ycwl/basic/pay/entity/WxMpPayConfig.java
Normal file
12
src/main/java/com/ycwl/basic/pay/entity/WxMpPayConfig.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.ycwl.basic.pay.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WxMpPayConfig {
|
||||||
|
private String merchantId;
|
||||||
|
private String appId;
|
||||||
|
private String privateKey;
|
||||||
|
private String serialNumber;
|
||||||
|
private String apiV3Key;
|
||||||
|
}
|
20
src/main/java/com/ycwl/basic/pay/enums/PayAdapterType.java
Normal file
20
src/main/java/com/ycwl/basic/pay/enums/PayAdapterType.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package com.ycwl.basic.pay.enums;
|
||||||
|
|
||||||
|
public enum PayAdapterType {
|
||||||
|
WX_MP_PAY("WX_MP_PAY"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
PayAdapterType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.ycwl.basic.pay.exceptions;
|
||||||
|
|
||||||
|
public class PayException extends RuntimeException {
|
||||||
|
public PayException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.ycwl.basic.pay.exceptions;
|
||||||
|
|
||||||
|
public class PayUndefinedException extends RuntimeException {
|
||||||
|
public PayUndefinedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.ycwl.basic.pay.exceptions;
|
||||||
|
|
||||||
|
public class PayUnsupportedException extends PayException {
|
||||||
|
public PayUnsupportedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.ycwl.basic.pay.exceptions;
|
||||||
|
|
||||||
|
public class PayWrongConfigException extends PayException {
|
||||||
|
public PayWrongConfigException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.ycwl.basic.pay.starter;
|
||||||
|
|
||||||
|
import com.ycwl.basic.pay.PayFactory;
|
||||||
|
import com.ycwl.basic.pay.adapter.IPayAdapter;
|
||||||
|
import com.ycwl.basic.pay.starter.config.OverallPayConfig;
|
||||||
|
import com.ycwl.basic.pay.starter.config.PayConfigItem;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class PayAutoConfiguration {
|
||||||
|
private final OverallPayConfig config;
|
||||||
|
public PayAutoConfiguration(OverallPayConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
if (config != null) {
|
||||||
|
if (config.getConfigs() != null) {
|
||||||
|
loadConfig();
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(config.getDefaultUse())) {
|
||||||
|
PayFactory.setDefault(config.getDefaultUse());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadConfig() {
|
||||||
|
for (PayConfigItem item : config.getConfigs()) {
|
||||||
|
IPayAdapter adapter = PayFactory.getAdapter(item.getType());
|
||||||
|
adapter.loadConfig(item.getConfig());
|
||||||
|
PayFactory.register(item.getName(), adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.ycwl.basic.pay.starter.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "pay")
|
||||||
|
@Data
|
||||||
|
public class OverallPayConfig {
|
||||||
|
private String defaultUse;
|
||||||
|
private List<PayConfigItem> configs;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.ycwl.basic.pay.starter.config;
|
||||||
|
|
||||||
|
import com.ycwl.basic.pay.enums.PayAdapterType;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PayConfigItem {
|
||||||
|
private String name;
|
||||||
|
private PayAdapterType type;
|
||||||
|
private Map<String, String> config;
|
||||||
|
}
|
@ -1,54 +0,0 @@
|
|||||||
package com.ycwl.basic.utils;
|
|
||||||
|
|
||||||
|
|
||||||
import cn.hutool.core.util.XmlUtil;
|
|
||||||
import com.wechat.pay.java.core.util.PemUtil;
|
|
||||||
import org.springframework.util.Base64Utils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.*;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author: songmingsong
|
|
||||||
* @CreateTime: 2024-12-05
|
|
||||||
* @Description: 微信支付
|
|
||||||
* @Version: 1.0
|
|
||||||
*/
|
|
||||||
public class WXPayUtil {
|
|
||||||
|
|
||||||
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
||||||
|
|
||||||
private static final Random RANDOM = new SecureRandom();
|
|
||||||
|
|
||||||
|
|
||||||
public static String getSign(String signatureStr,String privateKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException {
|
|
||||||
//replace 根据实际情况,不一定都需要
|
|
||||||
String replace = privateKey.replace("\\n", "\n");
|
|
||||||
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromString(replace);
|
|
||||||
Signature sign = Signature.getInstance("SHA256withRSA");
|
|
||||||
sign.initSign(merchantPrivateKey);
|
|
||||||
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
|
|
||||||
return Base64Utils.encodeToString(sign.sign());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取随机字符串 Nonce Str
|
|
||||||
*
|
|
||||||
* @return String 随机字符串
|
|
||||||
*/
|
|
||||||
public static String generateNonceStr() {
|
|
||||||
char[] nonceChars = new char[32];
|
|
||||||
for (int index = 0; index < nonceChars.length; ++index) {
|
|
||||||
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
|
|
||||||
}
|
|
||||||
return new String(nonceChars);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, Object> xmlToMap(String xmlData) {
|
|
||||||
return XmlUtil.xmlToMap(xmlData);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user