alipay.jsp),表單自動提交至支付寶網關。return_url、異步通知 notify_url。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
}
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); }
}
}
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);
}
// 寫日誌、查詢時間戳防釣魚等輔助方法...
}
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); // 需自行實作
}
}
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("");
sb.append("");
return sb.toString();
}
}
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
}
<%@ 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>
@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 + 狀態機制防重複處理。notify_id 驗證。TRANSPORT=https,網關地址用 https://mapi.alipay.com/gateway.do。URLEncoder.encode。本文提供從配置、簽名工具、表單生成、Controller 串聯到異步通知驗證的完整支付寶即時到賬整合代碼。核心邏輯集中在 AlipayFunction(參數處理與簽名)與 AlipayNotify(通知驗證),業務層僅需關注訂單狀態流轉。現代專案建議直接引入官方 alipay-sdk-java 依賴,減少手工維護成本。