官方文檔
准備工作:已通過微信認證的公眾號,必須通過ICP備案域名(否則會報支付失敗)
借鑒了很多大神的文章,在此先謝過了
大體過程:先掃碼(還沒有確定實際要支付的金額),這個碼是商品的二維碼,再生成訂單,適用於自動販賣機之類固定金額的。
模式一支付的流程如下圖,稍微有點復雜
業務流程說明:
(1)商戶后台系統根據微信支付規定格式生成二維碼(規則見下文),展示給用戶掃碼。
(2)用戶打開微信“掃一掃”掃描二維碼,微信客戶端將掃碼內容發送到微信支付系統。
(3)微信支付系統收到客戶端請求,發起對商戶后台系統支付回調URL的調用。調用請求將帶productid和用戶的openid等參數,並要求商戶系統返回交數據包
(4)商戶后台系統收到微信支付系統的回調請求,根據productid生成商戶系統的訂單。
(5)商戶系統調用微信支付【統一下單API】請求下單,獲取交易會話標識(prepay_id)。
(6)微信支付系統根據商戶系統的請求生成預支付交易,並返回交易會話標識(prepay_id)。
(7)商戶后台系統得到交易會話標識prepay_id(2小時內有效)。
(8)商戶后台系統將prepay_id返回給微信支付系統。
(9)微信支付系統根據交易會話標識,發起用戶端授權支付流程。
(10)用戶在微信客戶端輸入密碼,確認支付后,微信客戶端提交支付授權。
(11)微信支付系統驗證后扣款,完成支付交易。
(12)微信支付系統完成支付交易后給微信客戶端返回交易結果,並將交易結果通過短信、微信消息提示用戶。微信客戶端展示支付交易結果頁面。
(13)微信支付系統通過發送異步消息通知商戶后台系統支付結果。商戶后台系統需回復接收情況,通知微信后台系統不再發送該單的支付通知。
(14)未收到支付通知的情況,商戶后台系統調用【查詢訂單API】。
(15)商戶確認訂單已支付后給用戶發貨。
一、設置回調地址
商戶后台系統根據微信支付規則鏈接生成二維碼,鏈接中帶固定參數productid(可定義為產品標識或訂單號)。用戶掃碼后,微信支付系統將productid和用戶唯一標識(openid)回調商戶后台系統(需要設置支付回調URL),商戶后台系統根據productid生成支付交易,最后微信支付系統發起用戶支付流程
商戶支付回調URL設置指引:進入公眾平台-->微信支付-->開發配置-->掃碼支付-->修改,如下圖所示。
這個支付回調的URL設置的作用是接收用戶掃碼后微信支付系統發送的數據,根據接收的數據生成支付訂單,調用【統一下單API】提交支付交易。
二、生成微信支付二維碼
二維碼長鏈接示例:weixin:/

/** * 掃碼支付模式一生成二維碼 * * @param request * @param response * @throws IOException */ @RequestMapping("qrcode") public String createPayImage(HttpServletRequest request, HttpServletResponse response) { String nonce_str = PayUtil.createNonceStr(); String product_id = "product_001"; // 推薦根據商品ID生成 SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>(); packageParams.put("appid", WechatConfig.APP_ID); packageParams.put("mch_id", WechatConfig.MCH_ID); packageParams.put("product_id", product_id); packageParams.put("time_stamp", PayUtil.createTimeStamp()); packageParams.put("nonce_str", nonce_str); String str = PayUtil.createPayImageUrl(packageParams); String sign = SignatureUtil.createSign(packageParams,WechatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING); packageParams.put("sign", sign); String payurl = "weixin://wxpay/bizpayurl?sign=" + sign + str; logger.info("payurl is " + payurl); /**** 轉成短鏈接 ****/ PayShortUrlParams payShortUrlParams = new PayShortUrlParams(); payShortUrlParams.setAppid(WechatConfig.APP_ID); payShortUrlParams.setMch_id(WechatConfig.MCH_ID); payShortUrlParams.setLong_url(payurl); payShortUrlParams.setNonce_str(nonce_str); String urlSign = SignatureUtil.createSign(payShortUrlParams,WechatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING); payShortUrlParams.setSign(urlSign); String longXml = XmlUtil.toSplitXml(payShortUrlParams); String shortResult = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.POST_METHOD, WechatConfig.PAY_SHORT_URL, null, longXml); PayShortUrlResult payShortUrlResult = XmlUtil.getObjectFromXML(shortResult, PayShortUrlResult.class); if("SUCCESS".equals(payShortUrlResult.getReturn_code())){ payurl = payShortUrlResult.getShort_url(); }else{ logger.debug("錯誤信息"+payShortUrlResult.getReturn_msg()); } /**** 生成 二維碼圖片自己實現****/ return null; }
/** * 生成支付二維碼URL * * @param params * @return */ public static String createPayImageUrl(SortedMap<Object, Object> params) { StringBuffer buffer = new StringBuffer(); for (Map.Entry<Object, Object> entry : params.entrySet()) { if (entry.getValue() != null) { buffer.append("&" + entry.getKey() + "=" + entry.getValue()); } } return buffer.toString(); }
三、回調商戶支付URL
接收用戶掃碼后微信支付系統發送的數據,根據接收的數據生成支付訂單,調用統一下單API提交支付交易
統一接口文檔https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
package com.phil.wechatpay.controller; import java.io.BufferedOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.phil.common.config.SystemConfig; import com.phil.common.config.WeChatConfig; import com.phil.common.util.HttpReqUtil; import com.phil.common.util.PayUtil; import com.phil.common.util.SignatureUtil; import com.phil.common.util.XmlUtil; import com.phil.wechatpay.model.rep.PayCallBackParams; import com.phil.wechatpay.model.rep.UnifiedOrderParams; import com.phil.wechatpay.model.resp.PayCallBackResult; import com.phil.wechatpay.model.resp.UnifiedOrderResult; /** * 掃碼模式一回調 * * @author phil * @date 2017年6月27日 * */ @Controller @RequestMapping("/wxpay/") public class WechatPayCallBackController { private static final Logger logger = Logger.getLogger(WechatPayCallBackController.class); @RequestMapping("/callback")//payone public void callBack(HttpServletRequest request, HttpServletResponse response) throws Exception { String resXml = "";// 反饋給微信服務器 // 微信支付系統發送的數據(<![CDATA[product_001]]>格式) String xml = HttpReqUtil.inputStreamToStrFromByte(request.getInputStream()); // logger.info("微信支付系統發送的數據"+xml); /**** 微信支付系統發送的數據其實就是回調地址輸入的參數Xml****/ // 驗證簽名 if (SignatureUtil.checkIsSignValidFromWeiXin(xml, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING)) { // 轉換成輸入參數, PayCallBackParams payCallBackParams = XmlUtil.getObjectFromXML(xml, PayCallBackParams.class); // appid openid mch_id is_subscribe nonce_str product_id sign // 統一下單 String openid = payCallBackParams.getOpenid(); String product_id = payCallBackParams.getProduct_id(); /**** product_id 等 生成自己系統的訂單****/ int total_fee = 1; // 根據product_id算出價格 String out_trade_no = PayUtil.createOutTradeNo(); //生成訂單號 String body = product_id; // 商品名稱設置為product_id String attach = "XXX店"; // 附加數據 String nonce_str = PayUtil.createNonceStr(); String spbill_create_ip = HttpReqUtil.getRemortIP(request); // 組裝統一下單的請求參數 UnifiedOrderParams unifiedOrderParams = new UnifiedOrderParams(); unifiedOrderParams.setAppid(WeChatConfig.APP_ID);// 必須 unifiedOrderParams.setMch_id(WeChatConfig.MCH_ID);// 必須 unifiedOrderParams.setOut_trade_no(out_trade_no); unifiedOrderParams.setBody(body); unifiedOrderParams.setAttach(attach); unifiedOrderParams.setTotal_fee(total_fee);// 必須 unifiedOrderParams.setNonce_str(nonce_str); // 必須 unifiedOrderParams.setSpbill_create_ip(spbill_create_ip); // 必須 unifiedOrderParams.setTrade_type("NATIVE");// 必須 unifiedOrderParams.setOpenid(openid); unifiedOrderParams.setNotify_url(WeChatConfig.NOTIFY_URL); // 異步通知URL // 簽名 String sign = SignatureUtil.createSign(unifiedOrderParams, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING); unifiedOrderParams.setSign(sign); // 統一下單 請求的Xml String unifiedXmL = XmlUtil.toSplitXml(unifiedOrderParams); // 統一下單 返回的xml String unifiedOrderResultXmL = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.POST_METHOD,WeChatConfig.UNIFIED_ORDER_URL, null, unifiedXmL); // 統一下單返回 驗證簽名 if (SignatureUtil.checkIsSignValidFromWeiXin(unifiedOrderResultXmL, WeChatConfig.API_KEY,SystemConfig.CHARACTER_ENCODING)) { UnifiedOrderResult unifiedOrderResult = (UnifiedOrderResult) XmlUtil.getObjectFromXML(unifiedOrderResultXmL, UnifiedOrderResult.class); if("SUCCESS".equals(unifiedOrderResult.getReturn_code()) && "SUCCESS".equals(unifiedOrderResult.getResult_code())){ PayCallBackResult payCallBackResult = new PayCallBackResult(); payCallBackResult.setReturn_code("SUCCESS"); payCallBackResult.setAppid(WeChatConfig.APP_ID); payCallBackResult.setMch_id(WeChatConfig.MCH_ID); payCallBackResult.setNonce_str(unifiedOrderResult.getNonce_str());//直接用微信返回的簽名即可 /**** prepay_id 2小時內都有效,根據product_id再次支付方法自己寫 ****/ payCallBackResult.setPrepay_id(unifiedOrderResult.getPrepay_id()); payCallBackResult.setResult_code("SUCCESS"); String callsign = SignatureUtil.createSign(payCallBackResult, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING); payCallBackResult.setSign(callsign); resXml = XmlUtil.toXml(payCallBackResult).replace("__", "_"); //將數據包返回給微信支付系統處理 } } else { logger.info("簽名驗證錯誤"); resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[簽名驗證錯誤]]></return_msg>" + "</xml> "; } } else { logger.info("簽名驗證錯誤"); resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[簽名驗證錯誤]]></return_msg>" + "</xml> "; } BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); IOUtils.closeQuietly(out); } }
第四步、完成支付並通知支付結果