1:先創建測試環境(沙箱測試)。支付寶官網上有詳細的步驟介紹。
2:下載官方 demo。
https://os.alipayobjects.com/rmsportal/kDBdHXwcnGxkVeBkomFL.zip
3:添加阿里巴巴支付接口的SDK
<!--阿里巴巴支付sdk--> <!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>3.1.0</version> </dependency>
4:把官方demo中 的AlipayConfig復制到自己的項目。然后把里面的參數修改成自己創建的測試環境的參數。
package com.betteryanwo.pay.config; import java.io.FileWriter; import java.io.IOException; /
* *
*類名:AlipayConfig
*功能:基礎配置類
*詳細:設置帳戶有關信息及返回路徑
*修改日期:2017-04-05
*說明:
*以下代碼只是為了方便商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,並非一定要使用該代碼。
*該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓請在這里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號
public static String app_id = "2016102600767136";
// 商戶私鑰,您的PKCS8格式RSA2私鑰
public static String merchant_private_key = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCFX/ctPAzn/+EHArB6y+XbuPBPysKnmWtpkXcIckTIiHaNsTryUt3NvXKuu1gqarOvF0hk/9ChQAnRL/P6OxKy3/WG9vt0e0bMq41l7GzziLK35YKY48Tvx585jHY3Gub7Vf1ZDXCH3uhAECbnWUhX88Eaj0qFIoAADN1tER/tfCtoTmwMBqbKgkcsZu6AVHJ9o3hvC7MOVIo0Av+MCE/TjVHMoEMfmyjMi9xFq/59pWqnWNFfNorFir4nGTg4U5OTAfyfxOxaZ1WrqUozRXqFH7c/w5yfKcrvARGwZ8IDha6dtN7jKhitE8ZTqUCbVSVIuEblDBj9rd3iHMa7kcWlAgMBAAECggEAbNAqfQEtFmowUOPkz4picaykgrTNsqb8dxhyoLV5YJHVByy9Bs6MnzocwmmeOJztE8BPE/eRYr04RS52QoTHcKA/VGlUJWgeDwU3SxxklxvDkzzwG4RYO7gFM5JhL/wGzq8NDwZj9LXrjbifxMeWugroVJ7rEUtgWqokTMNX5QvnRsETliEOGPyrHQs3l7Vvnbhb7DSSqY8Ukn74jHqp6qKOipi9lACbZ+hEEkcaD1tFFLyW4v5rx4C8tfFbCMTPgoOFSrwLDluvJwg2aQ+gM00FCcbAzbFkEGjETcBMkOjeP21V+BgPBDFbFZK0aHefgeS5D2eQjbRPtHxn19j8AQKBgQC+lrldZB/zTa1fYcqA/jk7unVq3PH6+tgFk0q+bkbQGWwgBqUNPSoXWOTunADCng2ThHkf+WriiKb2gHkU4xEd7lxL33ewR3GsmczB2Z1KhA/bBHs3FFOFb3Tm9SCW/asZwNaYUFT3PKNVLaeVW34iIjQwTA2h00BWX1f/VY9UZQKBgQCzJmJQNzqfXl4H3LyKwgeRS9icE0uJsyUyDrqdVYKTBpSsVp4wq/bExCD7XPhzoZfH5DiFI+NzC2ZCTee9x+buuRsXRzze3uRvlSsMJU5InoBzfzvb74acg8HE2hcRM17T3skRcKgDfB60zBS8B7m13cbk6uv3F7Zp6u8VU0Z4QQKBgQCjXm28SSQ+GZqAhUHheomy0izuEkB2Q+lCNjS+saaPoQsXFLsMcx6ObsumYSEhsfuvNMHjD5h4YGzGJfAkYFN+1aBWBVMCTut46Ukj7Zavli5FLj00RH8nhRevFfX+l6i/5ZXNcCa020yiqgTZuViQh8M2lyqkSYH/x7CVlVDMJQKBgQCMCyJpcHMMJ/m7jat5kOnXhAHlKBQxpXAbLHZnDXRQOKG6AtAQXi0+bc8Wj/vMcHK1GMuyEHb59NxQUpQov/7feKYb0V1dH8JVE7ed+/+xNu5U2w5RBHInIbw0hQPpN04Ws55cWsOMTde3H53JywQ6jg6v3pDtDIVVUQjqk7mPwQKBgH+EUTZerrICi051K5sRNNQf7cvH6aeMQ5/Au5b+qkpb9YwBfoH9ZhoIRGjQZ8klsnCuT0dzYRis7ia4vpIqKtpuGo+zRSEWEYUIrSneR5f1Xaq2w6CMfUQohNmSikI8QiVz254sZ+smiYMP/cVFeg4WILYmsjXT3N+/KG87EV8O";
// 支付寶公鑰,查看地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。
public static String alipay_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo6Df262okFXG9JRggTn4r11kXpK3vhe2G7BLGNNGq02FhOGWcxAgprYazXsOj8fWzWlB7jGsTbaYsExNxHFnhIPL7aYu+tN4/7052k+EiMMwnCh9FD1UC/mFmBVGbPjHOclL1IWOgIXM4K2Woh6/0H/BMOH9zOEYXm2MlVvoHywk8TIxxaf+HaKsyW7r9zrV6dJftOUHUVNJf5vc/qPZfquWTQeDhesS87KZm6eycWN+HJpFj/tnBzGnxQMbqdAM4wL+Vzh19v316UtF8Fmb7yCHmLCEML9nKRMEUOr1Eaxhe6M8wsutp3Gd7JEjhE57W1jXIk73N62Nt6rLmUM15wIDAQAB";
// 服務器異步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問
public static String notify_url = "http://127.0.0.1:8080/JustCodeMusic_war_exploded/notify_url.jsp";
// 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問,請求的同步地址
public static String return_url = "http://127.0.0.1:8080/JustCodeMusic_war_exploded/AliPayServlet.succes";
// 簽名方式
public static String sign_type = "RSA2";
// 字符編碼格式
public static String charset = "utf-8";
// 支付寶網關
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
// 支付寶網關
public static String log_path = "C:\\";
//↑↑↑↑↑↑↑↑↑↑請在這里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
/**
* 寫日志,方便測試(看網站需求,也可以改成把記錄存入數據庫)
* @param sWord 要寫入日志里的文本內容
*/
public static void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(sWord);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
@WebServlet(name = "AliPayServlet",urlPatterns = "/AliPayServlet")
public class AliPayServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//獲得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
//設置請求參數
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(AlipayConfig.return_url);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
//商戶訂單號,商戶網站訂單系統中唯一訂單號,必填
//這里需要去調用自己生成的id號
String out_trade_no = UUIDHelper.getOrderId();
//付款金額,必填
String total_amount = "99";
//訂單名稱,必填
String subject = "悅心vip充值";
//商品描述,可空
String body = "音樂會員vip充值,充值即可永久vip";
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//若想給BizContent增加其他可選請求參數,以增加自定義超時時間參數timeout_express來舉例說明
//alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
// + "\"total_amount\":\""+ total_amount +"\","
// + "\"subject\":\""+ subject +"\","
// + "\"body\":\""+ body +"\","
// + "\"timeout_express\":\"10m\","
// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//請求參數可查閱【電腦網站支付的API文檔-alipay.trade.page.pay-請求參數】章節
//請求
String result = null;
try {
result = alipayClient.pageExecute(alipayRequest).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}
// System.out.println(result);
//輸出
request.setCharacterEncoding("utf-8");
//// response.setCharacterEncoding("utf-8");
//// response.setHeader("Content-Type", "text/html;charset=UTF-8");
response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
out.println(result);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
支付寶同步通知。
寫一下支付流程,首先請求的一個servlet,在這里讀取信息將信息發送給支付寶,創建支付,支付成功后將會返回一些數據,這個時候在return_ url里將會驗證簽名,也就是比較數據,數據里有加密的sign,這個時候應該調用rsaCheckV2的方法進行驗簽,
boolean signVerified = true; //調用SDK驗證簽名
try {
signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); } catch (AlipayApiException e) { e.printStackTrace(); }
rsaCheckV2 和rsaCheckV1的區別分享
說明:
目前支付寶的SDK驗簽方法主要有兩種一種是rsaCheckV1一種是rsaCheckV2 兩種驗簽方法用於不同的接口的返回參數驗簽
1.rsaCheckV1驗簽方法
rsaCheckV1驗簽方法主要用於支付接口的返回參數的驗簽比如:當面付,APP支付,手機網站支付,電腦網站支付 這些接口都是使用rsaCheckV1方法驗簽的
2.rsaCheckV2驗簽方法
rsaCheckV2驗簽方法主要是用於生活號相關的事件消息和口碑服務市場訂購信息等發送到應用網關地址的異步信息的驗簽
3.驗簽出錯處理方案
驗簽出錯一般是支付寶公鑰使用錯誤導致,詳細的自查方案:https://openclub.alipay.com/read.php?tid=2432&fid=72
重點排查一下支付寶公鑰的問題
如果自查無法解決,可在該貼后面發帖追問 正確的提問方式:測試環境(沙箱&正式)+請求參數+appid
/* * * 功能:支付寶服務器同步通知頁面 * 日期:2017-03-30 * 說明: * 以下代碼只是為了方便商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,並非一定要使用該代碼。 * 該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。 *************************頁面功能說明************************* * 該頁面僅做頁面展示,業務邏輯處理請勿在該頁面執行 */
@WebServlet(name = "SuccesServlet",urlPatterns = "/AliPayServlet.succes")
public class SuccesServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
//獲取支付寶GET過來反饋信息
Map<String, String> params = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
//輸出信息
System.out.println(name+"---->>"+ Arrays.toString(values));
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//亂碼解決,這段代碼在出現亂碼時使用
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
boolean signVerified = true; //調用SDK驗證簽名
try {
//這里調用的rsaCheckV2和v1可能有區別,會導致驗簽失敗
//這里必須要用v2的方法,v1的代碼里不會調用sign進行驗簽,會導致錯誤
signVerified = AlipaySignature.rsaCheckV2(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
} catch (AlipayApiException e) {
e.printStackTrace();
}
//——請在這里編寫您的程序(以下代碼僅作參考)——
if (signVerified) {
//商戶訂單號
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
//支付寶交易號
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
//付款金額
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
System.out.println("trade_no:" + trade_no + "<br/>out_trade_no:" + out_trade_no + "<br/>total_amount:" + total_amount);
response.sendRedirect("/index.jsp");
} else {
//商戶訂單號
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
//支付寶交易號
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
//付款金額
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
//這里寫一下思路,驗簽成功后應該去將用戶的數據修改,但是現在無法在該頁面請求到數據,無法請求session,cookie,request
//在調用支付的時候將會生成商戶號的id,這時需要在數據庫創建一條數據,如果成功了將會去將數據庫的數據進行更改
//或者將用戶放在redis緩存中,但是可能會出現多個用戶支付的問題
// out_trade_no---->>[839ffd5521404901]
// out.println(AlipayConfig.alipay_public_key);
System.out.println("trade_no:" + trade_no + "<br/>out_trade_no:" + out_trade_no + "<br/>total_amount:" + total_amount);
// out.println("驗簽失敗");
}
// CustomerinfoEntity cust = (CustomerinfoEntity)request.getSession().getAttribute("cust");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
打印輸出
charset---->>[utf-8] out_trade_no---->>[cf0ed32562bd4f0a] method---->>[alipay.trade.page.pay.return] total_amount---->>[99.00] sign---->>[Gnge3aYb3oX0FRDEjE4XjxpBsrifxDyt19+Hkkfrq86Z59IP0eG8xW97D76qR8DliDpk8G/FQRUrXHI8TYIcUlHhnm7sRY83BnHdA7tCoc8DBwipkAnDbUmEPKyk1tIObFNQSDWbXF7RPL78c/b7K7uPrR3Towo9E/79Vu4H7UYOsYmugQadRWaK6sWif0ydOpiRP+3JIRWmG5HC9Tddl4HKea3n3sSnK2vi8QPp03higfJd0IAPDK/uKDiA1OOeeWX1CK+aC9M4xzJRyVy1CqRcijwWsLSh3CbJ9ak8sq6NrSrUv+ii2M3uOj1wp8NOCIftA/pDsZIbtj39kdVqZA==] trade_no---->>[2020072722001492940500907766] auth_app_id---->>[2016102600767136] version---->>[1.0] app_id---->>[2016102600767136] sign_type---->>[RSA2] seller_id---->>[2088102181191482] timestamp---->>[2020-07-27 23:06:00] trade_no:2020072722001492940500907766<br/>out_trade_no:cf0ed32562bd4f0a<br/>total_amount:99.00 charset---->>[utf-8] out_trade_no---->>[839ffd5521404901] method---->>[alipay.trade.page.pay.return] total_amount---->>[99.00] sign---->>[jfvzdnDz9jhtYli3iNNVfajXhBgXSRUeI+01965ev3kibhyvKLPFlaHckEBChvE+yApkMtyD77K0FxGP9ugCZlDxCVPNkA98veUeKFXD47okHvdikNOFFrOThWPhOdXkONTyrgMAE6LajrWDH9IfPlp6TKZJkdXuD1L/RSohkfVPlrZdPOy1ci3L9QDkKfK/0OMOBE2C35qrVmL5lcXDnUmXNGOm/WI+JWZC2ZJu9G4mpKyzz3HMRnJIJ8Jhr2Bwzs+pGswZWAFlpnvqlszM4YQJIKvjInSBku9YHm6jPdYsFJapn1hM2pwPyo+bWmHkgMdkzNs/NvyvHgZUkkYH3w==] trade_no---->>[2020072722001492940500907581] auth_app_id---->>[2016102600767136] version---->>[1.0] app_id---->>[2016102600767136] sign_type---->>[RSA2] seller_id---->>[2088102181191482] timestamp---->>[2020-07-27 22:55:43] trade_no:2020072722001492940500907581<br/>out_trade_no:839ffd5521404901<br/>total_amount:99.00
異步通知
/* * * 功能:支付寶服務器異步通知頁面 * 日期:2017-03-30 * 說明: * 以下代碼只是為了方便商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,並非一定要使用該代碼。 * 該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。 *************************頁面功能說明************************* * 創建該頁面文件時,請留心該頁面文件中無任何HTML代碼及空格。 * 該頁面不能在本機電腦測試,請到服務器上做測試。請確保外部可以訪問該頁面。 * 如果沒有收到該頁面返回的 success * 建議該頁面只做支付成功的業務邏輯處理,退款的處理請以調用退款查詢接口的結果為准。 */ @RequestMapping(value = "/notify_url") public String notifyUrl(ModelMap map,HttpServletRequest request){ try{ //獲取支付寶POST過來反饋信息 Map<String,String> params = new HashMap<String,String>(); Map<String,String[]> requestParams = request.getParameterMap(); for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } //亂碼解決,這段代碼在出現亂碼時使用 valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8"); params.put(name, valueStr); } boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //調用SDK驗證簽名 //——請在這里編寫您的程序(以下代碼僅作參考)—— /* 實際驗證過程建議商戶務必添加以下校驗: 1、需要驗證該通知數據中的out_trade_no是否為商戶系統中創建的訂單號, 2、判斷total_amount是否確實為該訂單的實際金額(即商戶訂單創建時的金額), 3、校驗通知中的seller_id(或者seller_email) 是否為out_trade_no這筆單據的對應的操作方(有的時候,一個商戶可能有多個seller_id/seller_email) 4、驗證app_id是否為該商戶本身。 */ if(signVerified) {//驗證成功 //商戶訂單號 String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8"); //支付寶交易號 String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8"); //交易狀態 String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8"); if(trade_status.equals("TRADE_FINISHED")){ //判斷該筆訂單是否在商戶網站中已經做過處理 //如果沒有做過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序 //如果有做過處理,不執行商戶的業務程序 //注意: //退款日期超過可退款期限后(如三個月可退款),支付寶系統發送該交易狀態通知 }else if (trade_status.equals("TRADE_SUCCESS")){ //判斷該筆訂單是否在商戶網站中已經做過處理 //如果沒有做過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序 //如果有做過處理,不執行商戶的業務程序 //注意: //付款完成后,支付寶系統發送該交易狀態通知 } System.out.println("success"); map.put("alipayResult","成功"); }else {//驗證失敗 System.out.println("fail"); map.put("alipayResult","失敗"); //調試用,寫文本函數記錄程序運行情況是否正常 //String sWord = AlipaySignature.getSignCheckContentV1(params); //AlipayConfig.logResult(sWord); } }catch (Exception e){ } return "/pay/notify_url"; }