天天看點

第三方支付——支付寶web端支付(java)

這段時間把支付基本搞完了,因為做的過程中遇到許多問題,特地記錄下來,同時友善其他java coder,廢話少說,下面開始。

整體思路:在背景,根據參數建立支付寶用戶端AlipayClient,發送參數到支付寶,支付寶直接傳回一個表單,我們隻需要将表單輸出到頁面上,後續支付寶異步通知,比較重要是驗簽,支付寶也提供的工具,比較友善。

(jar包或maven的引入這裡省略)

1、申請支付寶支付,這裡大家自己研究,網上很多教程。

2、建立支付

/**
 * 調用支付寶支付alipay.trade.page.pay
 * 商戶系統請求支付寶接口alipay.trade.page.pay,支付寶對商戶請求參數進行校驗,而後重定向至使用者登入頁面。
 *
 * @param model
 * @return
 * @throws Exception
 */
public String createAlipay(Model model, String order_no, BigDecimal amount, Integer resource_trad_id, String trad_type, HttpServletResponse response) throws Exception {
    String form = "";
    User user = (User) model.asMap().get("user");
    //生成一筆預付訂單流水
    String trad_no = "PC_ALIPAY" + OrderNoUtil.leadsNo();//訂單流水号
    ShareUserTrad trad = new ShareUserTrad();
    trad.setResourceTradId(resource_trad_id);
    trad.setUserId(user.getId());
    trad.setCreatedBy(user.getId());
    trad.setLastUpdBy(user.getId());
    trad.setOnlineOfflineFlag("0");//線上
    trad.setOrderNo(order_no);
    trad.setUserTradAmount(amount);
    trad.setTradMethod("3");//支付寶
    trad.setPayReceiveFlag("2");//支出
    trad.setSuccessFlag("0");//交易進行中
    trad.setTradType("1");//訂單支付
    trad.setTradNo(trad_no);
    trad.setModifyNum(0);
    shareUserTradMapper.insertSelective(trad);
    try {
        //初始化用戶端
        AlipayClient alipayClient = new DefaultAlipayClient(Config.alipay_url, Config.alipay_appid, Config.alipay_app_private_key, Config.alipay_format, Config.alipay_charset, Config.alipay_app_public_key, Config.alipay_sign_type);
        //建立API對應的request
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl("");//回退到訂單清單頁面
        alipayRequest.setNotifyUrl("");//在公共參數中設定回跳和通知位址
        alipayRequest.setBizContent("{" +
                "    \"out_trade_no\":\"" + trad_no + "\"," +
                "    \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
                //"    \"total_amount\":" + amount.toString() + "," +
                "\"total_amount\":\"0.01\"," +
                "    \"subject\":\"訂單支付\"," +
                "    \"body\":\"訂單:" + order_no + "支付\"," +
                "    \"passback_params\":\"" + order_no + "\"" +
                "  }");//填充業務參數
        form = alipayClient.pageExecute(alipayRequest).getBody(); //調用SDK生成表單
    } catch (Exception e) {
        e.printStackTrace();
        String sOut = "";
        StackTraceElement[] trace = e.getStackTrace();
        for (StackTraceElement s : trace) {
            sOut += "\tat " + s + "\r\n";
        }
        model.addAttribute("failMsg", sOut + "alipay_url:" + Config.alipay_url);
        return "/pay/payFail";
    }
    response.setContentType("text/html;charset=" + Config.alipay_charset);
    response.getWriter().write(form);//直接将完整的表單html輸出到頁面
    response.getWriter().flush();
    response.getWriter().close();
    return null;
}      

這裡注意幾點:

        ①上面那個建立預付訂單流水,主要用于後面支付寶異步通知,訂單流水号需要傳給支付寶,這樣支付寶回調通知的時候,會把這個參數傳回來。

        ②

alipayRequest.setReturnUrl("");//回退到訂單清單頁面      

這個位址為使用者掃碼成功後,支付寶會在五秒後從支付頁面跳轉到的頁面。

        ③

alipayRequest.setNotifyUrl("");//在公共參數中設定回跳和通知位址      

這個位址為支付寶支付異步通知的位址。這個位址必須要是外網可通路的,如果大家在本地測試,需要把本機映射到外網上去,這裡推薦大家使用ngrok,注冊後是可以免費使用的。

上面兩個位址出于隐私考慮我這裡是空的,大家要加上。

3、支付寶異步通知

/**
 * 接受支付寶異步通知
 */
@RequestMapping(value = "/notify", method = RequestMethod.POST)
@Transactional(readOnly = false)
public void alipayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
    Map<String, String> map = new HashMap<String, String>();
    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] + ",";
        }
        //亂碼解決,這段代碼在出現亂碼時使用。如果mysign和sign不相等也可以使用這段代碼轉化
        //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
        map.put(name, valueStr);
    }
    System.out.println("支付結果---:" + map.toString());
    //調用SDK驗證簽名
    boolean signVerified = false;
    try {
        signVerified = AlipaySignature.rsaCheckV1(map, Config.alipay_app_public_key, Config.alipay_charset, Config.alipay_sign_type);
    } catch (Exception e) {
        e.printStackTrace();
    }
    if (signVerified) {
        System.out.println("支付結果---:" + map.toString());
        String trad_no = map.get("out_trade_no");
        //根據交易流水号查詢交易資訊
        if ("TRADE_SUCCESS".equals(map.get("trade_status"))) {//交易成功
            DealUserTradModel dealUserTradModel = new DealUserTradModel();
            dealUserTradModel.setUser_account_name("");
            dealUserTradModel.setUser_account(map.get("buyer_id"));
            dealUserTradModel.setTrad_no(trad_no);
            dealUserTradModel.setPay_amount(new BigDecimal(map.get("total_amount")));
            dealUserTradModel.setCompany_amount(new BigDecimal(map.get("receipt_amount")));
            dealUserTradModel.setOut_trad_no(map.get("trade_no"));
            payService.dealTrad(dealUserTradModel);
        }
        try {
            PrintWriter out = response.getWriter();
            out.print("success");
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // TODO 驗簽成功後,按照支付結果異步通知中的描述,對支付結果中的業務内容進行二次校驗,校驗成功後在response中傳回success并繼續商戶自身業務處理,校驗失敗傳回failure
    } else {
        // TODO 驗簽失敗則記錄異常日志,并在response中傳回failure.
        try {
            PrintWriter out = response.getWriter();
            out.print("failure");
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}      

收到通知,将交易流水的狀态更新就可以了,這裡用到支付寶回傳的交易流水号。

4、幾個參數

Config.alipay_url      

這個參數是支付寶API位址,如果大家是在沙箱上面測試,則位址為:https://openapi.alipaydev.com/gateway.do,如果是正式環境,為:https://openapi.alipay.com/gateway.do.

Config.alipay_appid      

這個參數是支付寶配置設定的APP ID,如果是沙箱環境,則在https://openhome.alipay.com/platform/appDaily.htm?tab=info這裡看:

第三方支付——支付寶web端支付(java)

如果是正式環境,則在螞蟻金服開方平台-->開發者中心-->網頁&移動應用,選擇應用點檢視,左上角的icon旁邊.

Config.alipay_app_public_key      

這個參數是支付寶公鑰,如果是沙箱環境,在這裡看:

第三方支付——支付寶web端支付(java)

正式環境,地方跟上面的差不多。

Config.alipay_charset      

這個參數是編碼格式,推薦跟你的項目編碼一緻。

Config.alipay_sign_type      

這個參數是簽名類型,推薦使用RSA2。

Config.alipay_app_private_key      

這個參數是你的應用私鑰,用支付寶提供的秘鑰生成工具生成的應用私鑰。

Config.alipay_format      

這個參數就是字元串json。

繼續閱讀