需求分析
- 提供统一的支付接口,支持支付宝和微信支付
- 基于Spring Boot自动配置机制,简化集成过程
- 支持配置化管理支付参数
- 易于扩展新的支付方式
- 提供完整的支付流程支持(下单、查询、退款等)
设计方案
整体架构设计
- 定义统一支付接口,屏蔽不同支付平台差异
- 采用策略模式支持多种支付平台
- 利用Spring Boot自动配置实现零配置启动
- 通过配置属性管理各支付平台参数
核心组件设计
PaymentService: 统一支付服务接口PaymentProperties: 配置属性类PaymentAutoConfiguration: 自动配置类- 各支付平台具体实现类
完整实现代码
Maven配置
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>payment-spring-boot-starter</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.boot.version>2.7.0</spring.boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.10</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies>
</project>
核心枚举和常量
// src/main/java/com/example/payment/enums/PaymentType.java
package com.example.payment.enums;
/**
* 支付类型枚举
*/
public enum PaymentType {
ALIPAY("alipay", "支付宝"),
WECHAT_PAY("wechat", "微信支付");
private final String code;
private final String name;
PaymentType(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
}
// src/main/java/com/example/payment/enums/PaymentStatus.java
package com.example.payment.enums;
/**
* 支付状态枚举
*/
public enum PaymentStatus {
SUCCESS("success", "支付成功"),
FAILED("failed", "支付失败"),
PROCESSING("processing", "支付处理中"),
CLOSED("closed", "交易关闭"),
REFUND("refund", "已退款");
private final String code;
private final String desc;
PaymentStatus(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
统一数据模型
// src/main/java/com/example/payment/model/PaymentRequest.java
package com.example.payment.model;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Map;
/**
* 支付请求对象
*/
@Data
public class PaymentRequest {
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 支付金额
*/
private BigDecimal totalAmount;
/**
* 商品标题
*/
private String subject;
/**
* 支付平台类型
*/
private String paymentType;
/**
* 客户端IP
*/
private String clientIp;
/**
* 回调地址
*/
private String notifyUrl;
/**
* 返回地址
*/
private String returnUrl;
/**
* 扩展参数
*/
private Map<String, Object> extendParams;
public static PaymentRequestBuilder builder() {
return new PaymentRequestBuilder();
}
public static class PaymentRequestBuilder {
private PaymentRequest request = new PaymentRequest();
public PaymentRequestBuilder outTradeNo(String outTradeNo) {
request.setOutTradeNo(outTradeNo);
return this;
}
public PaymentRequestBuilder totalAmount(BigDecimal totalAmount) {
request.setTotalAmount(totalAmount);
return this;
}
public PaymentRequestBuilder subject(String subject) {
request.setSubject(subject);
return this;
}
public PaymentRequestBuilder paymentType(String paymentType) {
request.setPaymentType(paymentType);
return this;
}
public PaymentRequestBuilder clientIp(String clientIp) {
request.setClientIp(clientIp);
return this;
}
public PaymentRequestBuilder notifyUrl(String notifyUrl) {
request.setNotifyUrl(notifyUrl);
return this;
}
public PaymentRequestBuilder returnUrl(String returnUrl) {
request.setReturnUrl(returnUrl);
return this;
}
public PaymentRequestBuilder extendParams(Map<String, Object> extendParams) {
request.setExtendParams(extendParams);
return this;
}
public PaymentRequest build() {
return request;
}
}
}
// src/main/java/com/example/payment/model/PaymentResponse.java
package com.example.payment.model;
import com.example.payment.enums.PaymentStatus;
import lombok.Data;
/**
* 支付响应对象
*/
@Data
public class PaymentResponse {
/**
* 是否成功
*/
private boolean success;
/**
* 支付状态
*/
private PaymentStatus status;
/**
* 错误码
*/
private String errorCode;
/**
* 错误消息
*/
private String errorMsg;
/**
* 平台交易号
*/
private String tradeNo;
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 支付凭证(如支付宝表单、微信预支付ID等)
*/
private String payData;
/**
* 其他数据
*/
private Object data;
}
// src/main/java/com/example/payment/model/QueryRequest.java
package com.example.payment.model;
import lombok.Data;
/**
* 查询请求对象
*/
@Data
public class QueryRequest {
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 平台交易号
*/
private String tradeNo;
/**
* 支付平台类型
*/
private String paymentType;
}
// src/main/java/com/example/payment/model/QueryResponse.java
package com.example.payment.model;
import com.example.payment.enums.PaymentStatus;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 查询响应对象
*/
@Data
public class QueryResponse {
/**
* 是否成功
*/
private boolean success;
/**
* 支付状态
*/
private PaymentStatus status;
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 平台交易号
*/
private String tradeNo;
/**
* 交易金额
*/
private BigDecimal totalAmount;
/**
* 实际支付金额
*/
private BigDecimal buyerPayAmount;
/**
* 交易创建时间
*/
private Date createTime;
/**
* 交易支付时间
*/
private Date payTime;
}
// src/main/java/com/example/payment/model/RefundRequest.java
package com.example.payment.model;
import lombok.Data;
import java.math.BigDecimal;
/**
* 退款请求对象
*/
@Data
public class RefundRequest {
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 平台交易号
*/
private String tradeNo;
/**
* 退款金额
*/
private BigDecimal refundAmount;
/**
* 退款原因
*/
private String refundReason;
/**
* 退款订单号
*/
private String outRefundNo;
/**
* 支付平台类型
*/
private String paymentType;
}
// src/main/java/com/example/payment/model/RefundResponse.java
package com.example.payment.model;
import lombok.Data;
import java.math.BigDecimal;
/**
* 退款响应对象
*/
@Data
public class RefundResponse {
/**
* 是否成功
*/
private boolean success;
/**
* 错误码
*/
private String errorCode;
/**
* 错误消息
*/
private String errorMsg;
/**
* 退款订单号
*/
private String outRefundNo;
/**
* 平台退款交易号
*/
private String refundId;
/**
* 退款金额
*/
private BigDecimal refundAmount;
/**
* 退款状态
*/
private String refundStatus;
}
核心接口定义
// src/main/java/com/example/payment/service/PaymentService.java
package com.example.payment.service;
import com.example.payment.model.*;
/**
* 统一支付服务接口
*/
public interface PaymentService {
/**
* 发起支付
* @param request 支付请求
* @return 支付响应
*/
PaymentResponse pay(PaymentRequest request);
/**
* 查询支付状态
* @param request 查询请求
* @return 查询响应
*/
QueryResponse query(QueryRequest request);
/**
* 退款
* @param request 退款请求
* @return 退款响应
*/
RefundResponse refund(RefundRequest request);
/**
* 关闭交易
* @param request 查询请求
* @return 查询响应
*/
QueryResponse close(QueryRequest request);
}
配置属性类
// src/main/java/com/example/payment/config/PaymentProperties.java
package com.example.payment.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.Map;
/**
* 支付配置属性
*/
@Data
@ConfigurationProperties(prefix = "payment")
public class PaymentProperties {
/**
* 各支付平台配置
*/
private Map<String, PlatformConfig> platforms = new HashMap<>();
/**
* 默认支付平台
*/
private String defaultPlatform = "alipay";
/**
* 是否启用支付功能
*/
private boolean enabled = true;
@Data
public static class PlatformConfig {
/**
* 应用ID
*/
private String appId;
/**
* 商户ID
*/
private String merchantId;
/**
* 私钥
*/
private String privateKey;
/**
* 公钥
*/
private String publicKey;
/**
* 网关地址
*/
private String gatewayUrl;
/**
* 通知地址
*/
private String notifyUrl;
/**
* 返回地址
*/
private String returnUrl;
/**
* 编码格式
*/
private String charset = "UTF-8";
/**
* 签名类型
*/
private String signType = "RSA2";
/**
* 是否沙箱环境
*/
private boolean sandbox = false;
}
}
异常处理
// src/main/java/com/example/payment/exception/PaymentException.java
package com.example.payment.exception;
/**
* 支付异常类
*/
public class PaymentException extends RuntimeException {
private String errorCode;
public PaymentException(String message) {
super(message);
}
public PaymentException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public PaymentException(String message, Throwable cause) {
super(message, cause);
}
public PaymentException(String errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
public String getErrorCode() {
return errorCode;
}
}
支付宝实现
// src/main/java/com/example/payment/service/impl/AliPaymentService.java
package com.example.payment.service.impl;
import com.alibaba.fastjson.JSON;
import com.example.payment.enums.PaymentStatus;
import com.example.payment.enums.PaymentType;
import com.example.payment.exception.PaymentException;
import com.example.payment.model.*;
import com.example.payment.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 支付宝支付实现
*/
@Slf4j
public class AliPaymentService implements PaymentService {
private final String appId;
private final String privateKey;
private final String publicKey;
private final String gatewayUrl;
private final boolean sandbox;
public AliPaymentService(String appId, String privateKey, String publicKey,
String gatewayUrl, boolean sandbox) {
this.appId = appId;
this.privateKey = privateKey;
this.publicKey = publicKey;
this.gatewayUrl = gatewayUrl;
this.sandbox = sandbox;
}
@Override
public PaymentResponse pay(PaymentRequest request) {
PaymentResponse response = new PaymentResponse();
try {
// 构造支付宝请求参数
Map<String, String> params = buildPayParams(request);
// 模拟调用支付宝接口
String result = callAlipayApi("alipay.trade.page.pay", params);
response.setSuccess(true);
response.setStatus(PaymentStatus.PROCESSING);
response.setOutTradeNo(request.getOutTradeNo());
response.setPayData(result); // 实际应返回表单HTML
log.info("支付宝支付请求成功,订单号:{}", request.getOutTradeNo());
} catch (Exception e) {
log.error("支付宝支付失败,订单号:{}", request.getOutTradeNo(), e);
response.setSuccess(false);
response.setErrorMsg(e.getMessage());
}
return response;
}
@Override
public QueryResponse query(QueryRequest request) {
QueryResponse response = new QueryResponse();
try {
Map<String, String> params = new HashMap<>();
params.put("out_trade_no", request.getOutTradeNo());
params.put("trade_no", request.getTradeNo());
// 模拟调用支付宝查询接口
String result = callAlipayApi("alipay.trade.query", params);
// 解析结果
response.setSuccess(true);
response.setOutTradeNo(request.getOutTradeNo());
response.setStatus(PaymentStatus.SUCCESS);
response.setTotalAmount(new BigDecimal("100.00"));
log.info("支付宝查询成功,订单号:{}", request.getOutTradeNo());
} catch (Exception e) {
log.error("支付宝查询失败,订单号:{}", request.getOutTradeNo(), e);
response.setSuccess(false);
}
return response;
}
@Override
public RefundResponse refund(RefundRequest request) {
RefundResponse response = new RefundResponse();
try {
Map<String, String> params = new HashMap<>();
params.put("out_trade_no", request.getOutTradeNo());
params.put("trade_no", request.getTradeNo());
params.put("refund_amount", request.getRefundAmount().toString());
params.put("refund_reason", request.getRefundReason());
params.put("out_request_no", StringUtils.hasText(request.getOutRefundNo())
? request.getOutRefundNo() : UUID.randomUUID().toString());
// 模拟调用支付宝退款接口
String result = callAlipayApi("alipay.trade.refund", params);
response.setSuccess(true);
response.setOutRefundNo(params.get("out_request_no"));
response.setRefundAmount(request.getRefundAmount());
log.info("支付宝退款成功,订单号:{}", request.getOutTradeNo());
} catch (Exception e) {
log.error("支付宝退款失败,订单号:{}", request.getOutTradeNo(), e);
response.setSuccess(false);
response.setErrorMsg(e.getMessage());
}
return response;
}
@Override
public QueryResponse close(QueryRequest request) {
QueryResponse response = new QueryResponse();
try {
Map<String, String> params = new HashMap<>();
params.put("out_trade_no", request.getOutTradeNo());
params.put("trade_no", request.getTradeNo());
// 模拟调用支付宝关闭接口
String result = callAlipayApi("alipay.trade.close", params);
response.setSuccess(true);
response.setOutTradeNo(request.getOutTradeNo());
response.setStatus(PaymentStatus.CLOSED);
log.info("支付宝关闭交易成功,订单号:{}", request.getOutTradeNo());
} catch (Exception e) {
log.error("支付宝关闭交易失败,订单号:{}", request.getOutTradeNo(), e);
response.setSuccess(false);
}
return response;
}
private Map<String, String> buildPayParams(PaymentRequest request) {
Map<String, String> params = new HashMap<>();
params.put("out_trade_no", request.getOutTradeNo());
params.put("total_amount", request.getTotalAmount().toString());
params.put("subject", request.getSubject());
params.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 即时到账产品
if (StringUtils.hasText(request.getNotifyUrl())) {
params.put("notify_url", request.getNotifyUrl());
}
if (StringUtils.hasText(request.getReturnUrl())) {
params.put("return_url", request.getReturnUrl());
}
return params;
}
private String callAlipayApi(String method, Map<String, String> params) {
// 实际实现中需要集成支付宝SDK并签名请求
// 这里仅做模拟
params.put("app_id", appId);
params.put("method", method);
params.put("charset", "UTF-8");
params.put("sign_type", "RSA2");
params.put("timestamp", String.valueOf(System.currentTimeMillis()));
params.put("version", "1.0");
log.debug("调用支付宝API: {}, 参数: {}", method, JSON.toJSONString(params));
// 模拟返回结果
return "<form>支付宝支付表单</form>";
}
}
微信支付实现
// src/main/java/com/example/payment/service/impl/WechatPaymentService.java
package com.example.payment.service.impl;
import com.example.payment.enums.PaymentStatus;
import com.example.payment.exception.PaymentException;
import com.example.payment.model.*;
import com.example.payment.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 微信支付实现
*/
@Slf4j
public class WechatPaymentService implements PaymentService {
private final String appId;
private final String merchantId;
private final String privateKey;
private final String publicKey;
private final String gatewayUrl;
private final boolean sandbox;
public WechatPaymentService(String appId, String merchantId, String privateKey,
String publicKey, String gatewayUrl, boolean sandbox) {
this.appId = appId;
this.merchantId = merchantId;
this.privateKey = privateKey;
this.publicKey = publicKey;
this.gatewayUrl = gatewayUrl;
this.sandbox = sandbox;
}
@Override
public PaymentResponse pay(PaymentRequest request) {
PaymentResponse response = new PaymentResponse();
try {
// 构造微信支付请求参数
Map<String, String> params = buildPayParams(request);
// 模拟调用微信统一下单接口
Map<String, String> result = callWechatApi("pay/unifiedorder", params);
response.setSuccess(true);
response.setStatus(PaymentStatus.PROCESSING);
response.setOutTradeNo(request.getOutTradeNo());
// 实际应该返回prepay_id等信息
response.setPayData(JSON.toJSONString(result));
log.info("微信支付请求成功,订单号:{}", request.getOutTradeNo());
} catch (Exception e) {
log.error("微信支付失败,订单号:{}", request.getOutTradeNo(), e);
response.setSuccess(false);
response.setErrorMsg(e.getMessage());
}
return response;
}
@Override
public QueryResponse query(QueryRequest request) {
QueryResponse response = new QueryResponse();
try {
Map<String, String> params = new HashMap<>();
params.put("out_trade_no", request.getOutTradeNo());
params.put("transaction_id", request.getTradeNo());
// 模拟调用微信查询接口
Map<String, String> result = callWechatApi("pay/orderquery", params);
// 解析结果
response.setSuccess(true);
response.setOutTradeNo(request.getOutTradeNo());
response.setStatus(PaymentStatus.SUCCESS);
response.setTotalAmount(new BigDecimal("100.00"));
log.info("微信查询成功,订单号:{}", request.getOutTradeNo());
} catch (Exception e) {
log.error("微信查询失败,订单号:{}", request.getOutTradeNo(), e);
response.setSuccess(false);
}
return response;
}
@Override
public RefundResponse refund(RefundRequest request) {
RefundResponse response = new RefundResponse();
try {
Map<String, String> params = new HashMap<>();
params.put("out_trade_no", request.getOutTradeNo());
params.put("transaction_id", request.getTradeNo());
params.put("out_refund_no", StringUtils.hasText(request.getOutRefundNo())
? request.getOutRefundNo() : UUID.randomUUID().toString());
params.put("total_fee", request.getRefundAmount().multiply(new BigDecimal("100")).intValue() + "");
params.put("refund_fee", request.getRefundAmount().multiply(new BigDecimal("100")).intValue() + "");
params.put("refund_desc", request.getRefundReason());
// 模拟调用微信退款接口
Map<String, String> result = callWechatApi("secapi/pay/refund", params);
response.setSuccess(true);
response.setOutRefundNo(params.get("out_refund_no"));
response.setRefundAmount(request.getRefundAmount());
log.info("微信退款成功,订单号:{}", request.getOutTradeNo());
} catch (Exception e) {
log.error("微信退款失败,订单号:{}", request.getOutTradeNo(), e);
response.setSuccess(false);
response.setErrorMsg(e.getMessage());
}
return response;
}
@Override
public QueryResponse close(QueryRequest request) {
QueryResponse response = new QueryResponse();
try {
Map<String, String> params = new HashMap<>();
params.put("out_trade_no", request.getOutTradeNo());
// 模拟调用微信关闭订单接口
Map<String, String> result = callWechatApi("pay/closeorder", params);
response.setSuccess(true);
response.setOutTradeNo(request.getOutTradeNo());
response.setStatus(PaymentStatus.CLOSED);
log.info("微信关闭交易成功,订单号:{}", request.getOutTradeNo());
} catch (Exception e) {
log.error("微信关闭交易失败,订单号:{}", request.getOutTradeNo(), e);
response.setSuccess(false);
}
return response;
}
private Map<String, String> buildPayParams(PaymentRequest request) {
Map<String, String> params = new HashMap<>();
params.put("appid", appId);
params.put("mch_id", merchantId);
params.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
params.put("body", request.getSubject());
params.put("out_trade_no", request.getOutTradeNo());
params.put("total_fee", request.getTotalAmount().multiply(new BigDecimal("100")).intValue() + "");
params.put("spbill_create_ip", request.getClientIp());
params.put("notify_url", request.getNotifyUrl());
params.put("trade_type", "NATIVE"); // 扫码支付
return params;
}
private Map<String, String> callWechatApi(String path, Map<String, String> params) {
// 实际实现中需要集成微信支付SDK并签名请求
// 这里仅做模拟
params.put("sign", generateSign(params));
log.debug("调用微信API: {}, 参数: {}", path, params);
// 模拟返回结果
Map<String, String> result = new HashMap<>();
result.put("return_code", "SUCCESS");
result.put("result_code", "SUCCESS");
result.put("prepay_id", "wx1234567890");
return result;
}
private String generateSign(Map<String, String> params) {
// 实际应实现微信签名算法
return "sign123456";
}
}
工厂类和服务管理
// src/main/java/com/example/payment/factory/PaymentServiceFactory.java
package com.example.payment.factory;
import com.example.payment.config.PaymentProperties;
import com.example.payment.exception.PaymentException;
import com.example.payment.service.PaymentService;
import com.example.payment.service.impl.AliPaymentService;
import com.example.payment.service.impl.WechatPaymentService;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 支付服务工厂类
*/
public class PaymentServiceFactory {
private final PaymentProperties properties;
private final Map<String, PaymentService> serviceCache = new ConcurrentHashMap<>();
public PaymentServiceFactory(PaymentProperties properties) {
this.properties = properties;
}
public PaymentService getPaymentService(String platform) {
return serviceCache.computeIfAbsent(platform, this::createPaymentService);
}
private PaymentService createPaymentService(String platform) {
PaymentProperties.PlatformConfig config = properties.getPlatforms().get(platform);
if (config == null) {
throw new PaymentException("PAYMENT_CONFIG_NOT_FOUND", "未找到支付平台配置: " + platform);
}
switch (platform) {
case "alipay":
return new AliPaymentService(
config.getAppId(),
config.getPrivateKey(),
config.getPublicKey(),
config.getGatewayUrl(),
config.isSandbox()
);
case "wechat":
return new WechatPaymentService(
config.getAppId(),
config.getMerchantId(),
config.getPrivateKey(),
config.getPublicKey(),
config.getGatewayUrl(),
config.isSandbox()
);
default:
throw new PaymentException("UNSUPPORTED_PAYMENT_TYPE", "不支持的支付类型: " + platform);
}
}
}
// src/main/java/com/example/payment/service/impl/DefaultPaymentService.java
package com.example.payment.service.impl;
import com.example.payment.config.PaymentProperties;
import com.example.payment.exception.PaymentException;
import com.example.payment.factory.PaymentServiceFactory;
import com.example.payment.model.*;
import com.example.payment.service.PaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
* 默认支付服务实现
*/
@Service
public class DefaultPaymentService implements PaymentService {
private final PaymentServiceFactory serviceFactory;
private final PaymentProperties properties;
@Autowired
public DefaultPaymentService(PaymentProperties properties) {
this.properties = properties;
this.serviceFactory = new PaymentServiceFactory(properties);
}
@Override
public PaymentResponse pay(PaymentRequest request) {
validateRequest(request);
String platform = determinePlatform(request.getPaymentType());
return serviceFactory.getPaymentService(platform).pay(request);
}
@Override
public QueryResponse query(QueryRequest request) {
validateRequest(request);
String platform = determinePlatform(request.getPaymentType());
return serviceFactory.getPaymentService(platform).query(request);
}
@Override
public RefundResponse refund(RefundRequest request) {
validateRequest(request);
String platform = determinePlatform(request.getPaymentType());
return serviceFactory.getPaymentService(platform).refund(request);
}
@Override
public QueryResponse close(QueryRequest request) {
validateRequest(request);
String platform = determinePlatform(request.getPaymentType());
return serviceFactory.getPaymentService(platform).close(request);
}
private void validateRequest(Object request) {
if (request == null) {
throw new PaymentException("INVALID_REQUEST", "请求参数不能为空");
}
}
private String determinePlatform(String paymentType) {
if (StringUtils.hasText(paymentType)) {
return paymentType;
}
return properties.getDefaultPlatform();
}
}
自动配置类
// src/main/java/com/example/payment/config/PaymentAutoConfiguration.java
package com.example.payment.config;
import com.example.payment.service.PaymentService;
import com.example.payment.service.impl.DefaultPaymentService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 支付自动配置类
*/
@Configuration
@ConditionalOnClass(PaymentService.class)
@EnableConfigurationProperties(PaymentProperties.class)
@ConditionalOnProperty(prefix = "payment", name = "enabled", havingValue = "true", matchIfMissing = true)
public class PaymentAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public PaymentService paymentService(PaymentProperties properties) {
return new DefaultPaymentService(properties);
}
}
Spring Boot启动器
// src/main/java/com/example/payment/PaymentSpringBootStarter.java
package com.example.payment;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Payment Starter 配置类
*/
@Configuration
@ComponentScan(basePackages = "com.example.payment")
public class PaymentSpringBootStarter {
// Starter入口配置
}
配置元数据
// src/main/resources/META-INF/spring-configuration-metadata.json
{
"groups": [
{
"name": "payment",
"type": "com.example.payment.config.PaymentProperties",
"sourceType": "com.example.payment.config.PaymentProperties"
}
],
"properties": [
{
"name": "payment.enabled",
"type": "java.lang.Boolean",
"description": "是否启用支付功能",
"defaultValue": true
},
{
"name": "payment.default-platform",
"type": "java.lang.String",
"description": "默认支付平台",
"defaultValue": "alipay"
},
{
"name": "payment.platforms",
"type": "java.util.Map<java.lang.String,com.example.payment.config.PaymentProperties$PlatformConfig>",
"description": "各支付平台配置"
}
]
}
配置示例
# application.yml 配置示例
payment:
enabled: true
default-platform: alipay
platforms:
alipay:
app-id: 2021000123456789
private-key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASC...
public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
gateway-url: https://openapi.alipay.com/gateway.do
notify-url: https://yourdomain.com/payment/alipay/notify
return-url: https://yourdomain.com/payment/alipay/return
sandbox: true
wechat:
app-id: wx1234567890abcdef
merchant-id: 1234567890
private-key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASC...
public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
gateway-url: https://api.mch.weixin.qq.com
notify-url: https://yourdomain.com/payment/wechat/notify
sandbox: true
使用示例
// 使用示例 Controller
@RestController
@RequestMapping("/payment")
public class PaymentController {
@Autowired
private PaymentService paymentService;
@PostMapping("/create")
public ResponseEntity<PaymentResponse> createPayment(@RequestBody PaymentDTO paymentDTO) {
PaymentRequest request = PaymentRequest.builder()
.outTradeNo(paymentDTO.getOrderNo())
.totalAmount(paymentDTO.getAmount())
.subject(paymentDTO.getSubject())
.paymentType(paymentDTO.getPaymentType())
.clientIp(paymentDTO.getClientIp())
.notifyUrl(paymentDTO.getNotifyUrl())
.returnUrl(paymentDTO.getReturnUrl())
.build();
PaymentResponse response = paymentService.pay(request);
return ResponseEntity.ok(response);
}
@GetMapping("/query")
public ResponseEntity<QueryResponse> queryPayment(
@RequestParam String outTradeNo,
@RequestParam(required = false) String paymentType) {
QueryRequest request = new QueryRequest();
request.setOutTradeNo(outTradeNo);
request.setPaymentType(paymentType);
QueryResponse response = paymentService.query(request);
return ResponseEntity.ok(response);
}
@PostMapping("/refund")
public ResponseEntity<RefundResponse> refundPayment(@RequestBody RefundDTO refundDTO) {
RefundRequest request = new RefundRequest();
request.setOutTradeNo(refundDTO.getOrderNo());
request.setRefundAmount(refundDTO.getAmount());
request.setRefundReason(refundDTO.getReason());
request.setPaymentType(refundDTO.getPaymentType());
RefundResponse response = paymentService.refund(request);
return ResponseEntity.ok(response);
}
}
// DTO类
@Data
class PaymentDTO {
private String orderNo;
private BigDecimal amount;
private String subject;
private String paymentType;
private String clientIp;
private String notifyUrl;
private String returnUrl;
}
@Data
class RefundDTO {
private String orderNo;
private BigDecimal amount;
private String reason;
private String paymentType;
}
特性总结
1. 统一接口设计
- 提供标准的支付、查询、退款、关闭接口
- 屏蔽不同支付平台的技术细节
- 支持扩展新的支付方式
2. 自动配置机制
- 基于Spring Boot的自动配置特性
- 零配置启动,按需启用
- 支持配置文件自定义参数
3. 灵活的扩展性
- 工厂模式支持多平台动态切换
- 插件化架构便于新增支付渠道
- 配置驱动的服务实例化
4. 完善的异常处理
- 统一异常类型定义
- 详细的错误码和错误信息
- 日志记录支持问题追踪
这个 payment-spring-boot-starter 提供了一个完整的支付解决方案,开发者只需要引入依赖并在配置文件中配置相应参数,就可以快速集成支付宝和微信支付功能,大大简化了支付模块的开发工作。


671

被折叠的 条评论
为什么被折叠?



