1. 程式人生 > >支付寶沙箱之電腦網站支付

支付寶沙箱之電腦網站支付

《支付寶沙箱連結》

《電腦網站支付快速接入文件》

引入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");