更新時(shí)間:2022-11-10 來(lái)源:黑馬程序員 瀏覽量:
在長(zhǎng)期的教學(xué)過(guò)程中,了解到很多同學(xué)在進(jìn)行畢業(yè)設(shè)計(jì)或課程設(shè)計(jì)時(shí)會(huì)開(kāi)發(fā)一些相應(yīng)的商城系統(tǒng),都有在線支付的相關(guān)需求,而做為個(gè)人,想在實(shí)現(xiàn)在線支付在很多平臺(tái)是不具備相關(guān)條件的,很多平臺(tái)要求具備獨(dú)立法人資格的企業(yè)或個(gè)人商業(yè)戶才可以申請(qǐng)?jiān)诰€支付,而支付寶提供的沙箱環(huán)境,對(duì)于個(gè)人實(shí)現(xiàn)在線支付測(cè)試環(huán)境來(lái)講,是一個(gè)不錯(cuò)的選擇,本文主要講解基于支付寶的沙箱環(huán)境來(lái)實(shí)現(xiàn)在線支付的功能。主要結(jié)合一個(gè)簡(jiǎn)易的商城系統(tǒng)來(lái)講解說(shuō)明在線支付的基本操作流程和相關(guān)API的應(yīng)用,從而實(shí)現(xiàn)基于支付寶沙箱在線支付的基本功能。
一、案例說(shuō)明
本案例基于一個(gè)簡(jiǎn)易的商城系統(tǒng),在這個(gè)系統(tǒng)基礎(chǔ)上實(shí)現(xiàn)在了在線沙箱支付的基本應(yīng)用。系統(tǒng)只保留了基本的商品展示和購(gòu)物基本功能,其它功能接口全部刪除。系統(tǒng)基于前后端分離的開(kāi)發(fā)方式,前端使用VUE開(kāi)發(fā)實(shí)現(xiàn),后端使用Springboot結(jié)合Mybatis開(kāi)發(fā)實(shí)現(xiàn)。
首頁(yè)商品展示
商品詳情
點(diǎn)擊立即付款后進(jìn)入支付寶付款流程
點(diǎn)擊下一步:
確認(rèn)付款
完成支付
看完了案例的演示,是不是有一種想試一試的沖突,感覺(jué)好簡(jiǎn)單?OK,那么我們一起開(kāi)始學(xué)習(xí)支付寶沙箱支付的旅程!
二,支付寶沙箱環(huán)境準(zhǔn)備
2.1 支付寶沙箱介紹
沙箱環(huán)境是支付寶開(kāi)放平臺(tái)為開(kāi)發(fā)者提供的與生產(chǎn)環(huán)境完全隔離的聯(lián)調(diào)測(cè)試環(huán)境,開(kāi)發(fā)者在沙箱環(huán)境中完成的接口調(diào)用不會(huì)對(duì)生產(chǎn)環(huán)境中的數(shù)據(jù)造成任何影響。
沙箱為開(kāi)放的產(chǎn)品提供有限功能范圍的支持,可以覆蓋產(chǎn)品的絕大部分核心鏈路和對(duì)接邏輯,便于開(kāi)發(fā)者快速學(xué)習(xí)/嘗試/開(kāi)發(fā)/調(diào)試。
沙箱環(huán)境會(huì)自動(dòng)完成或忽略一些場(chǎng)景的業(yè)務(wù)門(mén)檻,例如:開(kāi)發(fā)者無(wú)需等待產(chǎn)品開(kāi)通,即可直接在沙箱環(huán)境調(diào)用接口,使得開(kāi)發(fā)集成工作可以與業(yè)務(wù)流程并行,從而提高項(xiàng)目整體的交付效率。
注意:
- 由于沙箱環(huán)境并非 100% 與生產(chǎn)環(huán)境一致,接口的實(shí)際響應(yīng)邏輯請(qǐng)以生產(chǎn)環(huán)境為準(zhǔn),沙箱環(huán)境開(kāi)發(fā)調(diào)試完成后,仍然需要在生產(chǎn)環(huán)境進(jìn)行測(cè)試驗(yàn)收。
- 沙箱環(huán)境擁有完全獨(dú)立的數(shù)據(jù)體系,沙箱環(huán)境下返回的數(shù)據(jù)(比如用戶 ID 等)在生產(chǎn)環(huán)境中都是不存在的,開(kāi)發(fā)者不可將沙箱環(huán)境返回的數(shù)據(jù)與生產(chǎn)環(huán)境中的數(shù)據(jù)混淆。
2.2 支付寶沙箱注冊(cè)及配置
打開(kāi)支付寶開(kāi)發(fā)者頁(yè)面進(jìn)行注冊(cè)登陸:https://opendocs.alipay.com/common/02kkv7
登陸后進(jìn)入開(kāi)放平臺(tái)控制臺(tái):選擇左下解沙箱
在沙箱應(yīng)用中可以查看沙箱的相關(guān)信息,其中APPID需要復(fù)制并記錄,我們?cè)谶M(jìn)行支付時(shí)要指定APPID。
其它的均保持默認(rèn)配置即可,接口的加簽方式選擇系統(tǒng)默認(rèn)密鑰即可,選用公鑰模式,點(diǎn)擊查看可以看到對(duì)應(yīng)的公鑰和私鑰:做支付時(shí)需要用到。
支付寶網(wǎng)關(guān)地址:https://openapi.alipaydev.com/gateway.do
支付寶沙箱網(wǎng)關(guān)地址,開(kāi)發(fā)者在沙箱環(huán)境調(diào)用 OpenAPI 發(fā)送 http(s) 請(qǐng)求的目標(biāo)地址,需配置在AlipayClient中。此地址為固定的,在程序中需要配置。
選擇左邊沙賬戶:創(chuàng)建申請(qǐng)個(gè)人和商家對(duì)應(yīng)的虛擬賬戶,并可以在線模擬充值和取現(xiàn)。
至此,我們需要準(zhǔn)備的沙箱環(huán)境和配置己準(zhǔn)備就緒。
三,內(nèi)網(wǎng)穿透工具準(zhǔn)備
支付寶沙箱支付成功后,要回調(diào)本地的服務(wù)地址進(jìn)行支付結(jié)果的通知,而我們的測(cè)試環(huán)境是運(yùn)行在內(nèi)網(wǎng)中,所以需要借助內(nèi)網(wǎng)穿透工具來(lái)實(shí)現(xiàn)外網(wǎng)調(diào)用內(nèi)網(wǎng)的服務(wù)接口。內(nèi)網(wǎng)穿透的工具網(wǎng)上有很多免費(fèi)的,我們今天使用的是NATAPP這款工具。
1、打開(kāi)官網(wǎng)注冊(cè)并登陸:https://natapp.cn/login
2、登陸后選擇購(gòu)買(mǎi)隧道:選擇免費(fèi)隧道(有效期一個(gè)月)
3、指定名字,選擇Web協(xié)議,并指定本地的應(yīng)用通訊的端口
4、購(gòu)買(mǎi)成功后會(huì)生成認(rèn)證令牌:復(fù)制并保存
5、下載客戶端工具:根據(jù)你電腦情況選擇合適的版本下載,這里我選用Windows64位
6、創(chuàng)建配置文件:config.ini 具體下載:https://natapp.cn/article/config_ini
放在客戶端natapp.exe同級(jí)目錄下,并將其中的authtoken換成我們剛申請(qǐng)的免費(fèi)隧道的令牌
7、啟動(dòng)客戶端:windows下,直接雙擊natapp.exe 即可。紅框內(nèi)就是我們的隧道通信地址。
注意:每次啟動(dòng)客戶端都會(huì)分配一個(gè)新的隧道地址:要注意后期更換程序中配置 的地址。
8、測(cè)試:此時(shí)t2vnvc.natappfree.cc臨時(shí)域名就是代表了本地應(yīng)用程序的訪問(wèn)地址http://localhost:80
可以直接使用此域名來(lái)訪問(wèn)應(yīng)用程序下的任意一個(gè)接口,如能訪問(wèn),則環(huán)境OK。
http://t2vnvc.natappfree.cc/order/findAll 查詢(xún)所有訂單:測(cè)試成功
四,沙箱支付相關(guān)API說(shuō)明
支付寶支付提供了很多種支付應(yīng)用場(chǎng)景以及對(duì)應(yīng)的API應(yīng)用,打開(kāi)網(wǎng)址:https://open.alipay.com/api
可以看到有對(duì)應(yīng)的應(yīng)用類(lèi)型:選中其中某一個(gè)進(jìn)入,則有相關(guān)的文檔介紹和API應(yīng)用案例,所以學(xué)習(xí)和使用起來(lái)還是比較簡(jiǎn)單。
4.1 當(dāng)面付
如果我們想通過(guò)支付寶生成二維碼,讓客戶端掃描二維碼進(jìn)行支付,可以選擇當(dāng)面付:
https://open.alipay.com/api/detail?code=I1080300001000041016#api-detail-content
各個(gè)接口有在線文檔和在線調(diào)試的案例,可以自行查看。以其中的統(tǒng)一收單線下交易預(yù)創(chuàng)建為例說(shuō)明:
package com.java.sdk.demo; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.CertAlipayRequest; import com.alipay.api.AlipayConfig; import com.alipay.api.domain.AlipayTradePrecreateModel; import com.alipay.api.response.AlipayTradePrecreateResponse; import com.alipay.api.request.AlipayTradePrecreateRequest; import com.alipay.api.FileItem; import java.util.Base64; import java.util.ArrayList; import java.util.List; public class AlipayTradePrecreate { public static void main(String[] args) throws AlipayApiException { String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCs5FX6NlZEq8t43tnAT91hcGhTPL6Iieq/e1foh3N0ME601PwtfXzFPac0Qp5qygLtyZkYFY8UiYqdXPQpvO4T4ah5Rus8IiCwGDBpyg3ha6xSWnD7QCvc/YgOJ+ei1XD6F5Y+t2X3gbTCsy1H9wTe9XLh78tcpuhib/4OEwxJfHxEi5fBzQQJBE6vo8pX7mTeeP7f1RBxAgRsswMvCQs1SX362XdD3IRWErf8dUOqV0tT5ahjhXdbFv6fUQmnt+1i00CIAlOPm7vLWslw1qJT4ubZuL0HPQLWEoD3ab0OdgaJvPdoWvnUVzBQqwZgnXHFN5nV1QQbVTe3Q3XWIWTNAgMBAAECggEBAI2JI8WBRCBVj2306cgg2XfSQOWbfdNjLHTzMaUfdemxPk2VapiG+WSzMzV6MBv2Im2tsxHfXFGQw5RNq2ibzT8jZftajmqc+auPMdr42WIn9ls4xPM6tm3kc8Q68MsB9soWEx5mnyf+3A7kBfa7BxbLUvAZkZ7Y2Lb1PcMh3tYso7FYERStxpdtUgPSkqXgZERrzrtHpFDe05CI/xjZUfmvXsTrKiIXxqvil9hYGISkMyVJjaJUXvZwmgzLtHd7I8z7sNXOw26Ie1DihCy3VzPh5pwZPQId2v2LBABdUogTwVfb7GAV6swP+OMxoffYPdW7AV+82zOYybm3GjKCTiECgYEA57ukyNybLE7h7yeDSFKdkoBnY7RmIW4NRwQNFopb5LlNt9vTm5easuEdDhPlGoNpJ5Ul/ms9pDVDnH2ZkU7IaiMPd/MxQpQ5dos5tRsjb02YwligiD3ZxDBRCNSjcqiR7+5jYb68H2c8bXLtOtZ/dsYyqZmEhH0OsV/2WKMr9bUCgYEAvv9EO9J2UEVBPkEKvhLfH7X8GZK6CROEehNG8qbDqxJ5AbFmo8lzNvbcHjJoiwEMzYU1q5VMhMVMepk1Lk5Q4VRouAMTUY3o8byQMeX7BYHlmmLaXuw8NAFFE43Mr14UGkrWesZR9u1LSw0QPNag3cyL2XxmgpWCmPAwLzQ+obkCgYEAnFukKFOR8CeZkwCaOGZmI7+4AzJp5wyWsOAu5JKexomxwuj0svtsSl5SeHk8ENOyVB7y7+P0R6QY5rJot/7sg5D8kvbouf/Bdhci7OxO0H8PH7MlPkhdc18WjlrLnkXXkojLTdygmp/RyhfNsfGhN8crz/vhynOHQm95jP0sYDECgYBGJ2zgrEVY5pc4TpurhTL/atDWc9ZxTwHx9PKyXh6wz3Ay9v5EWtI+9h2T2eAcbp5NPwifpY8dlXqp7WxjPdDncyGjtT17Tyyo3iH4sgip1TSIqJadFxhl4bGFpSfxohSwM5zkK8QfdrmZL4svTYZ48ZDMqazdeh0mH9MTA5WdwQKBgFO79TUR5fq+u0sBjYW7VLiQ5J6Tq7U0phFjFFjR/eYqkKUtE5K/BdOsSQjwY21TOkoTazyDJiVLEx5pWQueIa0ez1WPNswz7VN2su9N/jwP+G5ILrz1I+nj2PiWXVbfWZ2qd2h0qB27CF7gZ9lQgjg5dQMt4Fmd7f8uzBzrwLQW"; String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydfxVYSMISxqvitww5063ZQFVRzrcRW9a+xQ0Pozz7ve9IeK1h9p7xFwSUtTNi/ki9ZcnZ7wP0xunKrYPVXyUvRz/vSVnovkzvmcSTdqlgtzTAQTJFYL0nmzhnv4RWbcDnVbo+IIlu8g0p6wQv7V8P5EGpbh63+d3q/DYhsbxVVZN1fKjnw/y38/GuqGoiaxkcrf6h65rGO/aRGbX6gbGnRxUgBOEiFGFdkuCx9ACg4cskSme69lDbIBDpsmSbQpUKhTEA4Wv36mqrbee+hGf5sTaqDUHl0/SPgP45ab15J3BeWHB/yXbdK4Z/gPfMFMweOupg54Sm+kJOGmq73cMwIDAQAB"; AlipayConfig alipayConfig = new AlipayConfig(); alipayConfig.setServerUrl("https://openapi.alipaydev.com/gateway.do"); alipayConfig.setAppId("2021000121607425"); alipayConfig.setPrivateKey(privateKey); alipayConfig.setFormat("json"); alipayConfig.setAlipayPublicKey(alipayPublicKey); alipayConfig.setCharset("UTF8"); alipayConfig.setSignType("RSA2"); AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig); AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); model.setOutTradeNo("20150320010101001"); model.setTotalAmount("88.88"); model.setSubject("Iphone6 16G"); request.setBizModel(model); AlipayTradePrecreateResponse response = alipayClient.execute(request); //可以將此字符串返回到客戶端,使用二維碼生成工具來(lái)生成二維碼圖片即可 System.out.println("支付地址:"+response.getQrCode()); if (response.isSuccess()) { System.out.println("調(diào)用成功"); } else { System.out.println("調(diào)用失敗"); } } }
4.2 手機(jī)網(wǎng)站支付API
如果我們基于手機(jī)網(wǎng)站來(lái)實(shí)現(xiàn)在線支付,比如HTML5網(wǎng)站或小程序等,可以通過(guò)手機(jī)網(wǎng)站支付API進(jìn)行實(shí)現(xiàn):
https://open.alipay.com/api/detail?code=I1080300001000041949
各個(gè)接口有在線文檔和在線調(diào)試的案例,可以自行查看。以其中的手機(jī)網(wǎng)站支付接口2.0為例說(shuō)明:
package com.java.sdk.demo; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.CertAlipayRequest; import com.alipay.api.AlipayConfig; import com.alipay.api.response.AlipayTradeWapPayResponse; import com.alipay.api.domain.AlipayTradeWapPayModel; import com.alipay.api.request.AlipayTradeWapPayRequest; import com.alipay.api.FileItem; import java.util.Base64; import java.util.ArrayList; import java.util.List; public class AlipayTradeWapPay { public static void main(String[] args) throws AlipayApiException { String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCs5FX6NlZEq8t43tnAT91hcGhTPL6Iieq/e1foh3N0ME601PwtfXzFPac0Qp5qygLtyZkYFY8UiYqdXPQpvO4T4ah5Rus8IiCwGDBpyg3ha6xSWnD7QCvc/YgOJ+ei1XD6F5Y+t2X3gbTCsy1H9wTe9XLh78tcpuhib/4OEwxJfHxEi5fBzQQJBE6vo8pX7mTeeP7f1RBxAgRsswMvCQs1SX362XdD3IRWErf8dUOqV0tT5ahjhXdbFv6fUQmnt+1i00CIAlOPm7vLWslw1qJT4ubZuL0HPQLWEoD3ab0OdgaJvPdoWvnUVzBQqwZgnXHFN5nV1QQbVTe3Q3XWIWTNAgMBAAECggEBAI2JI8WBRCBVj2306cgg2XfSQOWbfdNjLHTzMaUfdemxPk2VapiG+WSzMzV6MBv2Im2tsxHfXFGQw5RNq2ibzT8jZftajmqc+auPMdr42WIn9ls4xPM6tm3kc8Q68MsB9soWEx5mnyf+3A7kBfa7BxbLUvAZkZ7Y2Lb1PcMh3tYso7FYERStxpdtUgPSkqXgZERrzrtHpFDe05CI/xjZUfmvXsTrKiIXxqvil9hYGISkMyVJjaJUXvZwmgzLtHd7I8z7sNXOw26Ie1DihCy3VzPh5pwZPQId2v2LBABdUogTwVfb7GAV6swP+OMxoffYPdW7AV+82zOYybm3GjKCTiECgYEA57ukyNybLE7h7yeDSFKdkoBnY7RmIW4NRwQNFopb5LlNt9vTm5easuEdDhPlGoNpJ5Ul/ms9pDVDnH2ZkU7IaiMPd/MxQpQ5dos5tRsjb02YwligiD3ZxDBRCNSjcqiR7+5jYb68H2c8bXLtOtZ/dsYyqZmEhH0OsV/2WKMr9bUCgYEAvv9EO9J2UEVBPkEKvhLfH7X8GZK6CROEehNG8qbDqxJ5AbFmo8lzNvbcHjJoiwEMzYU1q5VMhMVMepk1Lk5Q4VRouAMTUY3o8byQMeX7BYHlmmLaXuw8NAFFE43Mr14UGkrWesZR9u1LSw0QPNag3cyL2XxmgpWCmPAwLzQ+obkCgYEAnFukKFOR8CeZkwCaOGZmI7+4AzJp5wyWsOAu5JKexomxwuj0svtsSl5SeHk8ENOyVB7y7+P0R6QY5rJot/7sg5D8kvbouf/Bdhci7OxO0H8PH7MlPkhdc18WjlrLnkXXkojLTdygmp/RyhfNsfGhN8crz/vhynOHQm95jP0sYDECgYBGJ2zgrEVY5pc4TpurhTL/atDWc9ZxTwHx9PKyXh6wz3Ay9v5EWtI+9h2T2eAcbp5NPwifpY8dlXqp7WxjPdDncyGjtT17Tyyo3iH4sgip1TSIqJadFxhl4bGFpSfxohSwM5zkK8QfdrmZL4svTYZ48ZDMqazdeh0mH9MTA5WdwQKBgFO79TUR5fq+u0sBjYW7VLiQ5J6Tq7U0phFjFFjR/eYqkKUtE5K/BdOsSQjwY21TOkoTazyDJiVLEx5pWQueIa0ez1WPNswz7VN2su9N/jwP+G5ILrz1I+nj2PiWXVbfWZ2qd2h0qB27CF7gZ9lQgjg5dQMt4Fmd7f8uzBzrwLQW"; String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydfxVYSMISxqvitww5063ZQFVRzrcRW9a+xQ0Pozz7ve9IeK1h9p7xFwSUtTNi/ki9ZcnZ7wP0xunKrYPVXyUvRz/vSVnovkzvmcSTdqlgtzTAQTJFYL0nmzhnv4RWbcDnVbo+IIlu8g0p6wQv7V8P5EGpbh63+d3q/DYhsbxVVZN1fKjnw/y38/GuqGoiaxkcrf6h65rGO/aRGbX6gbGnRxUgBOEiFGFdkuCx9ACg4cskSme69lDbIBDpsmSbQpUKhTEA4Wv36mqrbee+hGf5sTaqDUHl0/SPgP45ab15J3BeWHB/yXbdK4Z/gPfMFMweOupg54Sm+kJOGmq73cMwIDAQAB"; AlipayConfig alipayConfig = new AlipayConfig(); alipayConfig.setServerUrl("https://openapi.alipaydev.com/gateway.do"); alipayConfig.setAppId("2021000121607425"); alipayConfig.setPrivateKey(privateKey); alipayConfig.setFormat("json"); alipayConfig.setAlipayPublicKey(alipayPublicKey); alipayConfig.setCharset("UTF8"); alipayConfig.setSignType("RSA2"); AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig); AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); model.setOutTradeNo("70501111111S001111119"); model.setTotalAmount("9.00"); model.setSubject("大樂(lè)透"); //手機(jī)網(wǎng)站支付指定productCode為固定值:QUICK_WAP_WAY model.setProductCode("QUICK_WAP_WAY"); model.setSellerId("2088102147948060"); request.setBizModel(model); AlipayTradeWapPayResponse response = alipayClient.pageExecute(request); //getBody()返回的就是提交支付的嵌套頁(yè)面,返回前端后自動(dòng)跳轉(zhuǎn)到支付寶支付操作界面 System.out.println(response.getBody()); if (response.isSuccess()) { System.out.println("調(diào)用成功"); } else { System.out.println("調(diào)用失敗"); } } }
4.3 電腦網(wǎng)站支付API
如果我們基于PC電腦網(wǎng)站來(lái)實(shí)現(xiàn)在線支付,比如相關(guān)電商平臺(tái)等,可以通過(guò)電腦網(wǎng)站支付API進(jìn)行實(shí)現(xiàn):
https://open.alipay.com/api/detail?code=I1080300001000041203#api-detail-content
各個(gè)接口有在線文檔和在線調(diào)試的案例,可以自行查看。以其中的統(tǒng)一收單下單并支付頁(yè)面接口為例說(shuō)明:
package com.java.sdk.demo; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.CertAlipayRequest; import com.alipay.api.AlipayConfig; import com.alipay.api.domain.AlipayTradePagePayModel; import com.alipay.api.response.AlipayTradePagePayResponse; import com.alipay.api.request.AlipayTradePagePayRequest; import com.alipay.api.FileItem; import java.util.Base64; import java.util.ArrayList; import java.util.List; public class AlipayTradePagePay { public static void main(String[] args) throws AlipayApiException { String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCs5FX6NlZEq8t43tnAT91hcGhTPL6Iieq/e1foh3N0ME601PwtfXzFPac0Qp5qygLtyZkYFY8UiYqdXPQpvO4T4ah5Rus8IiCwGDBpyg3ha6xSWnD7QCvc/YgOJ+ei1XD6F5Y+t2X3gbTCsy1H9wTe9XLh78tcpuhib/4OEwxJfHxEi5fBzQQJBE6vo8pX7mTeeP7f1RBxAgRsswMvCQs1SX362XdD3IRWErf8dUOqV0tT5ahjhXdbFv6fUQmnt+1i00CIAlOPm7vLWslw1qJT4ubZuL0HPQLWEoD3ab0OdgaJvPdoWvnUVzBQqwZgnXHFN5nV1QQbVTe3Q3XWIWTNAgMBAAECggEBAI2JI8WBRCBVj2306cgg2XfSQOWbfdNjLHTzMaUfdemxPk2VapiG+WSzMzV6MBv2Im2tsxHfXFGQw5RNq2ibzT8jZftajmqc+auPMdr42WIn9ls4xPM6tm3kc8Q68MsB9soWEx5mnyf+3A7kBfa7BxbLUvAZkZ7Y2Lb1PcMh3tYso7FYERStxpdtUgPSkqXgZERrzrtHpFDe05CI/xjZUfmvXsTrKiIXxqvil9hYGISkMyVJjaJUXvZwmgzLtHd7I8z7sNXOw26Ie1DihCy3VzPh5pwZPQId2v2LBABdUogTwVfb7GAV6swP+OMxoffYPdW7AV+82zOYybm3GjKCTiECgYEA57ukyNybLE7h7yeDSFKdkoBnY7RmIW4NRwQNFopb5LlNt9vTm5easuEdDhPlGoNpJ5Ul/ms9pDVDnH2ZkU7IaiMPd/MxQpQ5dos5tRsjb02YwligiD3ZxDBRCNSjcqiR7+5jYb68H2c8bXLtOtZ/dsYyqZmEhH0OsV/2WKMr9bUCgYEAvv9EO9J2UEVBPkEKvhLfH7X8GZK6CROEehNG8qbDqxJ5AbFmo8lzNvbcHjJoiwEMzYU1q5VMhMVMepk1Lk5Q4VRouAMTUY3o8byQMeX7BYHlmmLaXuw8NAFFE43Mr14UGkrWesZR9u1LSw0QPNag3cyL2XxmgpWCmPAwLzQ+obkCgYEAnFukKFOR8CeZkwCaOGZmI7+4AzJp5wyWsOAu5JKexomxwuj0svtsSl5SeHk8ENOyVB7y7+P0R6QY5rJot/7sg5D8kvbouf/Bdhci7OxO0H8PH7MlPkhdc18WjlrLnkXXkojLTdygmp/RyhfNsfGhN8crz/vhynOHQm95jP0sYDECgYBGJ2zgrEVY5pc4TpurhTL/atDWc9ZxTwHx9PKyXh6wz3Ay9v5EWtI+9h2T2eAcbp5NPwifpY8dlXqp7WxjPdDncyGjtT17Tyyo3iH4sgip1TSIqJadFxhl4bGFpSfxohSwM5zkK8QfdrmZL4svTYZ48ZDMqazdeh0mH9MTA5WdwQKBgFO79TUR5fq+u0sBjYW7VLiQ5J6Tq7U0phFjFFjR/eYqkKUtE5K/BdOsSQjwY21TOkoTazyDJiVLEx5pWQueIa0ez1WPNswz7VN2su9N/jwP+G5ILrz1I+nj2PiWXVbfWZ2qd2h0qB27CF7gZ9lQgjg5dQMt4Fmd7f8uzBzrwLQW"; String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydfxVYSMISxqvitww5063ZQFVRzrcRW9a+xQ0Pozz7ve9IeK1h9p7xFwSUtTNi/ki9ZcnZ7wP0xunKrYPVXyUvRz/vSVnovkzvmcSTdqlgtzTAQTJFYL0nmzhnv4RWbcDnVbo+IIlu8g0p6wQv7V8P5EGpbh63+d3q/DYhsbxVVZN1fKjnw/y38/GuqGoiaxkcrf6h65rGO/aRGbX6gbGnRxUgBOEiFGFdkuCx9ACg4cskSme69lDbIBDpsmSbQpUKhTEA4Wv36mqrbee+hGf5sTaqDUHl0/SPgP45ab15J3BeWHB/yXbdK4Z/gPfMFMweOupg54Sm+kJOGmq73cMwIDAQAB"; AlipayConfig alipayConfig = new AlipayConfig(); alipayConfig.setServerUrl("https://openapi.alipaydev.com/gateway.do"); alipayConfig.setAppId("2021000121607425"); alipayConfig.setPrivateKey(privateKey); alipayConfig.setFormat("json"); alipayConfig.setAlipayPublicKey(alipayPublicKey); alipayConfig.setCharset("UTF8"); alipayConfig.setSignType("RSA2"); AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig); AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); AlipayTradePagePayModel model = new AlipayTradePagePayModel(); model.setOutTradeNo("20150320010101001"); model.setTotalAmount("88.88"); model.setSubject("Iphone6 16G"); //電腦網(wǎng)站支付指定productCode為固定值:FAST_INSTANT_TRADE_PAY model.setProductCode("FAST_INSTANT_TRADE_PAY"); request.setBizModel(model); AlipayTradePagePayResponse response = alipayClient.pageExecute(request); //getBody()返回的就是提交支付的嵌套頁(yè)面,返回前端后自動(dòng)跳轉(zhuǎn)到支付寶支付操作界面 System.out.println(response.getBody()); if (response.isSuccess()) { System.out.println("調(diào)用成功"); } else { System.out.println("調(diào)用失敗"); } } }
五,沙箱支付功能實(shí)現(xiàn)
我們今天就以電腦網(wǎng)站支付為例進(jìn)行講解實(shí)現(xiàn)。
后臺(tái)開(kāi)發(fā)實(shí)現(xiàn)步驟:
5.1:支付功能的實(shí)現(xiàn)
1、創(chuàng)建配置類(lèi):配置沙箱相關(guān)環(huán)境信息 把配置類(lèi)中相關(guān)的信息改為自己的即可。
package com.qiu.config; import com.qiu.util.general.PropertiesUtil; import lombok.Data; import org.springframework.stereotype.Component; /** * @author ZNZ */ @Data @Component public class AlipayConfig { /** * 沙箱appId */ public static final String APPID = "2021000121607425"; /** * 請(qǐng)求網(wǎng)關(guān) 固定 */ public static final String URL = "https://openapi.alipaydev.com/gateway.do"; /** * 設(shè)置內(nèi)網(wǎng)穿透回調(diào)地址 */ public static final String CALLBACK = "fcswjw.natappfree.cc"; /** * 編碼 */ public static final String CHARSET = "UTF-8"; /** * 返回格式 */ public static final String FORMAT = "json"; /** * RSA2 */ public static final String SIGNTYPE = "RSA2"; /** * 異步通知地址 */ public static final String NOTIFY_URL = "http://"+CALLBACK+"/alipay/notify"; /** * 同步地址 */ //使用內(nèi)網(wǎng)穿透進(jìn)行回調(diào) public static final String RETURN_URL = "http://"+CALLBACK+"/alipay/success"; /** * 應(yīng)用私鑰 pkcs8格式 */ public static final String RSA_PRIVATE_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCs5FX6NlZEq8t43tnAT91hcGhTPL6Iieq/e1foh3N0ME601PwtfXzFPac0Qp5qygLtyZkYFY8UiYqdXPQpvO4T4ah5Rus8IiCwGDBpyg3ha6xSWnD7QCvc/YgOJ+ei1XD6F5Y+t2X3gbTCsy1H9wTe9XLh78tcpuhib/4OEwxJfHxEi5fBzQQJBE6vo8pX7mTeeP7f1RBxAgRsswMvCQs1SX362XdD3IRWErf8dUOqV0tT5ahjhXdbFv6fUQmnt+1i00CIAlOPm7vLWslw1qJT4ubZuL0HPQLWEoD3ab0OdgaJvPdoWvnUVzBQqwZgnXHFN5nV1QQbVTe3Q3XWIWTNAgMBAAECggEBAI2JI8WBRCBVj2306cgg2XfSQOWbfdNjLHTzMaUfdemxPk2VapiG+WSzMzV6MBv2Im2tsxHfXFGQw5RNq2ibzT8jZftajmqc+auPMdr42WIn9ls4xPM6tm3kc8Q68MsB9soWEx5mnyf+3A7kBfa7BxbLUvAZkZ7Y2Lb1PcMh3tYso7FYERStxpdtUgPSkqXgZERrzrtHpFDe05CI/xjZUfmvXsTrKiIXxqvil9hYGISkMyVJjaJUXvZwmgzLtHd7I8z7sNXOw26Ie1DihCy3VzPh5pwZPQId2v2LBABdUogTwVfb7GAV6swP+OMxoffYPdW7AV+82zOYybm3GjKCTiECgYEA57ukyNybLE7h7yeDSFKdkoBnY7RmIW4NRwQNFopb5LlNt9vTm5easuEdDhPlGoNpJ5Ul/ms9pDVDnH2ZkU7IaiMPd/MxQpQ5dos5tRsjb02YwligiD3ZxDBRCNSjcqiR7+5jYb68H2c8bXLtOtZ/dsYyqZmEhH0OsV/2WKMr9bUCgYEAvv9EO9J2UEVBPkEKvhLfH7X8GZK6CROEehNG8qbDqxJ5AbFmo8lzNvbcHjJoiwEMzYU1q5VMhMVMepk1Lk5Q4VRouAMTUY3o8byQMeX7BYHlmmLaXuw8NAFFE43Mr14UGkrWesZR9u1LSw0QPNag3cyL2XxmgpWCmPAwLzQ+obkCgYEAnFukKFOR8CeZkwCaOGZmI7+4AzJp5wyWsOAu5JKexomxwuj0svtsSl5SeHk8ENOyVB7y7+P0R6QY5rJot/7sg5D8kvbouf/Bdhci7OxO0H8PH7MlPkhdc18WjlrLnkXXkojLTdygmp/RyhfNsfGhN8crz/vhynOHQm95jP0sYDECgYBGJ2zgrEVY5pc4TpurhTL/atDWc9ZxTwHx9PKyXh6wz3Ay9v5EWtI+9h2T2eAcbp5NPwifpY8dlXqp7WxjPdDncyGjtT17Tyyo3iH4sgip1TSIqJadFxhl4bGFpSfxohSwM5zkK8QfdrmZL4svTYZ48ZDMqazdeh0mH9MTA5WdwQKBgFO79TUR5fq+u0sBjYW7VLiQ5J6Tq7U0phFjFFjR/eYqkKUtE5K/BdOsSQjwY21TOkoTazyDJiVLEx5pWQueIa0ez1WPNswz7VN2su9N/jwP+G5ILrz1I+nj2PiWXVbfWZ2qd2h0qB27CF7gZ9lQgjg5dQMt4Fmd7f8uzBzrwLQW"; /** * 沙箱支付寶公鑰 */ public static final String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydfxVYSMISxqvitww5063ZQFVRzrcRW9a+xQ0Pozz7ve9IeK1h9p7xFwSUtTNi/ki9ZcnZ7wP0xunKrYPVXyUvRz/vSVnovkzvmcSTdqlgtzTAQTJFYL0nmzhnv4RWbcDnVbo+IIlu8g0p6wQv7V8P5EGpbh63+d3q/DYhsbxVVZN1fKjnw/y38/GuqGoiaxkcrf6h65rGO/aRGbX6gbGnRxUgBOEiFGFdkuCx9ACg4cskSme69lDbIBDpsmSbQpUKhTEA4Wv36mqrbee+hGf5sTaqDUHl0/SPgP45ab15J3BeWHB/yXbdK4Z/gPfMFMweOupg54Sm+kJOGmq73cMwIDAQAB"; private AlipayConfig() { } }
2、定義支付接口地址:用戶點(diǎn)擊立即購(gòu)買(mǎi)后提交請(qǐng)求至下單接口,添加訂單成功后,發(fā)送請(qǐng)求到支付接口,向支付寶發(fā)送支付請(qǐng)求。
前端頁(yè)面代碼:在前端工程src/comments/mall/MallPurchase.vue中
createOrder(formName){ this.$refs[formName].validate((valid) => { if (valid) { if(this.isVip){ this.order.payPrice = this.order.discountPrice; } let loading = this.$loading({lock: true, text: "訂單提交中",background:"rgba(255,255,255,0.1)"}); this.$http.post('/order/add',this.$qs.stringify(this.order,{skipNulls: true})).then((rep)=>{ loading.close(); if(rep.data.code===200){ let orderNo = this.order.orderNo; let orderName = '新新商城-'+this.productInfo.productType+'-'+this.productInfo.productName+'支付訂單'; let payPrice = this.order.payPrice; let loading = this.$loading({lock: true, text: "正在跳轉(zhuǎn)支付頁(yè)面",background:"rgba(255,255,255,0.1)"}); this.$http.post('/alipay/create?orderNo='+orderNo+'&orderName='+orderName+'&payPrice='+payPrice).then((response)=>{ loading.close(); const div = document.createElement('div'); div.innerHTML = response.data; document.body.appendChild(div); document.forms[document.forms.length-1].submit(); }).catch((err)=>{loading.close();this.$msg.error(err)}) } }).catch((err)=>{loading.close();this.$msg.error(err);}) this.orderFormVisible=false; }else{ return false; } }); }
后端對(duì)應(yīng)的支付接口實(shí)現(xiàn):AlipayController
/** * 創(chuàng)建支付 * @param orderNo * @param orderName * @param payPrice * @return */ @ResponseBody @PostMapping(value = "/create", produces = "text/html;charset=utf-8") public String create(@RequestParam("orderNo") String orderNo, @RequestParam("orderName") String orderName, @RequestParam("payPrice") String payPrice) { return alipayService.create(orderNo, orderName, payPrice); }
業(yè)務(wù)方法實(shí)現(xiàn):
/** * 創(chuàng)建支付訂單 * * @param orderId 訂單編號(hào) * @param orderName 訂單名稱(chēng) * @param payPrice 支付金額 * @return 支付表單 */ @Override public String create(String orderId, String orderName, String payPrice) { //創(chuàng)建支付寶支付連接客戶端 AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE); AlipayTradeWapPayRequest payRequest = new AlipayTradeWapPayRequest(); AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); model.setOutTradeNo(orderId); model.setSubject(orderName); model.setTotalAmount(payPrice); model.setProductCode(PRODUCTCODE); payRequest.setBizModel(model); payRequest.setReturnUrl(AlipayConfig.RETURN_URL); payRequest.setNotifyUrl(AlipayConfig.NOTIFY_URL); try { return client.pageExecute(payRequest).getBody(); } catch (AlipayApiException e) { log.error("[支付寶] 支付失敗", e); } return null; }
此接口返回的字符串其實(shí)是一個(gè)FORM表單,并執(zhí)行自動(dòng)提交:所以接口執(zhí)行完后自動(dòng)跳轉(zhuǎn)到支付寶支付平臺(tái)頁(yè)面。
<form name="punchout_form" method="post" action="https://openapi.alipaydev.com/gateway.do?charset=UTF-8&method=alipay.trade.wap.pay&sign=bdVLwcblQMIpdDuHtLtrT6J9KK4IH0Y0FgfS4TVO61VBrG8xfKHitFafkoNAKL6CvPjEoxAhr2cSjluRirt6FpLAffrTd0iVHctBD6JBoK3oUy1j472c8onqZ9x5y896bi3zRObobc7ygJEvvR04RdwIAQPPHObkKjvNEta5qqHOW9S%2FApZSBoPqBZozkijWXhsQDlCmIpvd%2FH4LlHAcpQe67owOAujsJezrGA2cg7Exm50rUGyiAVhA3ICsqi9PVFWU4FbD3jdELHTD3%2BgP2l7%2FLzWMeEIUj9Y0vw6wU9xI%2FhPl1emZGt9iUHLce3NetowYh96kdR6vnfYSYBZhJw%3D%3D&return_url=http%3A%2F%2Ft2vnvc.natappfree.cc%2Falipay%2Fsuccess¬ify_url=http%3A%2F%2Ft2vnvc.natappfree.cc%2Falipay%2Fnotify&version=1.0&app_id=2021000121607425&sign_type=RSA2×tamp=2022-07-17+16%3A09%3A55&alipay_sdk=alipay-sdk-java-4.10.192.ALL&format=json"> <input type="hidden" name="biz_content" value="{"out_trade_no":"2271716507374","product_code":"QUICK_WAP_WAY","subject":"新新商城-生活家電-米家互聯(lián)網(wǎng)洗烘一體機(jī)Pro 20kg支付訂單","total_amount":"3299.00"}"> <input type="submit" value="立即支付" style="display:none" > </form> <script>document.forms[0].submit();</script>
然后就進(jìn)入到了我們文章開(kāi)頭展示的支付流程頁(yè)面:
可以對(duì)照著沙箱的商家賬戶和買(mǎi)家賬戶的錢(qián)數(shù)來(lái)看看是否實(shí)現(xiàn)了買(mǎi)家賬戶扣款和商家賬戶收款的功能。
5.2.回調(diào)本地接口更改支付狀態(tài)
在線支付成功了,買(mǎi)家支付寶的賬戶的錢(qián)也已經(jīng)轉(zhuǎn)移到了商家的賬戶,可是本地應(yīng)用怎么知道支付是否成功呢?我們?cè)诒镜氐膽?yīng)用中也需要根據(jù)支付成功與否來(lái)更改訂單的支付狀態(tài),這就需要我們定義回調(diào)通知接口來(lái)實(shí)現(xiàn)。
我們?cè)贏lipayConfig中已經(jīng)定義了回調(diào)的接口地址:
/** * 異步通知地址 */ public static final String NOTIFY_URL = "http://"+CALLBACK+"/alipay/notify"; ``` 那么這個(gè)接口主要實(shí)現(xiàn)的功能就是根據(jù)支付的結(jié)果來(lái)實(shí)現(xiàn)訂單狀態(tài)的更新: 在AlipayController中定義alipay/notify接口:
/** * @param map */ @ResponseBody @PostMapping(value = "/notify") public String payNotify(@RequestParam Map<String, String> map) { if (TRADE_SUCCESS.equals(map.get(TRADE_STATUS))) { String payTime = map.get(TRADE_TIME); String tradeNo = map.get(OUT_TRADE_NO); String tradeName = map.get(TRADE_NAME); String payAmount = map.get(TRADE_AMOUNT); log.info("[支付成功] {交易時(shí)間:{},訂單號(hào):{},訂單名稱(chēng):{},交易金額:{}}", payTime, tradeNo, tradeName, payAmount); } return "success"; }
/** * 支付回調(diào)成功 * @param map * @param response */ @GetMapping(value = "/success") public void success(@RequestParam Map<String, String> map, HttpServletResponse response) { try { String tradeNo = map.get(OUT_TRADE_NO); if (tradeNo.contains(VIP)) { openMember(response, tradeNo); } else { updateProductStatus(response, tradeNo); } } catch (IOException e) { e.printStackTrace(); } }
/** * 支付成功,更新商品狀態(tài)為待發(fā)貨 * * @param response HTTP響應(yīng) * @param tradeNo 訂單編號(hào) * @throws IOException IO異常信息 */ private void updateProductStatus(HttpServletResponse response, String tradeNo) throws IOException { Integer orderId = orderService.selectIdByKey(orderNo); Order order = new Order(); order.setOrderId(orderId); order.setOrderState("待發(fā)貨"); orderService.updateById(order); //跳轉(zhuǎn)到我的訂單頁(yè)面 response.sendRedirect("http://localhost:8080/#/myOrder"); }
六.總結(jié)
支付寶沙箱支付給了我們一個(gè)模擬支付寶在線支付的環(huán)境來(lái)進(jìn)行測(cè)試,在本地通過(guò)沙箱支付測(cè)試成功后,可以在實(shí)際應(yīng)用中,將對(duì)應(yīng)的賬戶修改為自己企業(yè)賬號(hào)申請(qǐng)的對(duì)公支付的賬戶信息即可,支付寶對(duì)整個(gè)開(kāi)發(fā)提供的文檔和案例也是比較豐富的,對(duì)于我們?nèi)粘W(xué)習(xí)或做一些學(xué)校要求的簡(jiǎn)單課設(shè)來(lái)講,這篇文章所講解的基本支付功能應(yīng)該能幫到大家的。如果還需要實(shí)現(xiàn)其它的像退款等相關(guān)功能,查看文中給的API接口地址,根據(jù)官方案例進(jìn)行修改即可。