Java實(shí)現(xiàn)統(tǒng)一支付入口集成六種支付方式
一、需求背景
我們正在為公司自研平臺(tái)(以下簡(jiǎn)稱“平臺(tái)”)構(gòu)建統(tǒng)一支付能力。平臺(tái)覆蓋多端場(chǎng)景,具體需求如下:
- PC 端:支持掃碼支付(展示二維碼,用戶掃碼完成付款)
- H5 端:支持調(diào)起支付寶/微信支付,并支持會(huì)員卡支付
- 微信小程序端:支持一鍵喚起微信支付,同時(shí)支持會(huì)員卡支付
?? 會(huì)員卡支付為平臺(tái)自研支付方式,基于用戶儲(chǔ)值賬戶體系實(shí)現(xiàn)。本文重點(diǎn)聚焦于第三方支付的統(tǒng)一接入設(shè)計(jì),故該部分僅作簡(jiǎn)要提及。
二、可行性分析:支持的支付方式
根據(jù)業(yè)務(wù)需求,需集成以下主流支付方式:
| 支付渠道 | 支付類型 | 官方文檔 |
|---|---|---|
| 支付寶 | H5 支付 | 支付寶 H5 支付文檔 |
| 支付寶 | 訂單碼支付(掃碼) | 支付寶訂單碼支付文檔 |
| 微信支付 | H5 支付 | 微信 H5 支付文檔 |
| 微信支付 | 二維碼支付(掃碼) | 微信掃碼支付文檔 |
| 微信支付 | 小程序支付 | 微信小程序支付文檔 |
| 自研 | 會(huì)員卡支付 | —— |
三、架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)
為避免大量 if-else 或 switch-case 判斷帶來的代碼膨脹與維護(hù)困難,我們采用 工廠模式 + Spring 容器管理 的策略,實(shí)現(xiàn)支付方式的動(dòng)態(tài)分發(fā)與解耦。
1. 統(tǒng)一支付入口:Controller 層
定義統(tǒng)一的 /pay 接口作為所有支付請(qǐng)求的入口,屏蔽客戶端差異。
@RestController
@RequestMapping("/api/pay")
@Tag(name = "支付中心", description = "統(tǒng)一支付接口")
public class PayController {
private final PayService payService;
private final RedissonPropertiesConfig redissonPropertiesConfig;
private final Redisson redisson;
@Autowired
public PayController(RedissonPropertiesConfig redissonPropertiesConfig,
Redisson redisson,
PayService payService) {
this.redissonPropertiesConfig = redissonPropertiesConfig;
this.redisson = redisson;
this.payService = payService;
}
@PostMapping("/pay")
@Operation(summary = "統(tǒng)一支付入口")
@PermitAll
public CommonResult<PaymentVO> corePay(@RequestBody @Valid PayVO payParam) throws Exception {
log.info("【訂單 {}】發(fā)起支付請(qǐng)求,參數(shù):{}", payParam.getOrderId(), JSON.toJSONString(payParam));
String lockKey = RedisConstant.PAYMENT_LOCK.concat(payParam.getOrderId().toString());
RLock lock = redisson.getLock(lockKey);
PaymentVO r = null;
try {
lock.lock(redissonPropertiesConfig.getAutomaticReleaseTimeValue(), TimeUnit.SECONDS);
// 執(zhí)行支付
r = payService.corePay(payParam);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return CommonResult.success(result);
}
}
?? 說明:使用 Redisson 分布式鎖防止訂單重復(fù)支付,鎖的持有時(shí)間與等待時(shí)間可配置,提升系統(tǒng)健壯性。
2. 支付服務(wù)層:PayService
PayService 是支付流程的調(diào)度中心,負(fù)責(zé)執(zhí)行公共邏輯,如:
- 查詢支付方式配置
- 支付金額校驗(yàn)
- 訂單狀態(tài)檢查(是否已支付、是否可支付)
- 調(diào)用對(duì)應(yīng)支付工廠執(zhí)行支付
@Service
public class PayServiceImpl implements PayService {
@Resource
private ApplicationContext applicationContext;
@Override
@Transactional(rollbackFor = Exception.class)
public PaymentVO corePay(PayVO payParam) throws Exception {
// todo 1. 查詢支付方式配置
PayTypeDO payType = ...
// todo 2. 金額預(yù)檢
validateAmount(payParam.getAmount());
// todo 3. 檢查訂單狀態(tài)(是否已支付、是否存在)
// 4. 動(dòng)態(tài)獲取支付實(shí)現(xiàn)類
CorePayService corePayService = applicationContext.getBean(payType.getServiceName(), CorePayService.class);
// 5. 執(zhí)行支付
return corePayService.pay(payParam);
}
}
?? 關(guān)鍵設(shè)計(jì):通過 PayTypeDO 表中的 serviceName 字段動(dòng)態(tài)映射到 Spring 容器中的 Bean,實(shí)現(xiàn)運(yùn)行時(shí)動(dòng)態(tài)調(diào)用。
3. 支付方式配置表:PayTypeDO
| 字段 | 描述 |
|---|---|
| id | 主鍵 |
| pay_code | 支付方式編碼(如:ALI_H5、WX_NATIVE、MEMBER_CARD) |
| service_name | 對(duì)應(yīng) Spring Bean 名稱(如:aliH5PayService、wxNativePayService) |
| config_json | 支付參數(shù)(JSON,如 appId、商戶號(hào)、密鑰等) |
? 這種設(shè)計(jì)使得新增支付方式只需:
- 新增配置記錄
- 實(shí)現(xiàn) CorePayService 接口
- 注冊(cè)為 Spring Bean
無需修改任何已有代碼,完美符合 開閉原則。
4. 支付工廠:CorePayService 接口與實(shí)現(xiàn)
接口定義
public interface CorePayService {
/**
* 執(zhí)行支付
* @param payParam 支付參數(shù)
* @return 支付結(jié)果(如:二維碼鏈接、跳轉(zhuǎn) URL 等)
*/
PaymentVO pay(PayVO payParam) throws Exception;
}
實(shí)現(xiàn)示例:支付寶掃碼支付(訂單碼)
@Service("AliPayQRCodeServiceImpl")
@Slf4j
public class AliPayQRCodeServiceImpl implements CorePayService {
@Resource
private AlipayConfig alipayConfig;
@Resource
private AliPayPropertiesConfig aliPayPropertiesConfig;
@Resource
private PayLogMapper logMapper;
@Resource
private OrderRefundApi orderRefundApi;
@Resource
private PayLogMapper payLogMapper;
@Resource
private OrderPayLogApi orderPayLogApi;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public PaymentVO pay(PayVO payParam) throws Exception {
// 支付日志
log.info("【{}】訂單支付場(chǎng)景信息 => {}", payLog.getOrderId(), JSONUtil.toJsonStr(build));
// 支付場(chǎng)景信息保存到redis中,避免傳入的數(shù)據(jù)字節(jié)數(shù)太大導(dǎo)致接口調(diào)用失敗
stringRedisTemplate.opsForValue().set(PAYMENT_INFO.concat(payLog.getPayPaylog()), JSONUtil.toJsonStr(build), 12, TimeUnit.HOURS);
// 初始化SDK
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
// 構(gòu)造請(qǐng)求參數(shù)以調(diào)用接口
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
// 設(shè)置商戶訂單號(hào)
model.setOutTradeNo(...);
// 設(shè)置訂單總金額(小數(shù)點(diǎn)后兩位)
model.setTotalAmount(...);
// 設(shè)置訂單標(biāo)題
model.setSubject(...);
// 設(shè)置產(chǎn)品碼
model.setProductCode("QR_CODE_OFFLINE");
// 設(shè)置訂單附加信息
model.setBody(JSON.toJSONString(payParam.getScenarios()));
log.info("訂單【{}】申請(qǐng)支付寶QR付款(v3)參數(shù) => {}", payParam.getOrderId(), JSONUtil.toJsonStr(model));
request.setBizModel(model);
request.setNotifyUrl(...);
AlipayTradePrecreateResponse response = alipayClient.execute(request);
JSONObject entries = JSONUtil.parseObj(response.getBody());
if (!response.isSuccess()) {
log.error("訂單【{}】申請(qǐng)支付寶QR付款(v3)失敗響應(yīng) => {}", payParam.getOrderId(), entries);
throw exception(PayErrorCode.ZFB_QR_PAY_ERR);
}
log.info("訂單【{}】申請(qǐng)支付寶QR付款(v3)成功響應(yīng) => {}", payParam.getOrderId(), entries);
JSONObject resp = entries.getJSONObject("alipay_trade_precreate_response");
String qrCode = resp.getStr("qr_code");
return PaymentVO.builder()
.payType(Integer.valueOf(payParam.getPayTypeNo()))
.inTradeNo(payParam.getPayLog())
.param(qrCode)
.build();
}
?? 同理可實(shí)現(xiàn) AliH5PayService、WxAppletPayService 等,各自獨(dú)立,互不影響。
5. 組件圖:統(tǒng)一支付系統(tǒng)架構(gòu)

6. 序列圖:用戶發(fā)起支付流程

四、總結(jié)與思考
通過本次統(tǒng)一支付系統(tǒng)的構(gòu)建,我們實(shí)現(xiàn)了以下目標(biāo):
? 成果亮點(diǎn)
- 高內(nèi)聚低耦合:各支付方式獨(dú)立實(shí)現(xiàn),職責(zé)清晰。
- 易擴(kuò)展:新增支付渠道只需新增實(shí)現(xiàn)類 + 配置,核心邏輯無侵入。
- 可維護(hù)性強(qiáng):避免
if-else地獄,代碼結(jié)構(gòu)清晰。 - 安全可靠:通過分布式鎖防止重復(fù)支付,金額校驗(yàn)防篡改。
- 符合開閉原則:對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。
五、結(jié)語
支付系統(tǒng)是業(yè)務(wù)的核心環(huán)節(jié),安全、穩(wěn)定、可擴(kuò)展是基本要求。本文通過 工廠模式 + Spring IOC 的組合拳,實(shí)現(xiàn)了多支付方式的優(yōu)雅接入。這不僅是一次技術(shù)實(shí)踐,更是對(duì)設(shè)計(jì)模式、系統(tǒng)架構(gòu)的深入理解。
支付無小事,細(xì)節(jié)定成敗。愿我們都能在構(gòu)建高質(zhì)量系統(tǒng)的過程中不斷精進(jìn),寫出更優(yōu)雅、更可靠的代碼。
到此這篇關(guān)于Java實(shí)現(xiàn)統(tǒng)一支付入口集成六種支付方式的文章就介紹到這了,更多相關(guān)Java 統(tǒng)一支付入口集成內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis實(shí)戰(zhàn)之TypeHandler高級(jí)進(jìn)階
本文主要介紹了自定義的枚舉TypeHandler的相關(guān)知識(shí),具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-02-02
JavaWeb 文件的上傳和下載功能簡(jiǎn)單實(shí)現(xiàn)代碼
這篇文章主要介紹了JavaWeb 文件的上傳和下載功能簡(jiǎn)單實(shí)現(xiàn)代碼,需要的朋友可以參考下2017-04-04
解決httpServletRequest.getParameter獲取不到參數(shù)的問題
這篇文章主要介紹了解決httpServletRequest.getParameter獲取不到參數(shù)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
Java跨session實(shí)現(xiàn)token接口測(cè)試過程圖解
這篇文章主要介紹了Java跨session實(shí)現(xiàn)token接口測(cè)試過程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
SpringSessionRedis配置及發(fā)現(xiàn)的問題講解
今天小編就為大家分享一篇關(guān)于SpringSessionRedis配置及發(fā)現(xiàn)的問題講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03
Java Caledar類(日歷類)如何判斷本周周數(shù)
這篇文章主要介紹了Java Caledar類(日歷類)如何判斷本周周數(shù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
Java數(shù)據(jù)庫(kù)連接池之proxool_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
Proxool是一種Java數(shù)據(jù)庫(kù)連接池技術(shù)。方便易用,便于發(fā)現(xiàn)連接泄漏的情況2017-08-08

