基於 Spring MVC + Dubbo 架構,僅對外暴露 REST 接口。本文覆蓋商戶配置、統一下單生成二維碼、支付回調處理、退款流程及關鍵安全點。
apiclient_cert.p12,退款、紅包等接口必須雙向證書校驗。public class ConfigUtil {
public static final String APP_ID = "wx_xxx"; // 服務號 AppID
public static final String MCH_ID = "1234567890"; // 商戶號
public static final String API_KEY = "xxxxxxxxxxxx"; // API 密鑰
public static final String CERT_PATH = "/path/apiclient_cert.p12";
public static final String NOTIFY_URL = "https://api.yourdomain.com/weixin/pay/notify";
public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// ... 其他接口 URL
public static void commonParams(SortedMap<Object, Object> params) {
params.put("appid", APP_ID);
params.put("mch_id", MCH_ID);
params.put("nonce_str", generateNonceStr()); // 隨機字符串
}
}
trade_type=NATIVE、product_id(商品 ID)、notify_url、body、out_trade_no(商户訂單號)、total_fee(分)等。unifiedorder 接口。code_url,前端生成二維碼供用戶掃碼。關鍵點:code_url 格式為 weixin://wxpay/bizpayurl?sr=xxxxx,可直接生成 QR 碼;也可調用 shorturl 接口轉短鏈接提升掃描速度。
@Controller
@RequestMapping("/weixin")
public class WeixinPayController {
@Autowired private IWeixinPayService weixinPayService;
@RequestMapping("/pay/notify")
public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 讀取輸入流,轉字符串
String xml = IOUtils.toString(request.getInputStream(), "UTF-8");
// 2. XML 轉 Map
Map<String, String> map = XMLUtil.parse(xml);
// 3. 簽名驗證
if (!PayCommonUtil.verifySign(map, ConfigUtil.API_KEY)) {
log.error("簽名驗證失敗");
return;
}
// 4. 判斷業務結果
if ("SUCCESS".equals(map.get("result_code"))) {
String outTradeNo = map.get("out_trade_no");
String attach = map.get("attach"); // 附加數據,可存業務標識
// 業務處理:更新訂單狀態、發貨、發券等
weixinPayService.handleSuccess(outTradeNo, attach);
// 5. 應答微信:必須返回 SUCCESS,否則微信會重試通知(共 8 次)
response.getWriter().write(" ");
} else {
log.warn("支付失敗: " + map.get("err_code_des"));
response.getWriter().write(" ");
}
}
}
out_trade_no + transaction_id 去重處理。total_fee 與訂單金額一致。.p12 文件嚴禁提交代碼倉庫,運行時從加密配置中心/密管平台加載。total_fee 單位是「分」,易漏乘 100。