java配置沙箱支付的實現(xiàn)示例
在 Java 項目中集成沙箱支付(以支付寶、微信支付這兩大主流平臺為例)是支付功能開發(fā)的必經(jīng)環(huán)節(jié),用于在測試環(huán)境驗證支付流程的正確性,避免真實資金交易風險。以下是詳細的集成步驟,包含前期準備、環(huán)境搭建、核心功能實現(xiàn)及測試驗證。
一、沙箱支付概述
沙箱支付是支付平臺(如支付寶、微信支付)提供的模擬支付環(huán)境,包含測試用的 APPID、密鑰、網(wǎng)關、測試賬號等資源,其接口邏輯與正式環(huán)境一致,但交易僅為模擬,不涉及真實資金。
核心作用:驗證支付下單、支付跳轉、異步通知、訂單查詢等全流程的正確性。
二、支付寶沙箱支付集成步驟(詳細)
2.1 前期準備:獲取沙箱環(huán)境參數(shù)
注冊支付寶開放平臺賬號
訪問 支付寶開放平臺,注冊開發(fā)者賬號并完成實名認證(個人開發(fā)者即可)。創(chuàng)建應用并開通沙箱
- 進入「控制臺」→「開發(fā)者中心」→「網(wǎng)頁 & 移動應用」,點擊「創(chuàng)建應用」(選擇 “自研” 類型)。
- 應用創(chuàng)建后,在應用詳情頁的「開發(fā)設置」中,開啟 “沙箱” 功能(沙箱環(huán)境默認自動生成,無需審核)。
獲取核心參數(shù)
在「沙箱環(huán)境」頁面獲取以下參數(shù)(后續(xù)代碼中需使用):APPID:沙箱應用唯一標識(如2021000000000000)。- 支付寶網(wǎng)關:沙箱環(huán)境專用網(wǎng)關
https://openapi.alipaydev.com/gateway.do(正式環(huán)境為https://openapi.alipay.com/gateway.do)。 - 密鑰:需生成 RSA2 密鑰對(公鑰 + 私鑰):
- 下載 支付寶開放平臺開發(fā)助手,生成 RSA2(2048 位)密鑰對。
- 將生成的應用公鑰上傳到沙箱環(huán)境的「密鑰管理」中,支付寶會自動生成對應的支付寶公鑰(需保存,用于驗簽)。
- 本地保留應用私鑰(用于接口簽名)。
獲取沙箱測試賬號
在沙箱環(huán)境頁面可獲取測試用的買家賬號、買家支付密碼、測試銀行卡信息(用于模擬支付)。
2.2 環(huán)境搭建:引入依賴與配置
引入支付寶 Java SDK
在項目的pom.xml(Maven)中添加依賴(最新版本可參考支付寶 SDK 文檔):<dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.38.0.ALL</version> </dependency>配置沙箱參數(shù)
在application.properties(或application.yml)中配置參數(shù):# 支付寶沙箱配置 alipay.app-id=你的沙箱APPID alipay.private-key=你的應用私鑰(生成的私鑰,不含換行) alipay.public-key=支付寶公鑰(上傳應用公鑰后生成的) alipay.gateway-url=https://openapi.alipaydev.com/gateway.do alipay.format=json alipay.charset=UTF-8 alipay.sign-type=RSA2
創(chuàng)建配置類
編寫 Java 配置類,初始化支付寶客戶端(AlipayClient):@Configuration public class AlipayConfig { @Value("${alipay.app-id}") private String appId; @Value("${alipay.private-key}") private String privateKey; @Value("${alipay.public-key}") private String publicKey; @Value("${alipay.gateway-url}") private String gatewayUrl; @Value("${alipay.format}") private String format; @Value("${alipay.charset}") private String charset; @Value("${alipay.sign-type}") private String signType; @Bean public AlipayClient alipayClient() { // 初始化支付寶客戶端(沙箱環(huán)境) return new DefaultAlipayClient( gatewayUrl, appId, privateKey, format, charset, publicKey, signType ); } }
2.3 核心功能實現(xiàn)
(1)創(chuàng)建支付訂單(電腦網(wǎng)站支付為例)
用戶下單后,調(diào)用支付寶接口生成支付表單,前端跳轉至沙箱支付頁面。
@Service
public class AlipayService {
@Autowired
private AlipayClient alipayClient;
/**
* 創(chuàng)建支付寶支付訂單
* @param outTradeNo 商戶訂單號(唯一)
* @param totalAmount 訂單金額(單位:元)
* @param subject 訂單標題
* @param returnUrl 支付成功后同步跳轉地址(商戶系統(tǒng)頁面)
* @param notifyUrl 支付成功后異步通知地址(商戶系統(tǒng)接口)
* @return 支付表單HTML(前端自動提交跳轉)
* @throws AlipayApiException 接口調(diào)用異常
*/
public String createPayOrder(String outTradeNo, String totalAmount, String subject,
String returnUrl, String notifyUrl) throws AlipayApiException {
// 1. 創(chuàng)建支付請求對象(電腦網(wǎng)站支付用AlipayTradePagePayRequest)
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
// 設置同步回調(diào)地址(用戶支付成功后跳轉)
request.setReturnUrl(returnUrl);
// 設置異步通知地址(支付寶主動推送支付結果)
request.setNotifyUrl(notifyUrl);
// 2. 組裝請求參數(shù)(JSON格式)
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", outTradeNo); // 商戶訂單號(自定義,需唯一)
bizContent.put("total_amount", totalAmount); // 訂單金額(精確到分)
bizContent.put("subject", subject); // 訂單標題
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 產(chǎn)品碼(固定值)
request.setBizContent(bizContent.toString());
// 3. 調(diào)用支付寶接口,獲取支付表單
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
if (response.isSuccess()) {
return response.getBody(); // 返回HTML表單,前端渲染后自動提交
} else {
throw new RuntimeException("創(chuàng)建支付訂單失?。? + response.getMsg());
}
}
}
(2)編寫控制器接口
提供給前端調(diào)用,觸發(fā)支付流程:
@RestController
@RequestMapping("/pay/alipay")
public class AlipayController {
@Autowired
private AlipayService alipayService;
/**
* 發(fā)起支付寶支付
*/
@PostMapping("/create")
public Result<String> createPay(@RequestBody PayDTO payDTO) {
try {
// 生成唯一商戶訂單號(可基于UUID或雪花算法)
String outTradeNo = "ORDER_" + System.currentTimeMillis();
// 調(diào)用服務生成支付表單
String payForm = alipayService.createPayOrder(
outTradeNo,
payDTO.getTotalAmount(), // 金額(如"0.01"元)
payDTO.getSubject(), // 訂單標題(如"測試訂單")
"http://localhost:8080/pay/success", // 同步回調(diào)地址(前端頁面)
"http://你的服務器公網(wǎng)地址/pay/alipay/notify" // 異步通知地址(需公網(wǎng)可訪問)
);
return Result.success(payForm);
} catch (Exception e) {
return Result.error("支付創(chuàng)建失?。? + e.getMessage());
}
}
}
(3)處理異步支付通知(核心?。?/h6>
支付寶在用戶支付成功后,會主動向notifyUrl發(fā)送 POST 請求(攜帶支付結果),商戶需驗證通知合法性并更新訂單狀態(tài)。
@PostMapping("/notify")
public String handleNotify(HttpServletRequest request) {
try {
// 1. 獲取請求參數(shù)(支付寶通知的所有參數(shù))
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String key : requestParams.keySet()) {
String[] values = requestParams.get(key);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
params.put(key, valueStr);
}
// 2. 驗證簽名(關鍵!防止偽造通知)
boolean signVerified = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey, // 支付寶公鑰(配置文件中獲取)
charset, // 編碼(UTF-8)
signType // 簽名類型(RSA2)
);
if (!signVerified) {
return "fail"; // 簽名驗證失敗,返回fail(支付寶會重試通知)
}
// 3. 驗證通知參數(shù)(確保支付狀態(tài)為成功)
String tradeStatus = params.get("trade_status");
if (!"TRADE_SUCCESS".equals(tradeStatus)) {
return "fail"; // 支付未成功,不處理
}
// 4. 處理業(yè)務邏輯(更新訂單狀態(tài)、記錄支付信息等)
String outTradeNo = params.get("out_trade_no"); // 商戶訂單號
String tradeNo = params.get("trade_no"); // 支付寶交易號
String totalAmount = params.get("total_amount"); // 支付金額
// 示例:調(diào)用訂單服務更新狀態(tài)為“已支付”
orderService.updateOrderStatus(outTradeNo, tradeNo, totalAmount);
// 5. 處理完成,返回"success"(支付寶收到后停止重試)
return "success";
} catch (Exception e) {
// 異常時返回fail,支付寶會重試
return "fail";
}
}
(4)查詢支付狀態(tài)(可選)
用于主動查詢訂單支付結果(如用戶未同步跳轉時):
public String queryPayStatus(String outTradeNo) throws AlipayApiException {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", outTradeNo); // 商戶訂單號
request.setBizContent(bizContent.toString());
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
return response.getTradeStatus(); // 返回狀態(tài)(如TRADE_SUCCESS、WAIT_BUYER_PAY)
} else {
throw new RuntimeException("查詢支付狀態(tài)失?。? + response.getMsg());
}
}
2.4 測試流程
- 前端處理:將后端返回的
payForm(HTML 表單)渲染到頁面,表單會自動提交至支付寶沙箱支付頁面。 - 模擬支付:
- 使用沙箱環(huán)境提供的買家賬號登錄(非開發(fā)者賬號)。
- 輸入沙箱買家支付密碼(如
111111),完成支付。
- 驗證結果:
- 同步跳轉:支付成功后跳轉到
returnUrl(前端成功頁)。 - 異步通知:支付寶會向
notifyUrl發(fā)送通知,驗證訂單狀態(tài)是否更新為 “已支付”。 - 主動查詢:調(diào)用
queryPayStatus接口,確認狀態(tài)為TRADE_SUCCESS。
- 同步跳轉:支付成功后跳轉到
三、微信支付沙箱支付集成步驟(簡要)
微信支付沙箱流程與支付寶類似,但細節(jié)略有不同(如需要證書、沙箱密鑰需動態(tài)獲?。?/p>
3.1 前期準備
- 注冊 微信支付商戶平臺,獲取
商戶號(mch_id)。 - 申請 API 證書(在商戶平臺的「賬戶中心→API 安全」中下載,包含
apiclient_cert.p12等文件)。 - 生成 API 密鑰(32 位,用于簽名),并在商戶平臺配置。
3.2 核心差異點
沙箱密鑰獲取:
微信沙箱需先調(diào)用沙箱簽名接口獲取沙箱專用密鑰(sandbox_signkey),后續(xù)接口用該密鑰簽名。// 示例:獲取沙箱密鑰 WXPayConfig config = new MyWXPayConfig(); // 自定義配置類(含商戶號、API密鑰等) WXPay wxPay = new WXPay(config); Map<String, String> params = new HashMap<>(); params.put("mch_id", config.getMchID()); params.put("nonce_str", WXPayUtil.generateNonceStr()); params.put("sign", WXPayUtil.generateSignature(params, config.getKey())); Map<String, String> result = wxPay.sandboxnewKey(params); String sandboxSignKey = result.get("sandbox_signkey"); // 沙箱密鑰接口調(diào)用:
微信支付接口(如 JSAPI 支付)需使用沙箱網(wǎng)關(https://api.mch.weixin.qq.com/sandboxnew/),并通過WXPay類(微信 SDK)調(diào)用。// 示例:創(chuàng)建JSAPI支付訂單 Map<String, String> data = new HashMap<>(); data.put("body", "測試訂單"); data.put("out_trade_no", outTradeNo); data.put("total_fee", "1"); // 金額(單位:分) data.put("spbill_create_ip", "127.0.0.1"); data.put("notify_url", "http://你的公網(wǎng)地址/pay/wx/notify"); data.put("trade_type", "JSAPI"); data.put("openid", "用戶的openid"); // 微信用戶唯一標識 WXPay wxPay = new WXPay(new SandboxWXPayConfig(sandboxSignKey)); // 使用沙箱配置 Map<String, String> result = wxPay.unifiedOrder(data);異步通知處理:
與支付寶類似,需驗證簽名(用沙箱密鑰),并返回"<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>"確認接收。
四、注意事項
- 簽名驗證:必須嚴格驗證支付平臺的通知簽名,防止惡意請求偽造支付結果。
- 回調(diào)地址:異步通知地址(
notifyUrl)需公網(wǎng)可訪問(本地測試可使用 ngrok 等工具映射端口)。 - 冪等性處理:同一訂單可能收到多次異步通知,需確保訂單狀態(tài)更新邏輯冪等(如通過訂單號判斷是否已處理)。
- 沙箱與正式環(huán)境隔離:通過配置文件區(qū)分沙箱和正式環(huán)境的參數(shù)(網(wǎng)關、密鑰等),避免混淆。
- 異常處理:接口調(diào)用超時、網(wǎng)絡錯誤等情況需重試,避免訂單狀態(tài)不一致。
通過以上步驟,可在 Java 項目中完整集成沙箱支付,驗證支付全流程的正確性后,再切換至正式環(huán)境即可上線。
到此這篇關于java配置沙箱支付的實現(xiàn)示例的文章就介紹到這了,更多相關java 沙箱支付內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
深入理解Java8新特性之Lambda表達式的基本語法和自定義函數(shù)式接口
Lambda 表達式,也可稱為閉包,它是推動 Java 8 發(fā)布的最重要新特性。Lambda 允許把函數(shù)作為一個方法的參數(shù)(函數(shù)作為參數(shù)傳遞進方法中)。使用 Lambda 表達式可以使代碼變的更加簡潔緊湊2021-11-11
使用MybatisPlus實現(xiàn)sql日志打印優(yōu)化
本文主要介紹了使用MybatisPlus實現(xiàn)sql日志打印優(yōu)化,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-08-08
Spring Boot Feign服務調(diào)用之間帶token問題
這篇文章主要介紹了Spring Boot Feign服務調(diào)用之間帶token的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09

