pay-spring-boot 開箱即用的Java支付模塊,整合支付寶支付、微信支付

關於

使用本模塊,可輕鬆實現支付寶支付、微信支付對接,從而專一於業務,無需關心第三方邏輯。前端

模塊徹底獨立,無支付寶、微信SDK依賴。git

基於Spring Boot。github

依賴Redis。web

 

我能作什麼

支付寶:電腦網站支付、手機網站支付、掃碼支付、APP支付。spring

微信:電腦網站支付(同掃碼支付)、手機網站支付(微信外H5支付)、掃碼支付、APP支付、JSAPI支付(微信內H5支付)。數據庫

統一支付方法。後端

異步回調封裝。api

訂單狀態查詢。服務器

退款。微信

公對私轉帳。

請確保支付寶、微信賬號已經申請了相應業務、權限

 

模塊集成

只須要簡單的、非侵入式的配置,便可集成到項目中。

 

添加模塊到Maven項目中

父項目中添加pay-spring-boot模塊依賴(pom.xml):

1 <modules>
2     ...
3     <module>pay-spring-boot</module>
4     ...
5 </modules>

 

修改pay-spring-boot的父項目(pom.xml):

1 <parent>
2     <groupId>yourself parent groupId</groupId>
3     <artifactId>yourself parent artifactId</artifactId>
4     <version>yourself parent version</version>
5 </parent>

 

支付憑證

application.yml(或application-*.yml,視項目具體狀況而定)中添加以下配置:

pay:
  wx:
    appid: wx1d96c6yxxc0d192a
    mchid: 1519719912
    key: A6nvI8Xp6A6nvI8Xp6A6nvI8Xp6
    notifyURL: https://xxx.com/wxpay/notify
    certPath: /data/cert/wx/apiclient_cert.p12
    certPassword: 1517923901
  ali:
    appid: 2019138363328891
    privateKey: MIIEuwIBADANBgkqhkiG9w...
    notifyURL: https://xxx.com/alipay/notify

 

關於配置項的具體含義參考AliPayConfigWxPayConfig兩個類,裏邊有詳細說明。

 

注入Redis鏈接池

在項目中建立一個Redis鏈接池工廠實現類,名稱無所謂,必須實現RedisResourceFactory接口,而後添加@ResourceFactoryComponent註解,例如:

 1 @ResourceFactoryComponent
 2 public class DefaultRedisResourceFactory implements RedisResourceFactory {
 3     @Override
 4     public JedisPool getJedisPool() {
 5         /*
 6           框架不關心JedisPool是怎麼來的
 7           這裏的RedisService是我本身實現的服務
 8           根據項目實際狀況換成你本身的實現
 9         */
10         return RedisService.getPool();
11     }
12 }

 

如何支付

通常來講,項目中會有不一樣的支付場景,好比:購買商品、充值等支付業務。

如今用商品購買舉例,經過這個例子展現如何使用框架。

 

建立支付適配器

支付適配器是支付模塊和數據訪問層的橋樑,適配器將支付結果抽象成支付成功(doPaySuccess)支付失敗(doPayFail)退款成功(doRefundSuccess)退款失敗(doRefundFail)四種狀況,表明了四個狀態。

建議不一樣的場景使用不一樣的適配器,不要全部業務邏輯都放一塊兒。

建立訂單的操做,也建議放在適配器中實現。

如下是商品適配器例子:

 1 @Service
 2 public class GoodsTradeService extends AbstractPayAdaptor {
 3     
 4     @Autowired
 5     private GoodsTradeManager goodsTradeManager;
 6  7     /**
 8      * 建立訂單
 9      * @param id 商品id
10      * @return
11      */
12     public Message createOrder(long id){
13         
14     }
15 16     @Override
17     public void doPaySuccess(String outTradeNo) {
18         //支付成功,這裏通常要更新數據庫的狀態
19     }
20 21     @Override
22     public void doPayFail(String outTradeNo) {
23         //支付失敗,這裏通常要更新數據庫的狀態
24     }
25 26     @Override
27     public void doRefundSuccess(String outTradeNo) {
28         //退款成功,這裏通常要更新數據庫的狀態
29     }
30 31     @Override
32     public void doRefundFail(String outTradeNo) {
33         //退款失敗,這裏通常要更新數據庫的狀態
34     }
35 }

 

看起來很是簡單,繼承AbstractPayAdaptor抽象類,而後經過@Service註解交給Spring管理,爲何要交給Spring呢?由於這裏你須要注入數據訪問層的實例(Dao),否則怎麼操做數據庫,只不過我這沒有寫而已~

這裏有一個GoodsTradeManager,是接下來要介紹的支付管理器,先無論它。

仔細觀察會發現,示例中的適配器名稱叫GoodsTradeService,爲何我無論他叫GoodsTradePayAdaptor呢?從支付框架的角度看,這的確是一個適配器實現,但從業務的角度看,它是商品訂單的服務中心,不只要處理訂單狀態,還要承擔建立訂單的職責,它底層(數據庫層)關聯的原本就是一個訂單表,把它稱做訂單服務,更加容易理解。

所以,所謂的適配器,就是用來適配支付框架和數據訪問層的。

 

建立支付管理器

有了適配器,就有了數據訪問的能力,再配上一個管理器做爲統一調度中心,那麼支付這事就搞定了,實現一個管理器很是容易:

 1 @PayManagerComponent
 2 public class GoodsTradeManager extends AbstractPayManager {
 3  4     @Autowired
 5     private GoodsTradeService goodsTradeService;
 6  7     @Override
 8     public String getTradeType() {
 9         return "0";
10     }
11 12     @Override
13     public AbstractPayAdaptor getPayAdaptor() {
14         return goodsTradeService;
15     }
16 }

 

首先繼承AbstractPayManager,而後使用@PayManagerComponent註解註冊管理器,這沒什麼神奇的,只不過是告訴框架這裏有一個管理器,而且把這個管理器交給Spring維護。

getTradeType方法返回長度爲1的字符串,建議取值範圍[0-9a-z],類型會拼接到訂單號中,因此不建議使用特殊字符。所以,一個項目中最多可建立36個不一樣的管理器。

getPayAdaptor方法返回上一步建立的適配器,管理器中包含了適配器。

所以,所謂的管理器,管理的目標就是適配器,同時擔負起統一支付調度的重任,管理器是支付模塊的窗口。

最佳實踐是:每對管理器-適配器對應一種支付業務

 

發起支付

接下來就能夠發起支付了,很是簡單,先來看一個支付寶掃碼支付示例:

1 Trade trade = AliTrade
2                 .qrcodePay()
3                 .subject("商品標題")
4                 .body("商品描述")
5                 .outTradeNo(goodsTradeManager.newTradeNo("你本身的用戶惟一標識"))
6                 .totalAmount("0.01")
7                 .build();
8 TradeToken<String> token = goodsTradeManager.qrcodePay(trade);
9 String url = token.value();

 

先經過AliTrade構造器的qrcodePay方法建立一個掃碼支付訂單,而後調用管理器的qrcodePay方法生成訂單憑證,不一樣的支付產品的訂單憑證可能不一樣,你能夠自由選擇泛型,掃碼支付的憑證就是一個url連接,所以我使用的String類型,調用憑證的value方法,便可得到憑證內容,憑證內容直接返回給前端(網頁或APP),前端便可調起支付。

goodsTradeManager上一步已經建立好,直接@Autowired注入便可。

newTradeNo方法很是重要,它能夠幫你生成一個訂單號,也就是商戶訂單號,在個人設計中,爲了省去繁瑣的全局惟一訂單號生成,將訂單號和用戶關聯起來,規避了訂單號惟一性問題,用戶惟一標識根據你的系統自由選擇,建議長度在[6-10]之間,而且爲固定長度,不能使用特殊字符,用戶惟一標識會直接拼接到訂單號中,長度不固定或太長的話,訂單號會很是難看,不規範,如需更多瞭解,直接看代碼註釋。

AliTrade構造器全部的屬性均與支付寶官方文檔相對應,具體含義參考代碼註釋或者支付寶官方文檔。

訂單的類型AliTrade.qrcodePay和管理器方法goodsTradeManager.qrcodePay必須配套使用。

 

再來看一個微信H5支付的例子:

 1 Trade trade = WxTrade
 2                 .webMobilePay()
 3                 .body("商品標題")
 4                 .outTradeNo(goodsTradeManager.newTradeNo("你本身的用戶惟一標識"))
 5                 .totalFee("1")
 6                 .spbillCreateIp("127.0.0.1")
 7                 .sceneInfo("商品測試場景")
 8                 .build();
 9 TradeToken<String> token = goodsTradeManager.webMobilePay(trade);
10 String url = token.value();

 

只不過是把AliTrade換成了WxTrade,而後調用WxTrade.webMobilePay構造器,加上配套的goodsTradeManager.webMobilePay便可完成微信H5支付。

微信H5支付的憑證也是一個url,直接交給前端處理便可。

由此能夠看出,咱們只須要關心訂單構造器,將訂單構造好,直接調用管理器對應的方法便可,管理器不關心支付寶仍是微信,只須要接收一個配套的訂單,最後拿到訂單憑證,就算是完工了。

依此類推,便可完成其它類型的支付業務。

 

異步回調

涉及錢的事沒有小事,別忘了還有支付結果異步回調

前端的支付結果回調是同步回調,僅供參考,必須之後端的結果爲準

支付寶回調:

 1 @PostMapping(value = "/notify")
 2 public void notify(HttpServletRequest request, HttpServletResponse response){
 3     /*
 4         解析請求參數
 5      */
 6     Map<String, String> params = NoticeManagers.getDefaultManager().receiveAliParams(request);
 7 
 8     /*
 9         封裝
10      */
11     AliPayNoticeInfo info = new AliPayNoticeInfo();
12     TradeStatus status = NoticeManagers.getDefaultManager().execute(params, info);
13 
14     /*
15         持久化回調數據
16      */
17     //TODO: 強烈建議將AliPayNoticeInfo持久化到數據庫中,以備不時之需,固然你也能夠忽略
18 
19     /*
20         業務分發
21      */
22     AbstractPayManager payManager = (AbstractPayManager) PayManagers.find(status.getTradeNo());
23     payManager.doTradeStatus(status);
24 
25     /*
26         響應
27      */
28     NoticeManagers.getDefaultManager().sendAliResponse(response);
29 }

 

微信回調:

 1 @PostMapping(value = "/notify")
 2 public void notify(HttpServletRequest request, HttpServletResponse response){
 3     /*
 4         解析請求參數
 5      */
 6     Map<String, String> params = NoticeManagers.getDefaultManager().receiveWxParams(request);
 7 
 8     /*
 9         封裝
10      */
11     WxPayNoticeInfo info = new WxPayNoticeInfo();
12     TradeStatus status = NoticeManagers.getDefaultManager().execute(params, info);
13 
14     /*
15         持久化回調數據
16      */
17     //TODO: 強烈建議將WxPayNoticeInfo持久化到數據庫中,以備不時之需,固然你也能夠忽略
18 
19     /*
20         業務分發
21      */
22     AbstractPayManager payManager = (AbstractPayManager) PayManagers.find(status.getTradeNo());
23     payManager.doTradeStatus(status);
24 
25     /*
26         響應
27      */
28     NoticeManagers.getDefaultManager().sendWxResponse(response);
29 }

 

最基本的Spring MVC Controller代碼不用我教了吧。

定義一個控制器,接收HTTP請求、響應對象,經過框架解析出參數和訂單狀態,而後將訂單狀態分發給適配器,實現訂單狀態更新,最後給支付寶、微信一個響應,告訴他們已經接收到請求。

回調處理很是規範化,基本不須要作什麼改動(直接Copy),惟一須要作的,也是很是重要的,就是根據你本身項目的實際狀況,以恰當的方式持久化回調數據。

這裏的@PostMapping請求路徑,就是配置在application.yml中的notifyURL,必須保證公網能夠無障礙訪問。

 

主動同步訂單狀態

用來彌補特殊緣由形成的異步回調丟失,異步回調不是100%可靠的

因爲須要請求支付寶、微信服務器,因此速度較慢。

支付寶訂單:

1 Trade trade = AliTrade.query().outTradeNo("商戶訂單號").build();
2 TradeStatus status = goodsTradeManager.status(trade);
3 status.isPaySuccess(); //是否支付成功,其它狀態不一一列舉,自行看代碼

 

微信訂單:

1 Trade trade = WxTrade.basic().outTradeNo("商戶訂單號").build();
2 TradeStatus status = goodsTradeManager.status(trade);
3 status.isPaySuccess(); //是否支付成功,其它狀態不一一列舉,自行看代碼

 

如何轉帳

公對私轉帳

由公司賬號向我的賬號轉帳。

支付寶轉帳:

 1 /*
 2     構造轉帳訂單
 3  */
 4 AliTransferTrade transferTrade = AliTransferTrade
 5                                         .transfer()
 6                                         .outBizNo("商戶轉帳惟一訂單號")
 7                                         .payeeAccount("收款人支付寶賬號")
 8                                         .amount("0.01")
 9                                         .build();
10 
11 /*
12     轉帳
13  */
14 try{
15     AliTransfer.getInstance().transfer(transferTrade);
16 }catch (TransferException e){
17     // 轉帳失敗處理邏輯...
18 }

 

轉帳方法無返回值,不發生異常表明轉帳成功,發生異常表明轉帳失敗,自行處理。

訂單參數含義參考支付寶官方文檔或代碼註釋。

 

微信轉帳:

 1 /*
 2     構造轉帳訂單
 3  */
 4 WxTransferTrade transferTrade = WxTransferTrade
 5                                         .transfer()
 6                                         .partnerTradeNo("商戶轉帳惟一訂單號")
 7                                         .openid("收款人openid")
 8                                         .amount("1")
 9                                         .spbillCreateIp("127.0.0.1")  //這裏是調用接口的服務器公網IP,自行獲取
10                                         .build();
11 /*
12     轉帳
13  */
14 try{
15     WxTransfer.getInstance().transfer(transferTrade);
16 }catch (TransferException e){
17     // 轉帳失敗處理邏輯...
18 }

 

轉帳方法無返回值,不發生異常表明轉帳成功,發生異常表明轉帳失敗,自行處理。

訂單參數含義參考微信官方文檔或代碼註釋。

 

轉帳狀態查詢

支付寶:

 1 /*
 2     構造轉帳查詢訂單
 3  */
 4 AliTransferTrade transferTrade = AliTransferTrade
 5                                         .query()
 6                                         .outBizNo("商戶轉帳惟一訂單號")
 7                                         .build();
 8 /*
 9     轉帳查詢
10  */
11 TransferStatus status = AliTransfer.getInstance().status(transferTrade);;
12 status.isSuccess();  //轉帳成功,其餘狀態自行查看代碼,不一一列舉

 

微信:

 1 /*
 2     構造轉帳查詢訂單
 3  */
 4 WxTransferTrade transferTrade = WxTransferTrade
 5                                         .custom()
 6                                         .partnerTradeNo("商戶轉帳惟一訂單號")
 7                                         .build();
 8 /*
 9     轉帳查詢
10  */
11 TransferStatus status = WxTransfer.getInstance().status(transferTrade);;
12 status.isSuccess();  //轉帳成功,其餘狀態自行查看代碼,不一一列舉

 

附加工具

獲取客戶端IP地址

微信支付大部分場景須要客戶端IP地址,能夠經過本模塊PayHttpUtil.getRealClientIp方法獲取。

若是獲取不到,請檢查代理軟件是否正確設置了X-Forwarded-For

 

其餘

若有疑問,歡迎積極反饋,直接提Issues別客氣。

 

pay-spring-boot項目地址

相關文章
相關標籤/搜索