SpringBoot 支付渠道实现统一的支付服务示例
作者:Java程序员 拥抱ai
本文提出了一种基于工厂模式和策略模式的多渠道支付封装方案,该设计采用策略模式实现各支付渠道(微信、支付宝、银联)的统一接口,通过工厂模式动态创建支付策略,感兴趣的可以了解一下
设计思路
使用工厂和策略模式设计一个可扩展的多渠道支付封装方案,支持微信、支付宝、银联等支付渠道。
- 策略模式:每个支付渠道实现统一的支付接口;
- 工厂模式:根据支付类型创建对应的支付策略;
- 配置化管理:支付参数通过配置文件管理;
- 统一响应:标准化支付返回结果。
设计优势:
- 开闭原则:新增支付渠道只需实现PayStrategy接口,无需修改现有代码;
- 统一入口:通过PayService提供统一的支付入口
- 配置化:支付参数通过配置文件管理,便于不同环境部署
- 异常处理:统一的异常处理机制
- 易于扩展:支持添加新的支付渠道和支付方式
- 维护性:代码结构清晰,易于维护和测试
实现代码
主要包含:支付相关枚举、支付请求和响应对象、支付策略接口、支付策略实现、支付策略工厂、统一支付服务。
依赖 pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.17</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>支付相关枚举定义
支付渠道 PayChannel.java
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum PayChannel {
WECHAT_PAY("wechat", "微信支付"),
ALI_PAY("alipay", "支付宝"),
UNION_PAY("union", "银联支付");
private String code;
private String name;
public static PayChannel getByCode(String code) {
for (PayChannel item : values()) {
if (item.getCode().equals(code)) {
return item;
}
}
return null;
}
}
支付状态 PayStatus.java
```java
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum PayStatus {
WAITING(0, "待支付"),
SUCCESS(1, "成功"),
FAILED(2, "失败"),
CANCELED(3, "已取消"),
REFUND(4, "已退款");
private Integer code;
private String desc;
public static PayStatus getByCode(String code) {
for (PayStatus item : values()) {
if (item.getCode().equals(code)) {
return item;
}
}
return null;
}
}交易类型 TradeType.java,根据支付渠道筛选可用的交易类型
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum TradeType {
APP("app", "APP支付"),
JSAPI("jsapi", "公众号/小程序支付"),
NATIVE("native", "扫码支付"),
H5("h5", "H5支付");
private String code;
private String desc;
public static TradeType getByCode(String code) {
for (TradeType item : values()) {
if (item.getCode().equals(code)) {
return item;
}
}
return null;
}
}交易方式 TradeMethod.java
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum TradeMethod {
PAY("pay", "支付"),
REFUND("refund", "退款");
private String code;
private String desc;
public static TradeMethod getByCode(String code) {
for (TradeMethod item : values()) {
if (item.getCode().equals(code)) {
return item;
}
}
return null;
}
}常量 Constants.java
public class Constants {
/**
* 操作状态
*/
public final static Integer OPERATE_SUCCESS_STATE = 200;
public final static String OPERATE_SUCCESS_MSG = "success";
public final static Integer OPERATE_ERROR_STATE = 500;
public final static String OPERATE_ERROR_MSG = "error";
}支付请求和响应对象
公共请求参数 BaseRequest.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BaseRequest implements Serializable {
/**
* appid
*/
private String appId;
/**
* 商户号
*/
private String mchId;
/**
* 商户证书序列号
*/
private String serialNo;
/**
* 商户API私钥
*/
private String privateKey;
/**
* 商户API公钥
*/
private String publicKey;
/**
* 商户APIV3密钥
*/
private String apiV3Key;
}关闭订单响应参数 CloseResponse.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CloseResponse implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 是否成功
*/
private Boolean success;
/**
* 订单号
*/
private String orderNo;
/**
* 错误码
*/
private String errorCode;
/**
* 错误信息
*/
private String errorMsg;
public static CloseResponse success(String orderNo) {
CloseResponse response = new CloseResponse();
response.setSuccess(true);
response.setOrderNo(orderNo);
return response;
}
public static CloseResponse failure(String errorCode, String errorMsg) {
CloseResponse response = new CloseResponse();
response.setSuccess(false);
response.setErrorCode(errorCode);
response.setErrorMsg(errorMsg);
return response;
}
}回调处理请求 NotifyRequest.java
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class NotifyRequest extends BaseRequest implements Serializable {
private static final long serialVersionUID=1L;
/**
* 支付渠道
*/
private PayChannel payChannel;
/**
* 交易方式
*/
private TradeMethod method;
/**
* 请求体
*/
private HttpServletRequest servletRequest;
}回调处理响应 NotifyResponse.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class NotifyResponse implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 是否成功
*/
private Boolean success;
/**
* 交易订单号
*/
private String orderNo;
/**
* 三方交易订单号
*/
private String transactionId;
/**
* 退款单号
*/
private String refundNo;
/**
* 退款三方订单号
*/
private String refundId;
/**
* 状态
*/
private PayStatus status;
/**
* 时间
*/
private LocalDateTime time;
/**
* 错误码
*/
private String errorCode;
/**
* 错误信息
*/
private String errorMsg;
public static NotifyResponse failure(String errorCode, String errorMsg) {
return NotifyResponse.builder().success(false).errorCode(errorCode).errorMsg(errorMsg).build();
}
}统一支付请求 PayRequest.java
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class PayRequest extends BaseRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 支付渠道
*/
private PayChannel payChannel;
/**
* 交易类型
*/
private TradeType tradeType;
/**
* 订单号
*/
private String orderNo;
/**
* 订单金额(单位:分)
*/
private BigDecimal amount;
/**
* 订单描述
*/
private String description;
/**
* 用户IP
*/
private String clientIp;
/**
* 过期时间
*/
private LocalDateTime expireTime;
/**
* 附加数据
*/
private String attach;
/**
* 通知地址
*/
private String notifyUrl;
/**
* 用户标识
*/
private String openId;
/**
* 场景类型:Wap、iOS、Android
*/
private String h5Type;
}统一支付响应 PayResponse.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PayResponse implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 是否成功
*/
private Boolean success;
/**
* 错误码
*/
private String errorCode;
/**
* 错误信息
*/
private String errorMsg;
/**
* 支付所需参数(前端调起支付所需)
*/
private Map<String, Object> payParams;
/**
* 预支付交易会话标识
*/
private String prepayId;
/**
* 支付跳转URL
*/
private String payUrl;
/**
* 二维码链接
*/
private String codeUrl;
public static PayResponse failure(String errorCode, String errorMsg) {
PayResponse response = new PayResponse();
response.setSuccess(false);
response.setErrorCode(errorCode);
response.setErrorMsg(errorMsg);
return response;
}
}查询响应 QueryResponse.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class QueryResponse implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 是否成功
*/
private Boolean success;
/**
* 订单号
*/
private String orderNo;
/**
* 三方交易订单号
*/
private String transactionId;
/**
* 状态
*/
private PayStatus payStatus;
/**
* 支付时间
*/
private LocalDateTime payTime;
/**
* 错误码
*/
private String errorCode;
/**
* 错误信息
*/
private String errorMsg;
public static QueryResponse failure(String errorCode, String errorMsg) {
QueryResponse response = new QueryResponse();
response.setSuccess(false);
response.setErrorCode(errorCode);
response.setErrorMsg(errorMsg);
return response;
}
}退款请求 RefundRequest.java
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class RefundRequest extends BaseRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 交易订单号
*/
private String orderNo;
/**
* 退款单号
*/
private String refundNo;
/**
* 交易金额
*/
private BigDecimal payAmount;
/**
* 退款金额
*/
private BigDecimal refundAmount;
/**
* 退款原因
*/
private String refundReason;
/**
* 退款回调地址
*/
private String notifyUrl;
}退款响应 RefundResponse.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RefundResponse implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 是否成功
*/
private Boolean success;
/**
* 交易订单号
*/
private String orderNo;
/**
* 退款单号
*/
private String refundNo;
/**
* 退款三方订单号
*/
private String refundId;
/**
* 状态
*/
private PayStatus refundStatus;
/**
* 退款时间
*/
private LocalDateTime refundTime;
/**
* 错误码
*/
private String errorCode;
/**
* 错误信息
*/
private String errorMsg;
public static RefundResponse failure(String errorCode, String errorMsg) {
return RefundResponse.builder().success(false).errorCode(errorCode).errorMsg(errorMsg).build();
}
}支付策略接口 PayStrategy.java
public interface PayStrategy {
/**
* 获取支付渠道
*/
PayChannel getPayChannel();
/**
* 统一支付
*/
PayResponse pay(PayRequest request);
/**
* 查询订单
*/
QueryResponse queryOrder(PayRequest request);
/**
* 关闭订单
*/
CloseResponse close(PayRequest request);
/**
* 退款
*/
RefundResponse refund(RefundRequest request);
/**
* 退款查询
*/
RefundResponse queryRefund(RefundRequest request);
/**
* 处理回调
*/
NotifyResponse handleNotify(NotifyRequest request);
}支付策略实现 AbstractPayStrategy.java
抽象支付策略
@Slf4j
public abstract class AbstractPayStrategy implements PayStrategy {
@Override
public PayResponse pay(PayRequest request) {
try {
log.info("开始支付,渠道:{},订单号:{}", getPayChannel().getName(), request.getOrderNo());
return doPay(request);
} catch (Exception e) {
log.error("支付异常,渠道:{},订单号:{}", getPayChannel().getName(), request.getOrderNo(), e);
return PayResponse.failure(Constants.OPERATE_ERROR_MSG, "支付处理异常:" + e.getMessage());
}
}
@Override
public QueryResponse queryOrder(PayRequest request) {
try {
log.info("查询订单,渠道:{},订单号:{}", getPayChannel().getName(), request.getOrderNo());
return doQueryOrder(request);
} catch (Exception e) {
log.error("查询订单异常,渠道:{},订单号:{}", getPayChannel().getName(), request.getOrderNo(), e);
return QueryResponse.failure(Constants.OPERATE_ERROR_MSG, "查询订单异常:" + e.getMessage());
}
}
@Override
public CloseResponse close(PayRequest request) {
try {
log.info("关闭订单,渠道:{},订单号:{}", getPayChannel().getName(), request.getOrderNo());
return doClose(request);
} catch (Exception e) {
log.error("关闭订单异常,渠道:{},订单号:{}", getPayChannel().getName(), request.getOrderNo(), e);
return CloseResponse.failure(Constants.OPERATE_ERROR_MSG, "关闭订单异常:" + e.getMessage());
}
}
@Override
public RefundResponse refund(RefundRequest request) {
try {
log.info("开始退款,渠道:{},退款单号:{}", getPayChannel().getName(), request.getRefundNo());
return doRefund(request);
} catch (Exception e) {
log.error("退款异常,渠道:{},退款单号:{}", getPayChannel().getName(), request.getRefundNo(), e);
return RefundResponse.failure(Constants.OPERATE_ERROR_MSG, "退款处理异常:" + e.getMessage());
}
}
@Override
public RefundResponse queryRefund(RefundRequest request) {
try {
log.info("开始退款查询,渠道:{},退款单号:{}", getPayChannel().getName(), request.getRefundNo());
return doQueryRefund(request);
} catch (Exception e) {
log.error("退款查询异常,渠道:{},退款单号:{}", getPayChannel().getName(), request.getRefundNo(), e);
return RefundResponse.failure(Constants.OPERATE_ERROR_MSG, "退款查询处理异常:" + e.getMessage());
}
}
protected abstract PayResponse doPay(PayRequest request);
protected abstract QueryResponse doQueryOrder(PayRequest request);
protected abstract CloseResponse doClose(PayRequest request);
protected abstract RefundResponse doRefund(RefundRequest request);
protected abstract RefundResponse doQueryRefund(RefundRequest request);
}各渠道实现
主要以微信支付为列,实现支付、退款、查询、关闭订单、支付退款回调等功能。
微信支付实现 WechatPayStrategy.java
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.app.AppServiceExtension;
import com.wechat.pay.java.service.payments.app.model.CloseOrderRequest;
import com.wechat.pay.java.service.payments.app.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.h5.H5Service;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Slf4j
@Component
public class WechatPayStrategy extends AbstractPayStrategy {
@Override
protected PayResponse doPay(PayRequest request) {
try {
// 初始化商户配置
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(request.getMchId())
.privateKey(request.getPrivateKey())
.merchantSerialNumber(request.getSerialNo())
.apiV3Key(request.getApiV3Key())
.build();
switch (request.getTradeType()) {
case TradeType.APP:
com.wechat.pay.java.service.payments.app.model.PrepayRequest appRrq = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
appRrq.setAppid(request.getAppId());
appRrq.setMchid(request.getMchId());
appRrq.setOutTradeNo(request.getOrderNo());
appRrq.setDescription(request.getDescription());
appRrq.setAttach(request.getAttach());
appRrq.setNotifyUrl(request.getNotifyUrl());
com.wechat.pay.java.service.payments.app.model.Amount appAmount = new com.wechat.pay.java.service.payments.app.model.Amount();
appAmount.setCurrency("CNY");
appAmount.setTotal(request.getAmount().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).intValue());
appRrq.setAmount(appAmount);
if (Objects.nonNull(request.getExpireTime())) {
appRrq.setTimeExpire(request.getExpireTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
}
log.info("APP支付,请求参数:{}", appRrq);
com.wechat.pay.java.service.payments.app.model.PrepayWithRequestPaymentResponse appRes = new AppServiceExtension.Builder().config(config).build().prepayWithRequestPayment(appRrq);
log.info("APP支付,返回参数:{}", appRes);
if (Objects.nonNull(appRes)) {
Map<String, Object> appParams = new HashMap<>();
appParams.put("appid", appRes.getAppid());
appParams.put("partnerid", appRes.getPartnerId());
appParams.put("prepayid", appRes.getPrepayId());
appParams.put("package", appRes.getPackageVal());
appParams.put("noncestr", appRes.getNonceStr());
appParams.put("timestamp", appRes.getTimestamp());
appParams.put("sign", appRes.getSign());
return PayResponse.builder().success(true).payParams(appParams).prepayId(appRes.getPrepayId()).build();
}
return PayResponse.failure(Constants.OPERATE_ERROR_MSG, Constants.OPERATE_ERROR_MSG);
case TradeType.JSAPI:
com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest jsapiReq = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
jsapiReq.setAppid(request.getAppId());
jsapiReq.setMchid(request.getMchId());
jsapiReq.setOutTradeNo(request.getOrderNo());
jsapiReq.setDescription(request.getDescription());
jsapiReq.setAttach(request.getAttach());
jsapiReq.setNotifyUrl(request.getNotifyUrl());
com.wechat.pay.java.service.payments.jsapi.model.Amount jsapiAmount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
jsapiAmount.setCurrency("CNY");
jsapiAmount.setTotal(request.getAmount().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).intValue());
jsapiReq.setAmount(jsapiAmount);
com.wechat.pay.java.service.payments.jsapi.model.Payer jsapiPayer = new com.wechat.pay.java.service.payments.jsapi.model.Payer();
jsapiPayer.setOpenid(request.getOpenId());
jsapiReq.setPayer(jsapiPayer);
if (Objects.nonNull(request.getExpireTime())) {
jsapiReq.setTimeExpire(request.getExpireTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
}
log.info("JSAPI支付,请求参数:{}", jsapiReq);
com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse jsapiRes = new JsapiServiceExtension.Builder().config(config).build().prepayWithRequestPayment(jsapiReq);
log.info("JSAPI支付,返回参数:{}", jsapiRes);
if (Objects.nonNull(jsapiRes)) {
Map<String, Object> jsapiParams = new HashMap<>();
jsapiParams.put("appId", jsapiRes.getAppId());
jsapiParams.put("paySign", jsapiRes.getPaySign());
jsapiParams.put("signType", jsapiRes.getSignType());
jsapiParams.put("package", jsapiRes.getPackageVal());
jsapiParams.put("nonceStr", jsapiRes.getNonceStr());
jsapiParams.put("timeStamp", jsapiRes.getTimeStamp());
return PayResponse.builder().success(true).payParams(jsapiParams).build();
}
return PayResponse.failure(Constants.OPERATE_ERROR_MSG, Constants.OPERATE_ERROR_MSG);
case TradeType.NATIVE:
com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest nativeReq = new com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
nativeReq.setAppid(request.getAppId());
nativeReq.setMchid(request.getMchId());
nativeReq.setOutTradeNo(request.getOrderNo());
nativeReq.setDescription(request.getDescription());
nativeReq.setAttach(request.getAttach());
nativeReq.setNotifyUrl(request.getNotifyUrl());
com.wechat.pay.java.service.payments.nativepay.model.Amount nativeAmount = new com.wechat.pay.java.service.payments.nativepay.model.Amount();
nativeAmount.setCurrency("CNY");
nativeAmount.setTotal(request.getAmount().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).intValue());
nativeReq.setAmount(nativeAmount);
if (Objects.nonNull(request.getExpireTime())) {
nativeReq.setTimeExpire(request.getExpireTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
}
log.info("NATIVE支付,请求参数:{}", nativeReq);
com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse nativeRes = new NativePayService.Builder().config(config).build().prepay(nativeReq);
log.info("NATIVE支付,返回参数:{}", nativeRes);
if (Objects.nonNull(nativeRes)) {
return PayResponse.builder().success(true).codeUrl(nativeRes.getCodeUrl()).build();
}
case TradeType.H5:
com.wechat.pay.java.service.payments.h5.model.PrepayRequest h5Req = new com.wechat.pay.java.service.payments.h5.model.PrepayRequest();
h5Req.setAppid(request.getAppId());
h5Req.setMchid(request.getMchId());
h5Req.setOutTradeNo(request.getOrderNo());
h5Req.setDescription(request.getDescription());
h5Req.setAttach(request.getAttach());
h5Req.setNotifyUrl(request.getNotifyUrl());
com.wechat.pay.java.service.payments.h5.model.Amount h5Amount = new com.wechat.pay.java.service.payments.h5.model.Amount();
h5Amount.setCurrency("CNY");
h5Amount.setTotal(request.getAmount().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).intValue());
h5Req.setAmount(h5Amount);
com.wechat.pay.java.service.payments.h5.model.SceneInfo h5Scene = new com.wechat.pay.java.service.payments.h5.model.SceneInfo();
h5Scene.setPayerClientIp(request.getClientIp());
com.wechat.pay.java.service.payments.h5.model.H5Info h5Info = new com.wechat.pay.java.service.payments.h5.model.H5Info();
h5Info.setType(request.getH5Type());
h5Scene.setH5Info(h5Info);
h5Req.setSceneInfo(h5Scene);
if (Objects.nonNull(request.getExpireTime())) {
h5Req.setTimeExpire(request.getExpireTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
}
log.info("H5支付,请求参数:{}", h5Req);
com.wechat.pay.java.service.payments.h5.model.PrepayResponse h5Re s= new H5Service.Builder().config(config).build().prepay(h5Req);
log.info("H5支付,返回参数:{}", h5Res);
if (Objects.nonNull(h5Res)) {
return PayResponse.builder().success(true).codeUrl(h5Res.getH5Url()).build();
}
default:
return PayResponse.failure(Constants.OPERATE_ERROR_MSG, "不支持的微信交易类型");
}
} catch (Exception e) {
log.error("微信支付失败: {}", e);
return PayResponse.failure(Constants.OPERATE_ERROR_MSG, e.getMessage());
}
}
@Override
protected QueryResponse doQueryOrder(PayRequest request) {
try {
QueryOrderByOutTradeNoRequest req = new QueryOrderByOutTradeNoRequest();
req.setMchid(request.getMchId());
req.setOutTradeNo(request.getOrderNo());
log.info("订单开始,请求参数:{}", req);
Transaction res = new AppServiceExtension.Builder().config(
new RSAAutoCertificateConfig.Builder()
.merchantId(request.getMchId())
.privateKey(request.getPrivateKey())
.merchantSerialNumber(request.getSerialNo())
.apiV3Key(request.getApiV3Key())
.build()
).build().queryOrderByOutTradeNo(req);
log.info("订单结束,返回参数:{}", res);
if (Objects.nonNull(res)) {
PayStatus payStatus = PayStatus.WAITING;
LocalDateTime payTime = null;
if (Transaction.TradeStateEnum.SUCCESS == res.getTradeState()) {
// 成功
payTime = LocalDateTime.parse(res.getSuccessTime());
payStatus = PayStatus.SUCCESS;
} else if (Transaction.TradeStateEnum.NOTPAY == res.getTradeState() || Transaction.TradeStateEnum.USERPAYING == res.getTradeState()) {
// 未支付、支付中
payStatus = PayStatus.WAITING;
} else if (Transaction.TradeStateEnum.CLOSED == res.getTradeState() || Transaction.TradeStateEnum.REVOKED == res.getTradeState()) {
// 已关闭、已撤销
payStatus = PayStatus.CANCELED;
} else if (Transaction.TradeStateEnum.PAYERROR == res.getTradeState()) {
// 失败
payStatus = PayStatus.FAILED;
}
return QueryResponse.builder()
.success(true)
.orderNo(res.getOutTradeNo())
.transactionId(res.getTransactionId())
.payStatus(payStatus)
.payTime(payTime)
.build();
}
return QueryResponse.failure(Constants.OPERATE_ERROR_MSG, "订单查询失败");
} catch (Exception e) {
log.error("微信支付查询失败: {}", e);
return QueryResponse.failure(Constants.OPERATE_ERROR_MSG, e.getMessage());
}
}
@Override
protected CloseResponse doClose(PayRequest request) {
try {
CloseOrderRequest req = new CloseOrderRequest();
req.setMchid(request.getMchId());
req.setOutTradeNo(request.getOrderNo());
log.info("关闭订单开始,请求参数:{}", req);
new AppServiceExtension.Builder().config(
new RSAAutoCertificateConfig.Builder()
.merchantId(request.getMchId())
.privateKey(request.getPrivateKey())
.merchantSerialNumber(request.getSerialNo())
.apiV3Key(request.getApiV3Key())
.build()
).build().closeOrder(req);
log.info("关闭订单结束");
return CloseResponse.success(request.getOrderNo());
} catch (Exception e) {
log.error("微信关闭订单失败: {}", e);
return CloseResponse.failure(Constants.OPERATE_ERROR_MSG, e.getMessage());
}
}
@Override
protected RefundResponse doRefund(RefundRequest request) {
try {
CreateRequest req = new CreateRequest();
req.setOutTradeNo(request.getOrderNo());
AmountReq amountReq = new AmountReq();
amountReq.setTotal(request.getPayAmount().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).longValue());
amountReq.setRefund(request.getRefundAmount().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).longValue());
amountReq.setCurrency("CNY");
req.setAmount(amountReq);
req.setOutRefundNo(request.getRefundNo());
req.setReason(request.getRefundReason());
req.setNotifyUrl(request.getNotifyUrl());
log.info("退款开始,请求参数:{}", req);
Refund res = new RefundService.Builder().config(
new RSAAutoCertificateConfig.Builder()
.merchantId(request.getMchId())
.privateKey(request.getPrivateKey())
.merchantSerialNumber(request.getSerialNo())
.apiV3Key(request.getApiV3Key())
.build()
).build().create(req);
log.info("退款结束,返回参数:{}", res);
if (Objects.nonNull(res)) {
return RefundResponse.builder().success(true).refundNo(res.getOutRefundNo()).build();
}
return RefundResponse.failure(Constants.OPERATE_ERROR_MSG, "退款失败");
} catch (Exception e) {
log.error("微信退款失败: {}", e);
return RefundResponse.failure(Constants.OPERATE_ERROR_MSG, e.getMessage());
}
}
@Override
protected RefundResponse doQueryRefund(RefundRequest request) {
try {
QueryByOutRefundNoRequest req = new QueryByOutRefundNoRequest();
req.setOutRefundNo(request.getRefundNo());
log.info("退款查询开始,请求参数:{}", req);
Refund res = new RefundService.Builder().config(
new RSAAutoCertificateConfig.Builder()
.merchantId(request.getMchId())
.privateKey(request.getPrivateKey())
.merchantSerialNumber(request.getSerialNo())
.apiV3Key(request.getApiV3Key())
.build()
).build().queryByOutRefundNo(req);
log.info("退款查询结束,返回参数:{}", res);
if (Objects.nonNull(res)) {
PayStatus payStatus = PayStatus.WAITING;
BigDecimal amount = null;
LocalDateTime refundTime = null;
if (Status.SUCCESS == res.getStatus()) {
// 成功
refundTime = LocalDateTime.parse(res.getSuccessTime());
payStatus = PayStatus.SUCCESS;
} else if (Status.PROCESSING == res.getStatus()) {
// 处理中
payStatus = PayStatus.WAITING;
} else if (Status.CLOSED == res.getStatus()) {
// 已关闭
payStatus = PayStatus.CANCELED;
} else if (Status.ABNORMAL == res.getStatus()) {
// 失败
payStatus = PayStatus.FAILED;
}
return RefundResponse.builder()
.success(true)
.orderNo(res.getOutTradeNo())
.refundNo(res.getOutRefundNo())
.refundId(res.getRefundId())
.refundStatus(payStatus)
.refundTime(refundTime)
.build();
}
return RefundResponse.failure(Constants.OPERATE_ERROR_MSG, "退款查询失败");
} catch (Exception e) {
log.error("微信退款查询失败: {}", e);
return RefundResponse.failure(Constants.OPERATE_ERROR_MSG, e.getMessage());
}
}
@Override
public NotifyResponse handleNotify(NotifyRequest request) {
try {
log.info("微信回调处理开始...");
HttpServletRequest servletRequest = request.getServletRequest();
// 读取请求体的信息
ServletInputStream inputStream = servletRequest.getInputStream();
StringBuffer stringBuffer = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s;
while ((s = bufferedReader.readLine()) != null) {
stringBuffer.append(s);
}
log.info("微信回调处理,请求体参数:{}", stringBuffer);
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(
new RSAAutoCertificateConfig.Builder()
.merchantId(request.getMchId())
.privateKey(request.getPrivateKey())
.merchantSerialNumber(request.getSerialNo())
.apiV3Key(request.getApiV3Key())
.build()
);
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(servletRequest.getHeader("Wechatpay-Serial"))
.nonce(servletRequest.getHeader("Wechatpay-Nonce"))
.signature(servletRequest.getHeader("Wechatpay-Signature"))
.timestamp(servletRequest.getHeader("Wechatpay-Timestamp"))
.signType(servletRequest.getHeader("Wechatpay-Signature-Type"))
.body(stringBuffer.toString())
.build();
log.info("微信回调处理,RequestParam:{}", requestParam);
switch (request.getMethod()) {
case TradeMethod.PAY:
Transaction transaction = parser.parse(requestParam, Transaction.class);
log.info("微信支付回调返回参数:{}", transaction);
NotifyResponse payResponse = NotifyResponse.builder().success(true).orderNo(transaction.getOutTradeNo()).build();
if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
// SUCCESS:支付成功
payResponse.setTransactionId(transaction.getTransactionId());
payResponse.setTime(LocalDateTime.parse(transaction.getSuccessTime()));
payResponse.setStatus(PayStatus.SUCCESS);
} else if (Transaction.TradeStateEnum.CLOSED == transaction.getTradeState() || Transaction.TradeStateEnum.REVOKED == transaction.getTradeState() || Transaction.TradeStateEnum.PAYERROR == transaction.getTradeState()) {
// CLOSED:已关闭、REVOKED:已撤销、PAYERROR:支付失败
payResponse.setStatus(PayStatus.FAILED);
} else {
// REFUND:转入退款、NOTPAY:未支付、USERPAYING:用户支付中
log.info("支付中的暂不处理:{}", transaction);
}
log.info("微信支付回调结束:{}", payResponse);
return payResponse;
case TradeMethod.REFUND:
RefundNotification refund = parser.parse(requestParam, RefundNotification.class);
log.info("微信退款回调返回参数:{}", refund);
NotifyResponse refundResponse = NotifyResponse.builder().success(true).refundNo(refund.getOutRefundNo()).build();
if (Status.SUCCESS == refund.getRefundStatus()) {
// SUCCESS:退款成功
refundResponse.setRefundId(refund.getRefundId());
refundResponse.setOrderNo(refund.getOutTradeNo());
refundResponse.setTransactionId(refund.getTransactionId());
refundResponse.setTime(LocalDateTime.parse(refund.getSuccessTime()));
refundResponse.setStatus(PayStatus.SUCCESS);
} else {
// ABNORMAL:退款异常、CLOSED:退款关闭
refundResponse.setRefundId(refund.getRefundId());
refundResponse.setOrderNo(refund.getOutTradeNo());
refundResponse.setTransactionId(refund.getTransactionId());
refundResponse.setTime(LocalDateTime.parse(refund.getCreateTime()));
refundResponse.setStatus(PayStatus.FAILED);
}
log.info("微信退款回调结束:{}", refundResponse);
return refundResponse;
default:
return NotifyResponse.failure(Constants.OPERATE_ERROR_MSG, "不支持的回调方式");
}
} catch (Exception e) {
log.error("微信回调处理失败: {}", e);
return NotifyResponse.failure(Constants.OPERATE_ERROR_MSG, e.getMessage());
}
}
@Override
public PayChannel getPayChannel() {
return PayChannel.WECHAT_PAY;
}
}支付宝支付实现 AlipayStrategy.java
@Slf4j
@Component
public class AlipayStrategy extends AbstractPayStrategy {
@Override
protected PayResponse doPay(PayRequest request) {
try {
// TODO 业务实现
return null;
} catch (Exception e) {
throw new RuntimeException("支付宝支付失败", e);
}
}
@Override
protected QueryResponse doQueryOrder(PayRequest request) {
return null;
}
@Override
protected CloseResponse doClose(PayRequest request) {
return null;
}
@Override
protected RefundResponse doRefund(RefundRequest request) {
return null;
}
@Override
protected RefundResponse doQueryRefund(RefundRequest request) {
return null;
}
@Override
public PayChannel getPayChannel() {
return PayChannel.ALI_PAY;
}
@Override
public NotifyResponse handleNotify(NotifyRequest request) {
return null;
}
}银联支付实现 UnionPayStrategy.java
@Slf4j
@Component
public class UnionPayStrategy extends AbstractPayStrategy {
@Override
protected PayResponse doPay(PayRequest request) {
try {
// TODO 业务实现
return null;
} catch (Exception e) {
throw new RuntimeException("银联支付失败", e);
}
}
@Override
protected QueryResponse doQueryOrder(PayRequest request) {
return null;
}
@Override
protected CloseResponse doClose(PayRequest request) {
return null;
}
@Override
protected RefundResponse doRefund(RefundRequest request) {
return null;
}
@Override
protected RefundResponse doQueryRefund(RefundRequest request) {
return null;
}
@Override
public PayChannel getPayChannel() {
return PayChannel.UNION_PAY;
}
@Override
public NotifyResponse handleNotify(NotifyRequest request) {
return null;
}
}支付策略工厂 PayStrategyFactory.java
@Component
public class PayStrategyFactory {
private final Map<PayChannel, PayStrategy> strategyMap = new ConcurrentHashMap<>();
public PayStrategyFactory(List<PayStrategy> strategies) {
for (PayStrategy strategy : strategies) {
strategyMap.put(strategy.getPayChannel(), strategy);
}
}
public PayStrategy getStrategy(PayChannel payChannel) {
PayStrategy strategy = strategyMap.get(payChannel);
if (strategy == null) {
throw new RuntimeException("不支持的支付渠道:" + payChannel.getName());
}
return strategy;
}
public PayStrategy getStrategy(String channelCode) {
PayChannel payChannel = PayChannel.getByCode(channelCode);
if (payChannel == null) {
throw new RuntimeException("不支持的支付渠道代码:" + channelCode);
}
return getStrategy(payChannel);
}
}统一支付服务 PayService.java
@Slf4j
@Service
public class PayService {
@Autowired
private PayStrategyFactory payStrategyFactory;
/**
* @Description: 支付
* @Date: 2025/11/21
* @Param: [request]
**/
public PayResponse pay(PayRequest request) {
PayStrategy strategy = payStrategyFactory.getStrategy(request.getPayChannel());
return strategy.pay(request);
}
/**
* @Description: 查询订单
* @Date: 2025/11/21
* @Param: [request]
**/
public QueryResponse queryOrder(PayRequest request) {
PayStrategy strategy = payStrategyFactory.getStrategy(request.getPayChannel());
return strategy.queryOrder(request);
}
/**
* @Description: 关闭订单
* @Date: 2025/11/21
* @Param: [request]
**/
public CloseResponse close(PayRequest request) {
PayStrategy strategy = payStrategyFactory.getStrategy(request.getPayChannel());
return strategy.close(request);
}
/**
* @Description: 退款
* @Date: 2025/11/21
* @Param: [payChannel, request]
**/
public RefundResponse refund(PayChannel payChannel, RefundRequest request) {
PayStrategy strategy = payStrategyFactory.getStrategy(payChannel);
return strategy.refund(request);
}
/**
* @Description: 退款查询
* @Date: 2025/11/21
* @Param: [payChannel, request]
**/
public RefundResponse queryRefund(PayChannel payChannel, RefundRequest request) {
PayStrategy strategy = payStrategyFactory.getStrategy(payChannel);
return strategy.queryRefund(request);
}
/**
* @Description: 处理回调
* @Date: 2025/11/21
* @Param: [request]
**/
public NotifyResponse handleNotify(NotifyRequest request) {
PayStrategy strategy = payStrategyFactory.getStrategy(request.getPayChannel());
return strategy.handleNotify(request);
}
}使用示例
@Service
public class OrderService {
@Autowired
private PayService payService;
public void createOrderAndPay() {
// 创建支付请求
PayRequest request = new PayRequest();
// 公共参数
request.setAppId("wxd678efxxxxxxxxxxxxxxxxx");
request.setMchId("123xxxxxxxx");
request.setSerialNo("1DDE55AD98Exxxxxxxxxx");
request.setPrivateKey("XXXXXXXXXXXXXXXXXXXXXXXXX");
request.setPublicKey("XXXXXXXXXXXXXXXXXXXXXXXXXX");
request.setApiV3Key("XXXXXXXXXXXXXXXXXXXXXX");
// 订单参数
request.setPayChannel(PayChannel.WECHAT_PAY);
request.setTradeType(TradeType.APP);
request.setOrderNo("ORDER_" + System.currentTimeMillis());
request.setAmount(100); // 1元
request.setDescription("测试商品");
request.setClientIp("127.0.0.1");
// 发起支付
PayResponse response = payService.pay(request);
if (response.isSuccess()) {
// 支付创建成功,返回支付参数给前端
System.out.println("支付参数:" + response.getPayParams());
} else {
// 处理支付失败
System.out.println("支付失败:" + response.getErrorMsg());
}
}
}到此这篇关于SpringBoot 支付渠道实现统一的支付服务的文章就介绍到这了,更多相关SpringBoot 支付渠道统一支付内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
