寫這篇文章的初衷是因為最近項目中涉及到較多支付模塊,於是打算從這篇文章開始,陸續整理一下支付寶和微信的支付模塊。每篇文末會給出最新整理的支付demo,會隨着文章進度不斷更新,一方面是自己的總結過程,另一方面希望能幫助到更多像我這種小白的程序汪。其實支付寶的官方文檔和demo也很詳細,本文只是做簡要總結。
一、Wap支付產品介紹
這篇文章我們先來介紹一下支付寶Wap支付(也叫作手機網站支付),手機網站支付功能適用於商家在移動端網頁應用中集成支付寶支付功能。 商家在網頁中調用支付寶提供的網頁支付接口調起支付寶客戶端內的支付模塊,商家網頁會跳轉到支付寶中完成支付,支付完后跳回到商家網頁內,最后展示支付結果。
注意:手機網站支付產品不建議在APP端使用;如果需要在APP端中使用支付,請接入APP支付產品,接入文檔詳見 APP支付開發文檔。(之后我會介紹APP支付)
1、應用場景
(1)用戶已安裝支付寶支付流程
步驟1:用戶在瀏覽器中訪問商家網頁應用,選擇商品下單、確認購買,進入支付環節,選擇支付寶付款,用戶點擊去支付,如下圖1;
步驟2:進入到支付寶支付路由頁面,支付寶處理支付請求,並嘗試喚起支付寶客戶端,如下圖2(此頁無法自定義刪除);
步驟3:進入到支付寶頁面,調起支付寶支付,出現確認支付界面,如下圖3;
步驟4:用戶確認收款方和金額,點擊立即支付后出現輸入密碼界面,如下圖4;
步驟5:輸入正確密碼后,支付寶端顯示支付結果,如下圖5;
步驟6:自動回跳到瀏覽器中,商家根據付款結果個性化展示訂單處理結果,如下圖6。
注意:
在iOS系統中,喚起支付寶App支付完成后,不會自動回到瀏覽器或商戶App。用戶可手工切回到瀏覽器或商戶App。
(2)用戶未安裝支付寶支付流程
步驟1:若用戶未安裝支付寶客戶端,用戶可先點擊支付寶支付路由頁面里的 點這里下載支付寶APP 藍色鏈接,下載支付寶,如圖 7;
步驟2:成功下載並安裝支付寶客戶端后,點擊支付寶支付路由頁面里的 使用支付寶APP付款 按鈕進行付款,如圖 8;
步驟3:點擊 使用支付寶APP付款 按鈕后,重新開始如上所述的“用戶已安裝支付寶支付流程”步驟。
注意事項:
(1)支付的時候可以配置退出地址和同步支付成功地址,后面我會提到
(2)現在新商戶的wap支付已經取消了瀏覽器網頁登錄支付寶賬號付款的功能,上面截圖給出的就是新申請的商戶支付截圖,如果是老商戶或者沙箱環境下,支付主頁面會多一個“繼續瀏覽器付款”的選項,至於為什么是“繼續”,當然是因為wap支付是瀏覽器H5產品調用的,哈哈哈
(3)不要在微信瀏覽器中調取支付寶WAP支付,雖然微信瀏覽器也是瀏覽器,但是我們都知道這兩家是死對頭的,所以別問我為什么不能
(4)如果你沒有正是環境的產品,可以設置沙箱環境,本人就是使用的沙箱環境
2、准入條件
-
申請前必須擁有經過實名認證的支付寶賬戶;
-
企業或個體工商戶可申請;
-
需提供真實有效的營業執照,且支付寶賬戶名稱需與營業執照主體一致;
-
網站能正常訪問且頁面顯示完整,網站需要明確經營內容且有完整的商品信息;
-
網站必須通過ICP備案。如為個體工商戶,網站備案主體需要與支付寶賬戶主體名稱一致;
-
如為個體工商戶,則團購不開放,且古玩、珠寶等奢侈品、投資類行業無法申請本產品。
3、計費模式
-
費率按單筆計算;
-
一般行業費率:0.6%;自2018年5月9日起,特殊行業新簽約費率從1.2%調整為1%,特殊行業范圍包括:休閑游戲;網絡游戲點卡、渠道代理;游戲系統商;網游周邊服務、交易平台;網游運營商(含網頁游戲)。
4、使用說明
手機網站支付產品包含的接口和描述如下:
接口英文名 | 接口中文名 |
描述 |
alipay.trade.wap.pay | 手機網頁支付接口 | 通過此接口傳入訂單參數,同時喚起支付寶手機網頁支付頁面 |
alipay.trade.close | 交易關閉接口 |
通過此接口關閉此前已創建的交易,關閉后,用戶將無法繼續付款。僅能關閉創建后未支付的交易。 |
alipay.trade.query | 交易狀態查詢接口 |
通過此接口查詢某筆交易的狀態,交易狀態:交易創建,等待買家付款;未付款交易超時關閉,或支付完成后全額退款;交易支付成功;交易結束,不可退款。 |
alipay.trade.refund | 交易退款接口 |
通過此接口對單筆交易進行退款操作。 |
alipay.trade.fastpay.refund.query | 退款查詢 |
查詢退款訂單的狀態。 |
alipay.data.dataservice.bill.downloadurl.query | 賬單查詢接口 |
調用此接口獲取賬單的下載鏈 |
注意:其實除了alipay.trade.wap.pay 手機網站支付接口,其他接口對於其他產品都是通用的
二、准備工作
先上官方文檔入口,支付寶文檔入口模仿微信最最近做了更新,變得更簡潔明了。
1、創建應用並獲取APPID
這個比較簡單可以參考《開放平台應用創建指南》
這個部分我就不詳細說了,一般我們作為開發人員不需要自己去處理這些應用申請的事情,會有專門的人員申請號相關產品,給到申請完成的支付寶賬號密碼登錄,然后我們自己配置開發環境就ok了。因為申請應用會涉及到營業執照、備案域名等等信息要求,比較耽誤時間,而且瑣碎,所以這個我們就不用自己操心了,做好開發就行了。
2、配置應用環境
開發者調用接口前需要先生成RSA密鑰,RSA密鑰包含應用私鑰(APP_PRIVATE_KEY)、應用公鑰(APP_PUBLIC_KEY)。生成密鑰后在開放平台管理中心進行密鑰配置,配置完成后可以獲取支付寶公鑰(ALIPAY_PUBLIC_KEY)。詳細步驟請參考《配置應用環境》。
不得不說支付寶簡直是太貼心了,為了方便開發者生成一對RSA密鑰支付寶提供一鍵生成工具,具體如何生成與配置密鑰詳見簽名專區。
下載該工具后,解壓打開文件夾,運行“RSA簽名驗簽工具.bat”(WINDOWS)或“RSA簽名驗簽工具.command”(MAC_OSX)。
以下演示截圖
步驟1:下載工具保存在本地並傻瓜式安裝,注意安裝目錄不要有空格和中文,保持優秀的開發習慣
步驟2:cmd命令運行或者點擊桌面的快捷方式
步驟3:設置密鑰,將生成的“公鑰字符串”粘貼保存設置,支付寶會生成對應的“支付寶公鑰”,保存好你的公私鑰信息,后面配置文件會用到。
注意事項:
生成的私鑰需妥善保管,避免遺失,不要泄露。應用私鑰需填寫到代碼中供簽名時使用。應用公鑰需提供給支付寶賬號管理者上傳到支付寶開放平台。
3、配置沙箱環境
注意:沙箱環境的密鑰最好與正式上線的應用進行區分避免一些不必要的麻煩。WAP支付支持沙箱環境而app支付不支持沙箱環境
上傳對應的公鑰,沙箱賬號待會在測試的時候回使用
4、服務端實現(集成並且配置SDK)
【官方資源下載地址】
(1)引入
關於sdk引入集成分別有以下兩種方法
方法一:直接下載sdk包並修改pom.xml文件,引入sdk,步驟如下:
步驟1:下載sdk包到本地
步驟2:maven項目中使用本地jar包,首先我在項目根目錄中創建一個lib文件夾,將jar包拷貝到lib文件夾下
步驟3:然后我們在maven的pom.xml中配置
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>20161129201425</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/alipay-sdk-java20161129201425.jar</systemPath>
</dependency>
注意:這里的groupId和artifactId以及version都是可以隨便填寫的 ,scope必須填寫為system,而systemPath我們現在我們jar包的地址就可以了
步驟4:我們必須在maven打包的過程中加入我們這個jar包。因為項目運行的時候需要這個Jar,並且我們得拷貝在WEB-INF/lib目錄下。若不修改配置的話,打包不進去該sdk
<groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <webResources> <resource> <directory>${project.basedir}/lib</directory> <targetPath>WEB-INF/lib</targetPath> <filtering>false</filtering> <includes> <include>**/*.jar</include> </includes> </resource> </webResources> </configuration> <version>2.1.1</version>
方法二:直接maven的pom.xml引入中央倉庫配置,過程如下截圖
步驟1:點擊“maven項目依賴”
步驟2:選擇你想引入的包文件,本人選擇的是“3.3.87.ALL”
步驟3:復制maven配置到你的pom.xml中
(2)集成
在使用SDK調用具體API前,需要先配置通用接入參數
1、APP_ID 使用沙箱模式中的APP_ID. 2、APP_PRIVATE_KEY ALIPAY_PUBLIC_KEY 使用你之前保存的“商戶私鑰”和支付寶給你生成的“支付寶公鑰” 3、CHARSET 默認使用UTF-8
然后,使用上述接入參數初始化AlipayClient,這里只是描述過程,后面會告訴你具體怎么配置
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",APP_ID,APP_PRIVATE_KEY,"json",CHARSET,ALIPAY_PUBLIC_KEY);
接下來,就可以用alipayClient來調用具體的API了。
注意:
(1)alipayClient只需要初始化一次,后續調用不同的API都可以使用同一個alipayClient對象。
(2)手機網站支付不支持第三方授權,不能代商家發起請求。
5、調用接口
手機網站支付產品包含兩類API:
頁面跳轉類:需要從前端頁面以Form表單的形式發起請求,瀏覽器會自動跳轉至支付寶的相關頁面(一般是收銀台或簽約頁面),用戶在該頁面完成相關業務操作后再回跳到商戶指定頁面。例如本產品中的手機網站支付接口alipay.trade.wap.pay。
系統調用類:直接從服務端發起HTTP請求,支付寶會同步返回請求結果。例如本產品中的交易查詢等配套API。
6、調用流程圖
三、服務端具體封裝和操作過程
對於頁面跳轉類API,SDK不會也無法像系統調用類API一樣自動請求支付寶並獲得結果,而是在接受request請求對象后,為開發者生成前台頁面請求需要的完整form表單的html(包含自動提交腳本),商戶直接將這個表單的String輸出到http response中即可。
哈哈,前面講了那么多的准備工作,相信你對支付寶的wap支付流程已經有了一個初步的了解,現在我們來看一下具體的封裝過程
1、將支付寶通用參數獨立配置在一個屬性文件中方便管理
支付寶alipay.properties文件配置信息如下:
#==================================公司測試賬戶線下環境==================================== #應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號 ali_app_id=********* #商戶私鑰(應用私鑰),您的PKCS8格式RSA2私鑰 ali_merchant_private_key=******* #商戶公鑰(應用公鑰) ali_merchant_public_key=******* #支付寶公鑰,對應APPID下的支付寶公鑰。 ali_alipay_public_key=********** #沙箱賬號:uinrgn0730@sandbox.com 沙箱登錄/支付密碼:111111 #服務器異步通知頁面路徑 ali_notify_url=******* #頁面跳轉同步通知頁面路徑 ali_return_url=****** #用戶付款中途退出返回商戶網站的地址 ali_quit_url=****** #簽名方式 ali_sign_type=RSA2 #字符編碼格式 ali_charset=utf-8 #支付寶網關(注意這是沙箱的網關,正式的網關為:https://openapi.alipay.com/gateway.do) ali_gatewayUrl=https://openapi.alipaydev.com/gateway.do #角色身份 ali_pid=2088621954885028 #過期時間 ali_timeout_express=30m
2、讀取配置文件信息
AliPayProperties.java文件的具體內容如下,與alipay.properties的屬性信息一致,讀取的就是配置文件的信息,封裝成實體類,方便直接使用,后續若修改切換支付寶信息,只需要修改alipay.properties文件信息即可,例如切換沙箱環境為正式環境
package com.example.learn.config.properties; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource("classpath:/alipay.properties") public class AliPayProperties { /** * 應用ID */ @Value("${ali_app_id}") private String ali_app_id; /** * 商戶私鑰 */ @Value("${ali_merchant_private_key}") private String ali_merchant_private_key; /** * 商戶公鑰 */ @Value("${ali_merchant_public_key}") private String ali_merchant_public_key; /** * 支付寶公鑰 */ @Value("${ali_alipay_public_key}") private String ali_alipay_public_key; /** * 服務器異步通知頁面路徑 */ @Value("${ali_notify_url}") private String ali_notify_url; /** * 頁面跳轉同步通知頁面路徑 */ @Value("${ali_return_url}") private String ali_return_url; /** * 用戶付款中途退出返回商戶網站的地址 */ @Value("${ali_quit_url}") private String ali_quit_url; /** * 簽名方式 */ @Value("${ali_sign_type}") private String ali_sign_type; /** * 字符編碼格式 */ @Value("${ali_charset}") private String ali_charset; /** * 支付寶網關 */ @Value("${ali_gatewayUrl}") private String ali_gatewayUrl; /** * 角色身份 */ @Value("${ali_pid}") private String ali_pid; /** * 過期時間 */ @Value("${ali_timeout_express}") private String ali_timeout_express; public String getAli_app_id() { return ali_app_id; } public String getAli_merchant_private_key() { return ali_merchant_private_key; } public String getAli_merchant_public_key() { return ali_merchant_public_key; } public String getAli_alipay_public_key() { return ali_alipay_public_key; } public String getAli_notify_url() { return ali_notify_url; } public String getAli_return_url() { return ali_return_url; } public String getAli_quit_url() { return ali_quit_url; } public String getAli_sign_type() { return ali_sign_type; } public String getAli_charset() { return ali_charset; } public String getAli_gatewayUrl() { return ali_gatewayUrl; } public String getAli_pid() { return ali_pid; } public String getAli_timeout_express() { return ali_timeout_express; } public void setAli_app_id(String ali_app_id) { this.ali_app_id = ali_app_id; } public void setAli_merchant_private_key(String ali_merchant_private_key) { this.ali_merchant_private_key = ali_merchant_private_key; } public void setAli_merchant_public_key(String ali_merchant_public_key) { this.ali_merchant_public_key = ali_merchant_public_key; } public void setAli_alipay_public_key(String ali_alipay_public_key) { this.ali_alipay_public_key = ali_alipay_public_key; } public void setAli_notify_url(String ali_notify_url) { this.ali_notify_url = ali_notify_url; } public void setAli_return_url(String ali_return_url) { this.ali_return_url = ali_return_url; } public void setAli_quit_url(String ali_quit_url) { this.ali_quit_url = ali_quit_url; } public void setAli_sign_type(String ali_sign_type) { this.ali_sign_type = ali_sign_type; } public void setAli_charset(String ali_charset) { this.ali_charset = ali_charset; } public void setAli_gatewayUrl(String ali_gatewayUrl) { this.ali_gatewayUrl = ali_gatewayUrl; } public void setAli_pid(String ali_pid) { this.ali_pid = ali_pid; } public void setAli_timeout_express(String ali_timeout_express) { this.ali_timeout_express = ali_timeout_express; } @Override public String toString() { return "AliPayProperties [ali_app_id=" + ali_app_id + ", ali_merchant_private_key=" + ali_merchant_private_key + ", ali_merchant_public_key=" + ali_merchant_public_key + ", ali_alipay_public_key=" + ali_alipay_public_key + ", ali_notify_url=" + ali_notify_url + ", ali_return_url=" + ali_return_url + ", ali_quit_url=" + ali_quit_url + ", ali_sign_type=" + ali_sign_type + ", ali_charset=" + ali_charset + ", ali_gatewayUrl=" + ali_gatewayUrl + ", ali_pid=" + ali_pid + ", ali_timeout_express=" + ali_timeout_express + "]"; } }
3、加載接入參數並初始化AlipayClient
AliPayUtil.java的實例化代碼信息如下:
package com.example.learn.utils.pay.alipay; /** * 支付寶工具類 * @author Administrator * */ @Component public class AliPayUtil { @Autowired private AliPayProperties aliPayProperties ; private static AliPayProperties staticAliPayProperties; static AlipayClient alipayClient; /** * 初始化 */ @PostConstruct public void init() { staticAliPayProperties = aliPayProperties; alipayClient = new DefaultAlipayClient(staticAliPayProperties.getAli_gatewayUrl(), staticAliPayProperties.getAli_app_id(), staticAliPayProperties.getAli_merchant_private_key(), "json", staticAliPayProperties.getAli_charset(), staticAliPayProperties.getAli_alipay_public_key(), staticAliPayProperties.getAli_sign_type()); } }
注意:
(1)alipayClient只需要初始化一次,后續調用不同的API都可以使用同一個alipayClient對象。
(2)我為了后面在業務層中直接調取該靜態API里面封裝好的方法(后面會給出對應方法),將其定義為了一個工具類,而該工具類需要注入讀取 AliPayProperties 里面的屬性,所以需要在該類上打上 @Component 注解
(3)我為了后面將其他支付API全部定義為靜態方法直接調用,這里初始化的時候將 alipayClient 定義為了靜態變量,所以需要將 AliPayProperties 也定義為靜態變量,但是由於 @Autowired 注解不能作用於靜態變量,所以借助靜態變量 staticAliPayProperties 作為過渡,並在初始化的方法里面,讓 staticAliPayProperties = aliPayProperties;
4、利用sdk封裝wap支付代碼
通用模塊,就在demo里面的 AliPayUtil.java 里面
/** * alipay.trade.wap.pay:H5手機網站支付接口2.0(外部商戶創建訂單並支付) * @param out_trade_no:商戶訂單號 * @param total_amount:支付金額,單位:元 * @return */ public static String alipayTradeWapPay(String out_trade_no, String total_amount){ try { //(1)封裝bizmodel信息 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); //SDK已經封裝掉了公共參數,這里只需要傳入業務參數。以下方法為sdk的model入參方式(model和biz_content同時存在的情況下取biz_content)。 model.setOutTradeNo(out_trade_no); model.setSubject("支付寶手機網站支付"); model.setBody("支付寶手機網站支付"); model.setProductCode("QUICK_WAP_WAY"); model.setTotalAmount(total_amount); model.setTimeoutExpress(staticAliPayProperties.getAli_timeout_express()); model.setQuitUrl(staticAliPayProperties.getAli_quit_url()); //(2)設置請求參數 AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest(); alipayRequest.setReturnUrl(staticAliPayProperties.getAli_return_url()); alipayRequest.setNotifyUrl(staticAliPayProperties.getAli_notify_url()); alipayRequest.setBizModel(model); //(3)請求 String form = alipayClient.pageExecute(alipayRequest).getBody(); System.out.println("*********************\n返回結果為:"+form); return form; } catch (AlipayApiException e) { e.printStackTrace(); return null; } }
5、模擬支付請求
步驟1:控制層 AliPayController.java 寫請求方法 alipayTradeWapPay
步驟2:調取的接口和實現類
注意:
(1)這里我隨機生成了商戶訂單號 out_trade_no,交易金額 total_amount 寫死了,正常業務處理中,我們是根據你的業務實際情況處理
(2)里面涉及到的其他的工具類,最后的demo里面會提供。當然,你也可以自己隨機商城,保持不重復的原則即可
(3)實際操作中,在調取之前,我們需要處理具體的業務邏輯,因為本文只總結支付寶相關的,就不補充了。例如:訂單金額計算處理;商戶訂單和支付寶訂單的關聯和存儲,商戶訂單和支付寶訂單可能是多對一的關系;支付寶訂單需要保存,因為在回調的時候你需要根據支付寶訂單的 out_trade_no 識別支付寶通知的訂單結果;你可以把它存儲在redis或者持久化到數據庫中。
步驟3:模擬支付請求
這里,我是寫了一個html頁面,通過前端請求到后台調取控制層方法模擬實際支付請求的
對於頁面跳轉類API,SDK不會也無法像系統調用類API一樣自動請求支付寶並獲得結果,而是在接受request請求對象后,為開發者生成前台頁面請求需要的完整form表單的html(包含自動提交腳本),對於返回的該form表單,通常有兩種方式處理,一種是后端,一種是前端
前端:獲取后端返回的form數據對象,取到表單對象並直接自動提交到支付寶,也就是上面和demo中的這種處理方式
后端:商戶直接將這個表單的String輸出到http response中即可,處理方式可以如下(以下兩種方式是我在項目過程中的處理,並不在此次demo中,僅供參考),處理差異和細節已經重點標出來了
方法一:
方法二:
注意的是:這種處理方式的請求一般不是上面的json請求模式,而是直接get頁面請求跳轉的模式
6、運行查看測試結果
右鍵運行demo中的 PayDemoApplication.java 文件,瀏覽器輸入 localhost:8080查看,當然你也可以修改application.properties配置文件中的端口號
點擊“H5手機網站支付”按鈕,其他按鈕功能開發后面有時間會發博文介紹,不過demo里面已經有了,你也可以自己先測試,請求后控制台日志顯示
詳細form內容如下:
<form name="punchout_form" method="post" action="https://openapi.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.wap.pay&sign=ctYtQCctKweZCxjDAn8D2y4VxnjAI6uu0SYtIaxkPxf2LskIT4fakZF5xciExeHJW5dbOdiujHltgjaTMK9ikxcdDpDiRhH78E8p83Vs6NQFpHjlBRDTf8dO24PC%2Be3DIbqR8VDImCeBcJKX5yDf4fiqdjoPRHZeb7LrktlmoXRk3TW%2BQbgKigwlCYGBwyBV4%2FTLVDo5piS6FJT1SjCiSZ3K02k7MzcKvFBTIJeiA9dDxyfLfh2SW14uVjU8ny6e7f5n62y0%2B5HcUsUrjN%2BLuVSaj6h76T0PvwVUhBW4gJGiL2n36MiaQTZUdhOrP1qHefzloRnxoeO%2Biwsl2SYCpQ%3D%3D&return_url=http%3A%2F%2Ftrt.wgzvip.com%2Fapi%2FaliReturnPay%2Frecharge%2FreturnPaySynchronizationRecharge¬ify_url=http%3A%2F%2Ftrt.wgzvip.com%2Fapi%2FaliReturnPay%2Frecharge%2FreturnPayAsynchronousRecharge&version=1.0&app_id=2021000116673834&sign_type=RSA2×tamp=2020-08-01+16%3A48%3A10&alipay_sdk=alipay-sdk-java-3.3.87.ALL&format=json"> <input type="hidden" name="biz_content" value="{"body":"支付寶手機網站支付","out_trade_no":"20200801164810929688437","product_code":"QUICK_WAP_WAY","quit_url":"http:\/\/trt.wgzvip.com\/alipay\/aliPayQuitUrl","subject":"支付寶手機網站支付","timeout_express":"30m","total_amount":"0.01"}"> <input type="submit" value="立即支付" style="display:none" > </form>
<script>document.forms[0].submit();</script>
我們可以很清楚的看到,支付寶返回的是一個form表單的內容,我們在前端接收后按照上面前端的處理方法處理后,發現點擊“H5手機網站支付”按鈕之后,直接跳到了支付寶下面頁面,按照以下步驟操作就行
注意事項:
(1)由於我用的是沙箱環境,所以輸入的是沙箱買家賬號和密碼測試的,出來的頁面有“繼續瀏覽器付款”和“使用支付寶APP付款”兩個選項,如果選擇支付寶APP付款需要在手機上安裝沙箱錢包才行。現在新商戶接入的正式環境調取頁面是沒有這個選項的,必須安裝支付寶錢包的,具體區別在產品介紹模塊有提到。頁面如下:
(2)在測試的時候我們發現如果返回不支付(退出、取消支付等)和支付完成之后,支付寶跳到了相關的頁面,這個原因我們在后面同步異步回調模塊會介紹到。
四、同異步結果處理
你以為在成功調取支付寶頁面支付了,我們的操作就結束了么,遠遠沒有,因為我們發現我們只是調取了支付寶的支付頁面,關於支付響應和結果,也就是是否支付了,支付是否成功了,我們並不知道。聰明的支付寶早就為我們考慮到了,所以他為我們提供了三個參數來處理,我們回過頭來看看配置文件和調取方法,分別如下:
1、quit_url:用戶付款中途退出返回商戶網站的地址,手機網站支付選填參數 (1)設置要求:完整的路徑名,攜帶http://或https:// (2)參數說明:添加該參數后在 h5支付收銀台會出現返回按鈕,可用於用戶付款中途退出並返回到該參數指定的商戶網站地址。 (3)注意:該參數對支付寶錢包標准收銀台下的跳轉不生效。2019年8月份后手機網站支付未安裝支付寶錢包,默認提示下載錢包,不走H5頁面支付。該參數設置無效。 2、return_url:頁面跳轉同步通知頁面路徑 (1)設置要求:完整的路徑名,攜帶http://或https:// (2)參數說明:添加該參數后,支付寶會以get的方式向該地址發送通知,核實同步頁面是否有跳轉,手機網站支付接口如果同步頁面沒有進行跳轉是不會發送對應的同步回調數據內容。 (3)注意:不能以同步的跳轉作為支付結果的判斷,支付寶最終的支付結果必須以異步通知或者商戶主動查詢結果為主,如果同步沒有跳轉,參考以下網址排查:https://opensupport.alipay.com/support/knowledge/01/201602474937?ant_source=antsupport 3、notify_url:服務器異步通知頁面路徑
(1)設置要求:手機網站支付接口的參數notify_url必須是公網能post訪問的接口地址;不建議加自定義參數,異步通知可通過請求參數設置passback_params公用回傳參數,該參數異步通知返回。
(2)參數說明:添加該參數之后,支付寶會以post方式發送支付結果通知,需要在異步地址頁面使用post方式進行接收。
(3)注意:如果沒有收到支付寶的異步通知,參考以下網址排查:https://opensupport.alipay.com/support/knowledge/01/201602475759?ant_source=antsupport
1、同步回調通知
步驟1:設計支付寶同步通知的controller入口,接收支付寶的通知
注意:該controller的入口路徑必須與你的配置文件里面的return_url保持一致,並且支持get接收
步驟2:設計接口和實現類驗簽,做你想做的
其中涉及到的工具類如下:
package com.example.learn.utils.string; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.http.HttpServletRequest; public class RequestMap { /** * 從request中獲得參數Map,並返回可讀的Map * * @param request * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Map getParameterMap(HttpServletRequest request) { // 參數Map Map properties = request.getParameterMap(); // 返回值Map Map returnMap = new HashMap(); Iterator entries = properties.entrySet().iterator(); Map.Entry entry; String name = ""; String value = ""; while (entries.hasNext()) { entry = (Map.Entry) entries.next(); name = (String) entry.getKey(); Object valueObj = entry.getValue(); if(null == valueObj){ value = ""; }else if(valueObj instanceof String[]){ String[] values = (String[])valueObj; for(int i=0;i<values.length;i++){ value = values[i] + ","; } value = value.substring(0, value.length()-1); }else{ value = valueObj.toString(); } returnMap.put(name, value); } return returnMap; } }
注意:在同步通知中,我們一般只需要驗簽,根據驗簽結果返回到具體頁面,不做其他處理,千萬不能以支付寶的同步通知結果作為支付結果判斷成功與否。在我測試過程中,發現在沙箱環境下,同步通知一直顯示驗簽失敗,但是異步成功,驗簽方法都一樣,當然不是我的問題了,問了阿里的技術,沙箱環境的bug,沙箱測試環境不穩定這個很正常。
我么可以觀察一下支付寶同步通知給了什么參數信息過來,信息如下:
{
charset=utf-8,
out_trade_no=20200801170240119363676,
method=alipay.trade.wap.pay.return,
total_amount=0.01,
sign=FZol3JVgDcbgNtFIxCACmnTj8ASYllvTJK8cNi2a2sgj0Q7vZ4Z56fI0qnR7fP12p7GAlLldGdHrYI4lHE8dyxVc2Mp4hHNGs4NgzbQluocYhajSR7rejJxjLuF+rkGQiJd5c1ixyIXDUEXeIT3sOShs6m+139DNvUpcfk/YxY07uNZxKBsJfIS2kT/WFZk4hH3/QD3hHwv5Z0CTRHhMD23fTXHXK5h92/Ux02+QaQ2oXb/POV0gr9pS/otE/A9rDVUg7SyA41X4JEnuSibL8k7q9L8ZY5QJD/vMiQAvqjr+CDqSdJMsFqBv03m7K7aOjvaC+fRwZh2k3ucQ5q7zlg==,
trade_no=2020080122001491140510105364,
auth_app_id=2021000116673834,
version=1.0,
app_id=2021000116673834,
sign_type=RSA2,
seller_id=2088621954885028,
timestamp=2020-08-01 17:02:57
}
2、異步回調通知
步驟1:設計支付寶異步通知的controller入口,接收支付寶的通知
收到結果之后,驗簽完畢之后需要及時向支付寶返回結果,否則支付寶收不到結果會連續通知,具體細節后面介紹
步驟2:設計接口和實現類驗簽,做你想做的
一般我們在接收到支付寶的異步通知之后,會先及時驗簽將結果立馬返回給支付寶,如果支付寶沒有及時收到反饋結果,他會認為你沒有收到重復通知你,所以你不僅要及時通知反饋給他,還要異步處理你的業務邏輯結果,而不是讓支付寶等待你處理完你的邏輯。
我么可以觀察一下支付寶異步通知給了什么參數信息過來,信息如下:
{
gmt_create=2020-08-01 17:02:52,
charset=utf-8,
seller_email=knqmbr3206@sandbox.com,
subject=支付寶手機網站支付,
sign=YlFLi+efcQVJyEppUiTE3XT25MrP6GWnIORf4/CiodPgiFHpWt/mjXczFupkIgkBdrvUH4IzvtFDS4Wu2+sdpO2tiiNzQLqzwWuVlpuz6EeBabligpR/kDEFuHLYhG0Wmbg+/bZiUKrRP/RbMeRwXZqNMVd62On3vlg2xUacQto2CEZ9Jirw46bSJl1i0oXPWmpatzmx6bavnfGJ515Ubzjli53q/6D9cxTIy5ZYTuTrFWcHeXXhuaxNdOXpIgajm9im1NgvLIhhrak2uSsOttNObHF8v05yXo81SI7McMTgvPvTFG55y5mN6mBi0+wdIIh2AiHZTi0yQt1tWnSGWg==,
body=支付寶手機網站支付, buyer_id=2088102181191145, invoice_amount=0.01, notify_id=2020080100222170255091140507330314, fund_bill_list=[{"amount":"0.01","fundChannel":"ALIPAYACCOUNT"}],
notify_type=trade_status_sync,
trade_status=TRADE_SUCCESS,
receipt_amount=0.01,
buyer_pay_amount=0.01,
app_id=2021000116673834,
sign_type=RSA2,
seller_id=2088621954885028,
gmt_payment=2020-08-01 17:02:54,
notify_time=2020-08-01 17:02:55,
version=1.0,
out_trade_no=20200801170240119363676,
total_amount=0.01,
trade_no=2020080122001491140510105364,
auth_app_id=2021000116673834,
buyer_logon_id=uin***@sandbox.com,
point_amount=0.00
}
步驟3:異步根據支付寶的返回結果處理你的業務邏輯
注意我注釋的那段文字,描述了在處理的時候需要注意的地方
五、demo示例下載
鏈接:https://pan.baidu.com/s/1Ru71zxFvlfj8l_GPpEZe8A
提取碼:7rxc
注意:因為涉及到私密賬號的原因,所以我注釋掉了alipay.properties 文件中的部分重要配置信息,下載后需要根據你自己的賬號信息修改配置
最后說一點,這個demo里面不僅僅是該篇文章介紹的手機wap支付,有興趣的小伙伴可以自己嘗試一下,后面有時間我會繼續更新博客,陸續介紹電腦網站支付、app支付、掃碼支付和條碼支付,包括介紹查詢、退款、取消、關閉訂單等。針對該文有疑問或者有更好建議的小伙伴可以留言。