首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 整合支付寶即時到賬(第三方網關)完整實戰代碼

     編輯:Java 相關知識

業務流程概覽

  1. 使用者下單 → 後台生成訂單號、金額、商品資訊 → 存入資料庫(狀態:待付款)。
  2. 跳轉支付寶收銀台頁面(alipay.jsp),表單自動提交至支付寶網關。
  3. 使用者完成付款 → 支付寶同步跳轉 return_url、異步通知 notify_url。
  4. 後台驗證簽名、更新訂單狀態(成功/失敗)→ 業務邏輯處理(發貨、充值等)。

核心工具類(官方 SDK 精簡版)

1. AlipayConfig:配置常數

public class AlipayConfig {
    public static final String PARTNER = "2088601003079118";      // 合作夥伴ID
    public static final String KEY = "zxcdvxgksaam2zjrmv5cv0p4jqesaioh"; // 安全校驗碼
    public static final String SELLER_EMAIL = "test@yahoo.com.cn"; // 賣家支付寶帳號
    public static final String NOTIFY_URL = "https://www.xxx.com/project/alipayTrade";
    public static final String RETURN_URL = "https://www.xxx.com/project/returnUrl";
    public static final String SHOW_URL = "https://www.alipay.com";
    public static final String INPUT_CHARSET = "UTF-8";
    public static final String SIGN_TYPE = "MD5";
    public static final String TRANSPORT = "http"; // 或 https
}

2. Md5Encrypt:MD5 簽名

public class Md5Encrypt {
    private static final char[] DIGITS = "0123456789abcdef".toCharArray();
    public static String md5(String text) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(text.getBytes(AlipayConfig.INPUT_CHARSET));
            byte[] bytes = md.digest();
            char[] out = new char[bytes.length * 2];
            for (int i = 0, j = 0; i < bytes.length; i++) {
                out[j++] = DIGITS[(0xF0 & bytes[i]) >>> 4];
                out[j++] = DIGITS[0x0F & bytes[i]];
            }
            return new String(out);
        } catch (Exception e) { throw new RuntimeException(e); }
    }
}

3. AlipayFunction:核心工具方法

public class AlipayFunction {
    // 過濾空值、sign、sign_type
    public static Map paraFilter(Map params) { ... }

    // 參數排序並拼接 key=value&key=value...
    public static String createLinkString(Map params) { ... }

    // 生成簽名:MD5(排序拼接字串 + KEY)
    public static String buildMysign(Map params, String key) {
        return Md5Encrypt.md5(createLinkString(paraFilter(params)) + key);
    }

    // 寫日誌、查詢時間戳防釣魚等輔助方法...
}

4. AlipayNotify:異步通知驗證

public class AlipayNotify {
    // 驗證簽名
    public static boolean verifySign(Map params, String key) {
        String mysign = AlipayFunction.buildMysign(AlipayFunction.paraFilter(params), key);
        return mysign.equals(params.get("sign"));
    }

    // 遠程驗證 notify_id(防偽造通知)
    public static String verifyNotifyId(String notifyId) {
        String url = (AlipayConfig.TRANSPORT.equals("https") ? 
            "https://www.alipay.com/cooperate/gateway.do" : 
            "https://notify.alipay.com/trade/notify_query.do")
            + "?partner=" + AlipayConfig.PARTNER + "¬ify_id=" + notifyId;
        // HTTP GET 獲取回應 "true"/"false"/"invalid"
        return HttpUtil.get(url); // 需自行實作
    }
}

5. AlipayService:生成自動提交表單

public class AlipayService {
    public static String buildForm(...) {
        Map params = new HashMap<>();
        params.put("service", "create_direct_pay_by_user");
        params.put("payment_type", "1");
        params.put("partner", AlipayConfig.PARTNER);
        params.put("seller_email", AlipayConfig.SELLER_EMAIL);
        params.put("return_url", AlipayConfig.RETURN_URL);
        params.put("notify_url", AlipayConfig.NOTIFY_URL);
        params.put("_input_charset", AlipayConfig.INPUT_CHARSET);
        params.put("out_trade_no", outTradeNo);      // 我方訂單號
        params.put("subject", subject);              // 商品標題
        params.put("body", body);                    // 商品描述
        params.put("total_fee", totalFee);           // 金額
        params.put("paymethod", "");                 // 預設支付方式
        params.put("defaultbank", "");               // 預設銀行
        params.put("anti_phishing_key", "");         // 防釣魚時間戳
        params.put("exter_invoke_ip", "");           // 客戶端IP
        params.put("extra_common_param", "");        // 自定義參數
        params.put("buyer_email", "");               // 買家帳號
        params.put("it_b_pay", "1h");                // 超時關單

        Map filtered = AlipayFunction.paraFilter(params);
        String sign = AlipayFunction.buildMysign(filtered, AlipayConfig.KEY);

        StringBuilder sb = new StringBuilder();
        sb.append("
"); for (Map.Entry e : filtered.entrySet()) { sb.append(""); } sb.append(""); sb.append(""); sb.append(""); sb.append(""); return sb.toString(); } }

Controller 層串聯

1. 下單並跳轉收銀台

public String toPay(HttpServletRequest req) {
    String orderNo = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
    req.setAttribute("out_trade_no", orderNo);
    req.setAttribute("totalMoney", "0.01"); // 測試金額
    req.setAttribute("subject", "測試商品");
    req.setAttribute("body", "測試描述");
    // 此處應保存訂單至資料庫,狀態=待付款
    return "alipay"; // 對應 alipay.jsp
}

2. alipay.jsp:輸出表單 HTML

<%@ page import="com.test.constants.AlipayConfig, com.test.util.alipay.AlipayService" %>
<%
    String sHtml = AlipayService.buildForm(
        AlipayConfig.PARTNER, AlipayConfig.SELLER_EMAIL,
        AlipayConfig.RETURN_URL, AlipayConfig.NOTIFY_URL,
        AlipayConfig.SHOW_URL,
        (String)request.getAttribute("out_trade_no"),
        (String)request.getAttribute("subject"),
        (String)request.getAttribute("body"),
        (String)request.getAttribute("totalMoney"),
        "", "", "", "", "", "", "", "", "",
        AlipayConfig.INPUT_CHARSET, AlipayConfig.KEY, AlipayConfig.SIGN_TYPE, ""
    );
%>
<html><body>
<table align="center">
<tr><td>訂單號:<%=request.getAttribute("out_trade_no")%></td></tr>
<tr><td>金額:<%=request.getAttribute("totalMoney")%></td></tr>
<tr><td colspan="2"><%=sHtml%></td></tr>
</table>
</body></html>

3. 異步通知處理


@Controller
@RequestMapping("/alipay")
public class AlipayNotifyAction {
    @RequestMapping("/notify")
    @ResponseBody
    public String notify(HttpServletRequest req) {
        Map params = new HashMap<>();
        Enumeration en = req.getParameterNames();
        while (en.hasMoreElements()) {
            String k = en.nextElement();
            params.put(k, req.getParameter(k));
        }

        // 1. 驗證簽名
        if (!AlipayNotify.verifySign(params, AlipayConfig.KEY)) return "fail";

        // 2. 遠程驗證 notify_id
        if (!"true".equals(AlipayNotify.verifyNotifyId(params.get("notify_id")))) return "fail";

        // 3. 業務判斷
        String outTradeNo = params.get("out_trade_no");
        String tradeStatus = params.get("trade_status");
        if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
            Order order = orderService.findByOutTradeNo(outTradeNo);
            if (order != null && order.getStatus() == 0) { // 防重複處理
                order.setStatus(1);
                order.setTradeNo(params.get("trade_no"));
                order.setNotifyTime(new Date());
                orderService.update(order);
                // 觸發下游業務:發貨、充值、發券...
            }
        }
        return "success"; // 必須回傳 "success" 否則支付寶會重試通知
    }
}

關鍵安全與最佳實踐

  • 金額單位: 支付寶以「元」為單位,精確到分,傳字串避免浮點誤差。
  • 冪等性: 通知可能重複,必須用 out_trade_no + 狀態機制防重複處理。
  • 簽名驗證雙重保障: 本地 MD5 驗證 + 遠程 notify_id 驗證。
  • HTTPS: 生產環境強制 TRANSPORT=https,網關地址用 https://mapi.alipay.com/gateway.do。
  • 參數編碼: 統一 UTF-8,URL 參數需 URLEncoder.encode。
  • 測試環境: 使用支付寶沙箱環境,配置沙箱 Partner/Key。

總結

本文提供從配置、簽名工具、表單生成、Controller 串聯到異步通知驗證的完整支付寶即時到賬整合代碼。核心邏輯集中在 AlipayFunction(參數處理與簽名)與 AlipayNotify(通知驗證),業務層僅需關注訂單狀態流轉。現代專案建議直接引入官方 alipay-sdk-java 依賴,減少手工維護成本。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved