前期醞釀準備
最近項目中要上線支付功能、前段時間剛開發完微信的掃碼支付、不得不說微信開發團隊的文檔真是一個爛。但總算是對照着API把功能交付上線了。
前幾天公司申請下來了企業支付寶,得空所以也把支付寶的掃碼支付給集成進去。這裏又不得不說,是支付寶的文檔寫的不咋地還是自己沒有仔細閱讀,總之翻遍了API最終在沙箱裏面運行成功(切記、認真讀文檔,不然各種BUG等着你)
什麼是掃碼支付?
掃碼支付,指用戶打開支付寶錢包中的“掃一掃”功能,掃描商家展示在某收銀場景下的二維碼並進行支付的模式。該模式適用於線下實體店支付、面對面支付等場景。
業務流程:
使用步驟:
用戶登陸支付寶錢包,點擊首頁“付款-掃碼付”,進入掃一掃界面; 收銀員在商家收銀系統操作生成支付寶訂單,用戶確認支付金額,並生成二維碼; 用戶使用錢包的“掃碼付”,掃收銀員提供的二維碼,確認支付; 用戶付款後商家收銀系統會拿到支付成功或者失敗的結果。
具體產品介紹
如何快速接入?
前面的大家可以大體瞭解一下
開放平臺服務端SDK下載地址(這裏選擇JAVA版本)、點擊下載、裏面有詳細的API測試方法。
如何集成到項目中去?
下載DEMO解壓、仔細閱讀裏面的readme.txt文件、裏面有詳細的項目結構。
參數配置zfbinfo.properties(沙箱環境網關參數不同)
# 支付寶網關名、partnerId和appId #open_api_domain = https://openapi.alipay.com/gateway.do #支付寶沙箱環境 open_api_domain = https://openapi.alipaydev.com/gateway.do mcloud_api_domain = http://mcloudmonitor.com/gateway.do pid = 此處請填寫你的PID appid = 此處請填寫你當面付的APPID # RSA私鑰、公鑰和支付寶公鑰 private_key = 此處請填寫你的商戶私鑰且轉PKCS8格式 public_key = 此處請填寫你的商戶公鑰 alipay_public_key = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkrIvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsraprwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUrCmZYI/FCEa3/cNMW0QIDAQAB # 當面付最大查詢次數和查詢間隔(毫秒) max_query_retry = 5 query_duration = 5000 # 當面付最大撤銷次數和撤銷間隔(毫秒) max_cancel_retry = 3 cancel_duration = 2000 # 交易保障線程第一次調度延遲和調度間隔(秒) heartbeat_delay = 5 heartbeat_duration = 900
RSA私鑰、公鑰和支付寶公鑰 獲取方法。 生成與配置密鑰
這裏我選擇的是方式一,使用支付寶提供的一鍵生成工具(內附使用說明)。
如果是JAVA程序public_key參數對應rsa_private_key_pkcs8.pem文件裏面的內容, public_key參數對用rsa_public_key.pem文件裏面的內容。然後把公鑰複製到沙箱中的RSA(SHA1)密鑰中生成支付寶公鑰、對應的是alipay_public_key參數。
如何生成二維碼訂單?
然後你就可以運行Main.java 中的額main方法進行測試了,運行結果如下:
[acts_pay]|2016-11-04 15:23:35:530|Configs{支付寶openapi網關: https://openapi.alipaydev.com/gateway.do , 支付寶mcloudapi網關域名: http://mcloudmonitor.com/gateway.do , pid: 2088102169116018 , appid: 2016073000123724 , 商戶RSA私鑰: MIICdw******rLZis= , 商戶RSA公鑰: MIGfMA******IDAQAB , 支付寶RSA公鑰: MIGfMA******IDAQAB , 查詢重試次數: 5 , 查詢間隔(毫秒): 5000 , 撤銷嘗試次數: 3 , 撤銷重試間隔(毫秒): 2000 , 交易保障調度延遲(秒): 5 , 交易保障調度間隔(秒): 900 } [acts_pay]|2016-11-04 15:23:35:719|trade.precreate bizContent:{"out_trade_no":"tradeprecreate14782442155652020005","seller_id":"","total_amount":"0.01","undiscountable_amount":"0","subject":"xxx品牌xxx門店當面付掃碼消費","body":"購買商品3件共20.00元","goods_detail":[{"goods_id":"goods_id001","goods_name":"xxx小麪包","quantity":1,"price":"10"},{"goods_id":"goods_id002","goods_name":"xxx牙刷","quantity":2,"price":"5"}],"operator_id":"test_operator_id","store_id":"test_store_id","extend_params":{"sys_service_provider_id":"2088100200300400500"}} [acts_pay]|2016-11-04 15:23:37:875|{"alipay_trade_precreate_response":{"code":"10000","msg":"Success","out_trade_no":"tradeprecreate14782442155652020005","qr_code":"https:\/\/qr.alipay.com\/bax03938xgzra2b5pijd00d2"},"sign":"LA2d5txq43c3t12sCsNEEGvu3plXUrqrd/uyzOy4HIMM5eRkWXaFkL+wqVNcYX/Jfn6no72yqiAUvYAivaWZkXZA3UxTRYlW+0EwZ96HrpnjFCK+QGOSDZuoiA2AyQlFgM/cQwdgTFGI+R2X9QZWxft1z3zYVG1uRGEZXed5RPQ="} [acts_pay]|2016-11-04 15:23:37:878|支付寶預下單成功: ) [acts_pay]|2016-11-04 15:23:37:878|code:10000, msg:Success [acts_pay]|2016-11-04 15:23:37:878|body:{"alipay_trade_precreate_response":{"code":"10000","msg":"Success","out_trade_no":"tradeprecreate14782442155652020005","qr_code":"https:\/\/qr.alipay.com\/bax03938xgzra2b5pijd00d2"},"sign":"LA2d5txq43c3t12sCsNEEGvu3plXUrqrd/uyzOy4HIMM5eRkWXaFkL+wqVNcYX/Jfn6no72yqiAUvYAivaWZkXZA3UxTRYlW+0EwZ96HrpnjFCK+QGOSDZuoiA2AyQlFgM/cQwdgTFGI+R2X9QZWxft1z3zYVG1uRGEZXed5RPQ="} [acts_pay]|2016-11-04 15:23:37:878|filePath:D:\qr.png
最後下載沙箱錢包就可以完成手機支付了。
下載地址
如何實現異步通知?
相關參數說明
用戶會用手機掃碼給支付寶付款,然後支付寶收到之後會發送一條支付成功的消息給我們設置的notify_url
import java.io.BufferedOutputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.acts.web.acc.service.IWeixinPayService; import com.acts.web.common.utils.LogUtil; import com.alipay.api.AlipayApiException; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.demo.trade.config.Configs; @Controller@RequestMapping(value = "alipay") public class AliPayController { //初始化參數 不然signVerified會驗證失敗 static { Configs.init("zfbinfo.properties"); } /** * 支付寶支付回調 * @param request * @param response * @throws Exception */ @SuppressWarnings("unchecked") @RequestMapping(value = "pay",method = RequestMethod.POST) public void alipay_notify(HttpServletRequest request, HttpServletResponse response) throws Exception { LogUtil.info("支付寶付款異步通知!"); String message = "success"; Map<String, String> params = new HashMap<String, String>(); // 取出所有參數是爲了驗證簽名 Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()) { String parameterName = parameterNames.nextElement(); params.put(parameterName, request.getParameter(parameterName)); } //驗證簽名 boolean signVerified = false; try { signVerified = AlipaySignature.rsaCheckV1(params, Configs.getAlipayPublicKey(), "UTF-8"); } catch (AlipayApiException e) { e.printStackTrace(); message = "failed"; } if (signVerified) { LogUtil.info("驗證簽名成功!"); // 若參數中的appid和填入的appid不相同,則爲異常通知 if (!Configs.getAppid().equals(params.get("app_id"))) { LogUtil.info("與付款時的appid不同,此爲異常通知,應忽略!"); message = "failed"; }else{ String outtradeno = params.get("out_trade_no"); LogUtil.info(outtradeno + "號訂單回調通知。"); //在數據庫中查找訂單號對應的訂單,並將其金額與數據庫中的金額對比,若對不上,也爲異常通知 String status = params.get("trade_status"); if (status.equals("WAIT_BUYER_PAY")) { // 如果狀態是正在等待用戶付款 } else if (status.equals("TRADE_CLOSED")) { // 如果狀態是未付款交易超時關閉,或支付完成後全額退款 } else if (status.equals("TRADE_SUCCESS") || status.equals("TRADE_FINISHED")) { // 如果狀態是已經支付成功 //成功 更新狀態 } else { weixinpayBack.updateAccOrder(outtradeno); } LogUtil.info(outtradeno + "訂單的狀態已經修改爲" + status); } } else { // 如果驗證簽名沒有通過 message = "failed"; LogUtil.info("驗證簽名失敗!"); } BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); out.write(message.getBytes()); out.flush(); out.close(); } }