支付寶沙箱之電腦網站支付
阿新 • • 發佈:2018-11-27
《支付寶沙箱連結》
《電腦網站支付快速接入文件》
引入SKD:
Maven依賴
程式碼:
public class AlipayVO implements Serializable { private static final long serialVersionUID = 1L; /** * 訂單名稱 */ private String subject; /** * 商戶網站唯一訂單號 */ private String out_trade_no; /** * 該筆訂單允許的最晚付款時間 */ private String timeout_express; /** * 付款金額 */ private String total_amount; /** * 銷售產品碼,與支付寶簽約的產品碼名稱 */ private String product_code; // 省略getter和setter }
/* * *類名:AlipayConfig *功能:基礎配置類 *詳細:設定帳戶有關資訊及返回路徑 *修改日期:2017-04-05 *說明: *以下程式碼只是為了方便商戶測試而提供的樣例程式碼,商戶可以根據自己網站的需要,按照技術文件編寫,並非一定要使用該程式碼。 *該程式碼僅供學習和研究支付寶介面使用,只是提供一個參考。 */ public class AlipayConfig { //↓↓↓↓↓↓↓↓↓↓請在這裡配置您的基本資訊↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // 應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號 public static String app_id = ""; // 商戶私鑰,您的PKCS8格式RSA2私鑰 public static String merchant_private_key = ""; // 支付寶公鑰,檢視地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。 public static String alipay_public_key = ""; // 伺服器非同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義引數,必須外網可以正常訪問 public static String notify_url = "http://vz6snm.natappfree.cc/ocPortal/alipay/notify"; // 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義引數,必須外網可以正常訪問 public static String return_url = "http://localhost/ocPortal/alipay/return"; // 簽名方式 public static String sign_type = "RSA2"; // 字元編碼格式 public static String charset = "UTF-8"; // 支付寶閘道器 // 正式環境 // public static String gateway_url = "https://openapi.alipay.com/gateway.do"; // 沙箱環境 public static String gateway_url = "https://openapi.alipaydev.com/gateway.do"; //↑↑↑↑↑↑↑↑↑↑請在這裡配置您的基本資訊↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ }
@RequestMapping("/alipay") @Controller public class AlipayController { /** * 支付網站掃碼支付介面-統一下單支付介面 * @return * @throws AlipayApiException */ @RequestMapping(value = "/pay", method = RequestMethod.GET) @ResponseBody private String alipayPay(@RequestParam("subscribeId") Long subscribeId) throws AlipayApiException { UserSubscribe userSubscribe = subscribeService.getById(subscribeId); AlipayVO vo = new AlipayVO(); vo.setOut_trade_no(UUID.randomUUID().toString().replace("-", "")); vo.setTotal_amount("0.01"); vo.setSubject("商品名稱"); vo.setProduct_code("FAST_INSTANT_TRADE_PAY"); //這個是固定的 String json = new Gson().toJson(vo); AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gateway_url, 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); alipayRequest.setBizContent(json); String result = alipayClient.pageExecute(alipayRequest).getBody(); return result; //這裡生成一個表單,會自動提交 } /** * 支付寶伺服器非同步通知頁面 * @param request * @param out_trade_no 商戶訂單號 * @param trade_no 支付寶交易憑證號 * @param trade_status 交易狀態 * @return * @throws AlipayApiException */ @RequestMapping("/notify") @ResponseBody public String alipayNotify(HttpServletRequest request, String out_trade_no, String trade_no, String trade_status) throws AlipayApiException { Map<String, String> params = getParamsMap(request); System.out.println("notify out_trade_no:" + out_trade_no); System.out.println("notify trade_no:" + trade_no); System.out.println("notify trade_status:" + trade_status); System.out.println("notify subject" + params.get("subject")); // 驗證簽名 boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); System.out.println("notify signVerified:" + signVerified); if (signVerified) { // 處理業務邏輯,更細訂單狀態等 // 支付成功 if("TRADE_SUCCESS".equals(trade_status)){ // return success可阻止支付寶繼續回撥 return "success"; } return "fail"; } else { return "fail"; } } /** * 支付寶伺服器同步通知頁面 * @param request * @param out_trade_no 商戶訂單號 * @param trade_no 支付寶交易憑證號 * @param total_amount 交易狀態 * @return * @throws AlipayApiException */ @RequestMapping("/return") public String alipayReturn(HttpServletRequest request, String out_trade_no, String trade_no, String total_amount) throws AlipayApiException { Map<String, String> params = getParamsMap(request); System.out.println("return out_trade_no:" + out_trade_no); System.out.println("return trade_no:" + trade_no); System.out.println("return total_amount:" + total_amount); // 驗證簽名 boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); System.out.println("return signVerified:"+signVerified); if(signVerified) { } else { } return "redirect:/user/subscribe.html"; } private Map<String, String> getParamsMap(HttpServletRequest request) { Map<String,String> params = new HashMap<>(); Map requestParams = request.getParameterMap(); for (Iterator 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] + ","; } //亂碼解決,這段程式碼在引數出現亂碼時使用 // try { // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "UTF-8"); params.put(name, valueStr); // } catch (UnsupportedEncodingException e) { // e.printStackTrace(); // } } return params; } }
注意:
1、非同步回撥的路徑必須暴露在外網下,而同步回撥路徑可根據環境選擇
2、非同步回撥路徑是被支付寶呼叫的,所以需要注意是否會被自己系統的登入攔截功能所攔截
3、修改訂單的狀態最好在非同步回撥裡處理,同步回撥只負責使用者瀏覽器的跳轉
4、AlipayConfig的引數需要確保正確
問題:
1、支付寶回撥驗籤失敗解決辦法
支付寶有兩個過載的方法。如果使用的是RSA加密的話,就呼叫下面這個方法。
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, "UTF-8");
如果使用的是RSA2加密的話,就呼叫下面這個方法。
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, "UTF-8", "RSA2");
2、支付寶同步和非同步驗簽結果不一致的解決方法
同步驗籤正確、而非同步驗籤錯誤,可能是引數中有中文亂碼導致的,一般就是subject這個引數,可以在回撥函式中打印出該引數,檢視是否亂碼。
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "UTF-8");