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


  關於

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

  模塊完全獨立,無支付寶、微信SDK依賴。

  基於Spring Boot。

  依賴Redis。

  我能做什么

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

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

  統一支付方法。

  異步回調封裝。

  訂單狀態查詢。

  退款。

  公對私轉賬。

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

  模塊集成

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

  添加模塊到Maven項目中

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

  1

  2 ...

  3 pay-spring-boot

  4 ...

  5

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

  1

  2 yourself parent groupId

  3 yourself parent artifactId

  4 yourself parent version

  5

  支付憑證

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

  pay:

  wx:

  appid: wx1d96c6yxxc0d192a

  mchid: 1519719912

  key: A6nvI8Xp6A6nvI8Xp6A6nvI8Xp6

  notifyURL: xxx/wxpay/notify

  certPath: /data/cert/wx/apiclient_cert.p12

  certPassword: 1517923901

  ali:

  appid: 2021138363328891

  privateKey: MIIEuwIBADANBgkqhkiG9w...

  notifyURL: xxx/alipay/notify

  關於配置項的具體含義參考AliPayConfig、WxPayConfig兩個類,里邊有詳細說明。

  注入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 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 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。

  歡迎工作一到五年的Java工程師朋友們加入Java程序員開發: 721575865

  群內提供免費的Java架構學習資料(里面有高可用、高並發、高性能及分布式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM