1. 核心依赖配置
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 支付宝 SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.22.110.ALL</version>
</dependency>
<!-- 微信支付 SDK -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version>
</dependency>
<!-- 银联支付 SDK -->
<dependency>
<groupId>com.unionpay</groupId>
<artifactId>upmp</artifactId>
<version>1.0.0</version>
</dependency>
<!-- 数据库相关 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
2. 商户配置实体类
@Entity
@Table(name = "merchant_config")
public class MerchantConfig {
@Id
private String merchantId;
@Enumerated(EnumType.STRING)
private PayChannel payChannel;
@ElementCollection
@MapKeyColumn(name = "config_key")
@Column(name = "config_value")
@CollectionTable(name = "merchant_config_params", joinColumns = @JoinColumn(name = "merchant_id"))
private Map<String, String> configParams = new HashMap<>();
private Boolean enabled = true;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// getter and setter
}
public enum PayChannel {
WECHAT("微信支付"),
ALIPAY("支付宝"),
UNIONPAY("银联支付");
private String desc;
PayChannel(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
3. 支付策略接口定义
public interface PaymentStrategy {
/**
* 统一下单
*/
PayResponse unifiedPay(PayRequest request, MerchantConfig config);
/**
* 订单查询
*/
PayQueryResponse queryOrder(String orderId, MerchantConfig config);
/**
* 退款处理
*/
RefundResponse refund(RefundRequest request, MerchantConfig config);
/**
* 通知验证
*/
boolean verifyNotify(Map<String, String> params, MerchantConfig config);
/**
* 支付渠道
*/
PayChannel getPayChannel();
}
4. 支付请求和响应模型
@Data
public class PayRequest {
private String merchantId;
private String orderId;
private BigDecimal amount;
private String subject;
private String body;
private PayChannel payChannel;
private PayType payType;
private String notifyUrl;
private String returnUrl;
private Map<String, Object> extraParams;
}
@Data
public class PayResponse {
private String code;
private String message;
private String orderId;
private String tradeNo;
private Map<String, Object> payInfo;
private PayChannel payChannel;
}
public enum PayType {
APP("app"),
H5("h5"),
JSAPI("jsapi"),
NATIVE("native"),
MINI_PROGRAM("mini_program");
private String type;
PayType(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
5. 微信支付策略实现
@Component
public class WechatPaymentStrategy implements PaymentStrategy {
@Autowired
private WechatPayService wechatPayService;
@Override
public PayChannel getPayChannel() {
return PayChannel.WECHAT;
}
@Override
public PayResponse unifiedPay(PayRequest request, MerchantConfig config) {
try {
// 初始化微信支付客户端
WechatPayClient client = createClient(config);
// 构建微信支付请求参数
String appid = config.getConfigParams().get("appid");
String mchId = config.getConfigParams().get("mch_id");
String description = request.getBody();
String outTradeNo = request.getOrderId();
long amount = request.getAmount().multiply(new BigDecimal(100)).longValue();
String notifyUrl = request.getNotifyUrl();
// 根据支付类型选择不同的支付方式
switch (request.getPayType()) {
case JSAPI:
return handleJsapiPay(client, appid, mchId, description, outTradeNo, amount, notifyUrl, request.getExtraParams());
case NATIVE:
return handleNativePay(client, appid, mchId, description, outTradeNo, amount, notifyUrl);
case APP:
return handleAppPay(client, appid, mchId, description, outTradeNo, amount, notifyUrl);
case H5:
return handleH5Pay(client, appid, mchId, description, outTradeNo, amount, notifyUrl);
default:
throw new IllegalArgumentException("不支持的微信支付类型: " + request.getPayType());
}
} catch (Exception e) {
log.error("微信支付下单失败", e);
return PayResponse.builder()
.code("FAIL")
.message(e.getMessage())
.orderId(request.getOrderId())
.build();
}
}
@Override
public PayQueryResponse queryOrder(String orderId, MerchantConfig config) {
// 微信订单查询实现
WechatPayClient client = createClient(config);
// 实现订单查询逻辑
return null;
}
@Override
public RefundResponse refund(RefundRequest request, MerchantConfig config) {
// 微信退款实现
WechatPayClient client = createClient(config);
// 实现退款逻辑
return null;
}
@Override
public boolean verifyNotify(Map<String, String> params, MerchantConfig config) {
// 微信通知验证实现
return false;
}
private WechatPayClient createClient(MerchantConfig config) {
// 创建微信支付客户端
String mchId = config.getConfigParams().get("mch_id");
String serialNo = config.getConfigParams().get("serial_no");
String privateKey = config.getConfigParams().get("private_key");
String certPath = config.getConfigParams().get("cert_path");
// 实现客户端创建逻辑
return new WechatPayClient(mchId, serialNo, privateKey, certPath);
}
private PayResponse handleJsapiPay(WechatPayClient client, String appid, String mchId,
String description, String outTradeNo, long amount,
String notifyUrl, Map<String, Object> extraParams) {
// JSAPI支付处理逻辑
return null;
}
private PayResponse handleNativePay(WechatPayClient client, String appid, String mchId,
String description, String outTradeNo, long amount,
String notifyUrl) {
// NATIVE支付处理逻辑
return null;
}
private PayResponse handleAppPay(WechatPayClient client, String appid, String mchId,
String description, String outTradeNo, long amount,
String notifyUrl) {
// APP支付处理逻辑
return null;
}
private PayResponse handleH5Pay(WechatPayClient client, String appid, String mchId,
String description, String outTradeNo, long amount,
String notifyUrl) {
// H5支付处理逻辑
return null;
}
}
6. 支付宝支付策略实现
@Component
public class AlipayPaymentStrategy implements PaymentStrategy {
@Override
public PayChannel getPayChannel() {
return PayChannel.ALIPAY;
}
@Override
public PayResponse unifiedPay(PayRequest request, MerchantConfig config) {
try {
// 初始化支付宝客户端
AlipayClient client = createAlipayClient(config);
String appId = config.getConfigParams().get("app_id");
String returnUrl = request.getReturnUrl();
String notifyUrl = request.getNotifyUrl();
// 根据支付类型构建不同请求
AlipayTradeCreateRequest tradeCreateRequest = new AlipayTradeCreateRequest();
tradeCreateRequest.setReturnUrl(returnUrl);
tradeCreateRequest.setNotifyUrl(notifyUrl);
// 设置业务参数
AlipayTradeCreateModel model = new AlipayTradeCreateModel();
model.setOutTradeNo(request.getOrderId());
model.setTotalAmount(request.getAmount().toString());
model.setSubject(request.getSubject());
model.setBody(request.getBody());
tradeCreateRequest.setBizModel(model);
// 执行请求
AlipayTradeCreateResponse response = client.execute(tradeCreateRequest);
if (response.isSuccess()) {
return PayResponse.builder()
.code("SUCCESS")
.message("支付创建成功")
.orderId(request.getOrderId())
.tradeNo(response.getTradeNo())
.payInfo(buildPayInfo(response, request.getPayType()))
.payChannel(PayChannel.ALIPAY)
.build();
} else {
return PayResponse.builder()
.code("FAIL")
.message(response.getMsg())
.orderId(request.getOrderId())
.build();
}
} catch (Exception e) {
log.error("支付宝支付下单失败", e);
return PayResponse.builder()
.code("FAIL")
.message(e.getMessage())
.orderId(request.getOrderId())
.build();
}
}
@Override
public PayQueryResponse queryOrder(String orderId, MerchantConfig config) {
// 支付宝订单查询实现
return null;
}
@Override
public RefundResponse refund(RefundRequest request, MerchantConfig config) {
// 支付宝退款实现
return null;
}
@Override
public boolean verifyNotify(Map<String, String> params, MerchantConfig config) {
// 支付宝通知验证实现
return false;
}
private AlipayClient createAlipayClient(MerchantConfig config) {
String appId = config.getConfigParams().get("app_id");
String privateKey = config.getConfigParams().get("private_key");
String alipayPublicKey = config.getConfigParams().get("alipay_public_key");
String gatewayUrl = config.getConfigParams().get("gateway_url");
return new DefaultAlipayClient(gatewayUrl, appId, privateKey,
"json", "UTF-8", alipayPublicKey, "RSA2");
}
private Map<String, Object> buildPayInfo(AlipayTradeCreateResponse response, PayType payType) {
Map<String, Object> payInfo = new HashMap<>();
// 根据支付类型返回不同的支付信息
switch (payType) {
case APP:
// 返回APP支付信息
break;
case H5:
// 返回H5支付信息
break;
case JSAPI:
// 返回JSAPI支付信息
break;
case NATIVE:
// 返回NATIVE支付信息
break;
}
return payInfo;
}
}
7. 银联支付策略实现
@Component
public class UnionpayPaymentStrategy implements PaymentStrategy {
@Override
public PayChannel getPayChannel() {
return PayChannel.UNIONPAY;
}
@Override
public PayResponse unifiedPay(PayRequest request, MerchantConfig config) {
try {
// 银联支付参数配置
String merId = config.getConfigParams().get("mer_id");
String certPath = config.getConfigParams().get("cert_path");
String certPwd = config.getConfigParams().get("cert_pwd");
// 初始化银联支付环境
AcpService.init(merId, certPath, certPwd);
// 构建银联支付请求
Map<String, String> reqData = new HashMap<>();
reqData.put("version", "5.1.0"); // 版本号
reqData.put("encoding", "UTF-8"); // 编码方式
reqData.put("signMethod", "01"); // 签名方法
reqData.put("txnType", "01"); // 交易类型
reqData.put("txnSubType", "01"); // 交易子类
reqData.put("bizType", "000201"); // 业务类型
reqData.put("channelType", "08"); // 渠道类型
reqData.put("accessType", "0"); // 接入类型
reqData.put("merId", merId); // 商户代码
reqData.put("orderId", request.getOrderId()); // 订单号
reqData.put("txnTime", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())); // 交易时间
reqData.put("txnAmt", request.getAmount().multiply(new BigDecimal(100)).intValue() + ""); // 交易金额
reqData.put("currencyCode", "156"); // 交易币种
reqData.put("frontUrl", request.getReturnUrl()); // 前台通知地址
reqData.put("backUrl", request.getNotifyUrl()); // 后台通知地址
reqData.put("orderDesc", request.getBody()); // 订单描述
// 签名
Map<String, String> signedData = AcpService.sign(reqData);
// 发送请求
String url = config.getConfigParams().get("front_trans_url");
Map<String, String> rspData = AcpService.post(url, signedData, "UTF-8");
if (null != rspData && !rspData.isEmpty()) {
if (AcpService.validate(rspData, "UTF-8")) {
String respCode = rspData.get("respCode");
if ("00".equals(respCode)) {
return PayResponse.builder()
.code("SUCCESS")
.message("支付创建成功")
.orderId(request.getOrderId())
.tradeNo(rspData.get("queryId"))
.payInfo(rspData)
.payChannel(PayChannel.UNIONPAY)
.build();
} else {
return PayResponse.builder()
.code("FAIL")
.message("银联支付失败:" + rspData.get("respMsg"))
.orderId(request.getOrderId())
.build();
}
}
}
return PayResponse.builder()
.code("FAIL")
.message("银联支付验证失败")
.orderId(request.getOrderId())
.build();
} catch (Exception e) {
log.error("银联支付下单失败", e);
return PayResponse.builder()
.code("FAIL")
.message(e.getMessage())
.orderId(request.getOrderId())
.build();
}
}
@Override
public PayQueryResponse queryOrder(String orderId, MerchantConfig config) {
// 银联订单查询实现
return null;
}
@Override
public RefundResponse refund(RefundRequest request, MerchantConfig config) {
// 银联退款实现
return null;
}
@Override
public boolean verifyNotify(Map<String, String> params, MerchantConfig config) {
// 银联通知验证实现
return AcpService.validate(params, "UTF-8");
}
}
8. 支付策略工厂
@Component
public class PaymentStrategyFactory {
@Autowired
private List<PaymentStrategy> strategies;
private final Map<PayChannel, PaymentStrategy> strategyMap = new EnumMap<>(PayChannel.class);
@PostConstruct
public void init() {
for (PaymentStrategy strategy : strategies) {
strategyMap.put(strategy.getPayChannel(), strategy);
}
}
public PaymentStrategy getStrategy(PayChannel payChannel) {
PaymentStrategy strategy = strategyMap.get(payChannel);
if (strategy == null) {
throw new IllegalArgumentException("不支持的支付渠道: " + payChannel);
}
return strategy;
}
}
9. 统一支付服务
@Service
public class UnifiedPayService {
@Autowired
private PaymentStrategyFactory strategyFactory;
@Autowired
private MerchantConfigRepository merchantConfigRepository;
public PayResponse unifiedPay(PayRequest request) {
// 获取商户配置
MerchantConfig config = getMerchantConfig(request.getMerchantId(), request.getPayChannel());
if (config == null || !config.getEnabled()) {
throw new BusinessException("商户配置不存在或已禁用");
}
// 获取支付策略
PaymentStrategy strategy = strategyFactory.getStrategy(request.getPayChannel());
// 执行支付
return strategy.unifiedPay(request, config);
}
public PayQueryResponse queryOrder(String merchantId, PayChannel payChannel, String orderId) {
MerchantConfig config = getMerchantConfig(merchantId, payChannel);
PaymentStrategy strategy = strategyFactory.getStrategy(payChannel);
return strategy.queryOrder(orderId, config);
}
public RefundResponse refund(RefundRequest request) {
MerchantConfig config = getMerchantConfig(request.getMerchantId(), request.getPayChannel());
PaymentStrategy strategy = strategyFactory.getStrategy(request.getPayChannel());
return strategy.refund(request, config);
}
public boolean verifyNotify(String merchantId, PayChannel payChannel, Map<String, String> params) {
MerchantConfig config = getMerchantConfig(merchantId, payChannel);
PaymentStrategy strategy = strategyFactory.getStrategy(payChannel);
return strategy.verifyNotify(params, config);
}
private MerchantConfig getMerchantConfig(String merchantId, PayChannel payChannel) {
return merchantConfigRepository.findByMerchantIdAndPayChannelAndEnabledTrue(merchantId, payChannel);
}
}
9. 对应多商户支付系统数据库设计
9.1 商户配置主表 (merchant_config)
CREATE TABLE merchant_config (
merchant_id VARCHAR(64) NOT NULL COMMENT '商户ID',
pay_channel VARCHAR(32) NOT NULL COMMENT '支付渠道',
enabled TINYINT(1) DEFAULT 1 COMMENT '是否启用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (merchant_id, pay_channel)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商户配置主表';
9.2 商户配置参数表 (merchant_config_params)
CREATE TABLE merchant_config_params (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
merchant_id VARCHAR(64) NOT NULL COMMENT '商户ID',
config_key VARCHAR(128) NOT NULL COMMENT '配置键',
config_value TEXT COMMENT '配置值',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_merchant_id (merchant_id),
INDEX idx_config_key (config_key),
FOREIGN KEY (merchant_id) REFERENCES merchant_config(merchant_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商户配置参数表';
9.3 配置参数存储方案
1. 微信支付配置参数
appid: 微信应用ID
mch_id: 商户ID
api_v3_key: API V3密钥
private_key: 私钥路径
cert_path: 证书路径
serial_no: 证书序列号
notify_url: 默认通知地址
2. 支付宝配置参数
app_id: 应用ID
private_key: 应用私钥
alipay_public_key: 支付宝公钥
gateway_url: 网关地址
charset: 字符编码
sign_type: 签名类型
3. 银联支付配置参数
mer_id: 商户代码
cert_path: 证书路径
cert_pwd: 证书密码
front_trans_url: 前台交易URL
back_trans_url: 后台交易URL
single_query_url: 单笔查询URL

449

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



