支付項目采用springMvc+Dubbo架構實作,隻對外提供接口。
話說,為什麼微信支付比支付寶來的晚了那麼一點,一句話,那一陣挺忙的,然後就沒有時間整理,最近做完支付寶支付,順便也把微信支付的也整理一下。
這裡再吐槽一下,微信支付的DEMO基本為零,很多代碼都是從網上查找的(也可能我麼有仔細找API)。
掃碼支付,目前來說個人是不可以申請的,包括現在支付寶的即時到帳個人相關業務也取消了。是以這裡必須有一個微信支付商戶平台,具體怎麼申請的,我也不清楚,隻是拿來用的。
商戶平台是要配合綁定微信公衆賬号使用的,具體操作申請下來已經綁定了,這裡你也隻管用就是了。
場景介紹
使用者掃描商戶展示在各種場景的二維碼進行支付。
步驟1:商戶根據微信支付的規則,為不同商品生成不同的二維碼(如圖6.1),展示在各種場景,用于使用者掃描購買。
步驟2:使用者使用微信“掃一掃”(如圖6.2)掃描二維碼後,擷取商品支付資訊,引導使用者完成支付(如圖6.3)。
支付二維碼
圖6.1 支付二維碼
打開微信掃一掃二維碼
圖6.2 打開微信掃一掃二維碼
确認支付頁面
圖6.3 确認支付頁面
步驟(3):使用者确認支付,輸入支付密碼(如圖6.4)。
步驟(4):支付完成後會提示使用者支付成功(如圖6.5),商戶背景得到支付成功的通知,然後進行發貨處理。
使用者确認支付,輸入密碼
圖6.4 使用者确認支付,輸入密碼
支付成功提示
圖6.5 支付成功提示
ConfigUtil參數配置:
<code>import java.util.Map;</code>
<code>import java.util.ResourceBundle;</code>
<code>import java.util.SortedMap;</code>
<code>import java.util.TreeMap;</code>
<code></code>
<code>/**</code>
<code>* 相關配置參數</code>
<code>* 建立者 張志朋</code>
<code>* 建立時間 2016年9月28日</code>
<code>*</code>
<code>*/</code>
<code>public class ConfigUtil {</code>
<code>* 服務号相關資訊</code>
<code>public final static String APP_ID = "2016";//服務号的應用ID</code>
<code>public final static String APP_SECRET = "2016";//服務号的應用密鑰</code>
<code>public final static String TOKEN = "weixinCourse";//服務号的配置token</code>
<code>public final static String MCH_ID = "2016";//商戶号</code>
<code>public final static String API_KEY = "2016";//API密鑰</code>
<code>public final static String SIGN_TYPE = "MD5";//簽名加密方式</code>
<code>public final static String CERT_PATH = "apiclient_cert.p12";//微信支付證書存放路徑位址</code>
<code>static ResourceBundle resource = ResourceBundle.getBundle("config");</code>
<code>//微信支付統一接口的回調action</code>
<code>public final static String NOTIFY_URL = resource.getString("WEIXIN_NOTIFY_URL");</code>
<code>* 微信基礎接口位址</code>
<code>//擷取token接口(GET)</code>
<code>public final static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";</code>
<code>//oauth2授權接口(GET)</code>
<code>public final static String OAUTH2_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";</code>
<code>//重新整理access_token接口(GET)</code>
<code>public final static String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";</code>
<code>// 菜單建立接口(POST)</code>
<code>public final static String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";</code>
<code>// 菜單查詢(GET)</code>
<code>public final static String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";</code>
<code>// 菜單删除(GET)</code>
<code>public final static String MENU_DELETE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";</code>
<code>* 微信支付接口位址</code>
<code>//微信支付統一接口(POST)</code>
<code>public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";</code>
<code>//微信退款接口(POST)</code>
<code>public final static String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";</code>
<code>//訂單查詢接口(POST)</code>
<code>public final static String CHECK_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery";</code>
<code>//關閉訂單接口(POST)</code>
<code>public final static String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder";</code>
<code>//退款查詢接口(POST)</code>
<code>public final static String CHECK_REFUND_URL = "https://api.mch.weixin.qq.com/pay/refundquery";</code>
<code>//對賬單接口(POST)</code>
<code>public final static String DOWNLOAD_BILL_URL = "https://api.mch.weixin.qq.com/pay/downloadbill";</code>
<code>//短連結轉換接口(POST)</code>
<code>public final static String SHORT_URL = "https://api.mch.weixin.qq.com/tools/shorturl";</code>
<code>//接口調用上報接口(POST)</code>
<code>public final static String REPORT_URL = "https://api.mch.weixin.qq.com/payitil/report";</code>
<code>public static void commonParams(SortedMap<Object, Object> packageParams){</code>
<code>// 賬号資訊</code>
<code>String appid = ConfigUtil.APP_ID; // appid</code>
<code>String mch_id = ConfigUtil.MCH_ID; // 商業号</code>
<code>// 生成随機字元串</code>
<code>String currTime = PayCommonUtil.getCurrTime();</code>
<code>String strTime = currTime.substring(8, currTime.length());</code>
<code>String strRandom = PayCommonUtil.buildRandom(4) + "";</code>
<code>String nonce_str = strTime + strRandom;</code>
<code>packageParams.put("appid", appid);// 公衆賬号ID</code>
<code>packageParams.put("mch_id", mch_id);// 商戶号</code>
<code>packageParams.put("nonce_str", nonce_str);// 随機字元串</code>
<code>}</code>
<code>* 該接口主要用于掃碼原生支付模式一中的二維碼連結轉成短連結(weixin://wxpay/s/XXXXXX),減小二維碼資料量,提升掃描速度和精确度。</code>
<code>* @Author 張志朋</code>
<code>* @param urlCode void</code>
<code>* @Date 2016年10月26日</code>
<code>* 更新日志</code>
<code>* 2016年10月26日 張志朋 首次建立</code>
<code>@SuppressWarnings("rawtypes")</code>
<code>public static void shorturl(String urlCode){</code>
<code>try {</code>
<code>String key = ConfigUtil.API_KEY; // key</code>
<code>SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();</code>
<code>ConfigUtil.commonParams(packageParams);</code>
<code>packageParams.put("long_url", urlCode);// URL連結</code>
<code>String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);</code>
<code>packageParams.put("sign", sign);// 簽名</code>
<code>String requestXML = PayCommonUtil.getRequestXml(packageParams);</code>
<code>String resXml = HttpUtil.postData(ConfigUtil.SHORT_URL, requestXML);</code>
<code>Map map = XMLUtil.doXMLParse(resXml);</code>
<code>String returnCode = (String) map.get("return_code");</code>
<code>if("SUCCESS".equals(returnCode)){</code>
<code>String resultCode = (String) map.get("return_code");</code>
<code>if("SUCCESS".equals(resultCode)){</code>
<code>urlCode = (String) map.get("short_url");</code>
<code>} catch (Exception e) {</code>
<code>e.printStackTrace();</code>
參數必填項 APP_ID 和APP_SECRET 是從微信公衆号裡面擷取的,而MCH_ID和API_KEY是從商戶平台擷取的。CERT_PATH 證書可選,但是如果做退款接口必須要使用證書。NOTIFY_URL 為回調位址,自定義路徑,但是一定要微信平台可以調用到你的url。
API文檔位址
文檔有詳細的參數說明,具體生成需要xml解析,這裡就不放了,好多的說,有需要的可以留言。
支付回調:
<code>* 二維碼支付</code>
<code>* 建立時間 2016年10月31日</code>
<code>@Controller</code>
<code>@RequestMapping(value = "weixin")</code>
<code>public class WeixinPayController {</code>
<code>@Autowired</code>
<code>private IWeixinPayService weixinpayBack;</code>
<code>* 微信支付回調</code>
<code>* @param request</code>
<code>* @param response</code>
<code>* @throws Exception void</code>
<code>* @Date 2016年9月28日</code>
<code>* 2016年9月28日 張志朋 首次建立</code>
<code>@SuppressWarnings({ "unchecked", "rawtypes" })</code>
<code>@RequestMapping(value = "pay")</code>
<code>public void weixin_notify(HttpServletRequest request, HttpServletResponse response) throws Exception {</code>
<code>LogUtil.info("支付成功回調");</code>
<code>// 讀取參數</code>
<code>InputStream inputStream = request.getInputStream();</code>
<code>StringBuffer sb = new StringBuffer();</code>
<code>String s;</code>
<code>BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));</code>
<code>while ((s = in.readLine()) != null) {</code>
<code>sb.append(s);</code>
<code>in.close();</code>
<code>inputStream.close();</code>
<code>// 解析xml成map</code>
<code>Map<String, String> m = new HashMap<String, String>();</code>
<code>m = XMLUtil.doXMLParse(sb.toString());</code>
<code>// 過濾空 設定 TreeMap</code>
<code>Iterator it = m.keySet().iterator();</code>
<code>while (it.hasNext()) {</code>
<code>String parameter = (String) it.next();</code>
<code>String parameterValue = m.get(parameter);</code>
<code>String v = "";</code>
<code>if (null != parameterValue) {</code>
<code>v = parameterValue.trim();</code>
<code>packageParams.put(parameter, v);</code>
<code>// 判斷簽名是否正确</code>
<code>if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, key)) {</code>
<code>// ------------------------------</code>
<code>// 處理業務開始</code>
<code>String resXml = "";</code>
<code>if ("SUCCESS".equals((String) packageParams.get("result_code"))) {</code>
<code>// 這裡是支付成功</code>
<code>String orderNo = (String) packageParams.get("out_trade_no");</code>
<code>String attach = (String) packageParams.get("attach");</code>
<code>//回調K12</code>
<code>LogUtil.info(attach+"(訂單号:"+orderNo+"付款成功)");</code>
<code>// 通知微信.異步确認成功.必寫.不然會一直通知背景.八次之後就認為交易失敗了.</code>
<code>resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";</code>
<code>weixinpayBack.updateAccOrder(orderNo);</code>
<code>} else {</code>
<code>LogUtil.info("支付失敗,錯誤資訊:" + packageParams.get("err_code"));</code>
<code>resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[封包為空]]></return_msg>"+ "</xml> ";</code>
<code>// 處理業務完畢</code>
<code>BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());</code>
<code>out.write(resXml.getBytes());</code>
<code>out.flush();</code>
<code>out.close();</code>
<code>LogUtil.info("通知簽名驗證失敗");</code>
大體就這個樣子,後續的可能就是安全優化了。涉及到錢可不是小問題。