JAVA實現的支付寶掃描二維碼支付

支付項目採用springMvc+Dubbo架構實現,只對外提供接口


項目地址:

前期醞釀準備

最近項目中要上線支付功能、前段時間剛開發完微信的掃碼支付、不得不說微信開發團隊的文檔真是一個爛。但總算是對照着API把功能交付上線了。

前幾天公司申請下來了企業支付寶,得空所以也把支付寶的掃碼支付給集成進去。這裏又不得不說,是支付寶的文檔寫的不咋地還是自己沒有仔細閱讀,總之翻遍了API最終在沙箱裏面運行成功(切記、認真讀文檔,不然各種BUG等着你)

什麼是掃碼支付?

掃碼支付,指用戶打開支付寶錢包中的“掃一掃”功能,掃描商家展示在某收銀場景下的二維碼並進行支付的模式。該模式適用於線下實體店支付、面對面支付等場景。

業務流程:
LB1KXhmLXXXXXaIapXXXXXXXXXX.png
使用步驟:
LB1UHBDLXXXXXbdXFXXXXXXXXXX.png
用戶登陸支付寶錢包,點擊首頁“付款-掃碼付”,進入掃一掃界面;
收銀員在商家收銀系統操作生成支付寶訂單,用戶確認支付金額,並生成二維碼;
用戶使用錢包的“掃碼付”,掃收銀員提供的二維碼,確認支付;
用戶付款後商家收銀系統會拿到支付成功或者失敗的結果。

具體產品介紹

如何快速接入?

前面的大家可以大體瞭解一下

開放平臺服務端SDK下載地址(這裏選擇JAVA版本)、點擊下載、裏面有詳細的API測試方法。

如何集成到項目中去?

下載DEMO解壓、仔細閱讀裏面的readme.txt文件、裏面有詳細的項目結構。

參數配置zfbinfo.properties(沙箱環境網關參數不同)

  1. # 支付寶網關名、partnerId和appId
  2. #open_api_domain = https://openapi.alipay.com/gateway.do
  3. #支付寶沙箱環境
  4. open_api_domain = https://openapi.alipaydev.com/gateway.do
  5. mcloud_api_domain = http://mcloudmonitor.com/gateway.do
  6. pid = 此處請填寫你的PID
  7. appid = 此處請填寫你當面付的APPID
  8. # RSA私鑰、公鑰和支付寶公鑰
  9. private_key = 此處請填寫你的商戶私鑰且轉PKCS8格式
  10. public_key = 此處請填寫你的商戶公鑰
  11. alipay_public_key = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkrIvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsraprwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUrCmZYI/FCEa3/cNMW0QIDAQAB
  12. # 當面付最大查詢次數和查詢間隔(毫秒)
  13. max_query_retry = 5
  14. query_duration = 5000
  15. # 當面付最大撤銷次數和撤銷間隔(毫秒)
  16. max_cancel_retry = 3
  17. cancel_duration = 2000
  18. # 交易保障線程第一次調度延遲和調度間隔(秒)
  19. heartbeat_delay = 5
  20. 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方法進行測試了,運行結果如下:

  1. [acts_pay]|2016-11-04 15:23:35:530|Configs{支付寶openapi網關: https://openapi.alipaydev.com/gateway.do
  2. , 支付寶mcloudapi網關域名: http://mcloudmonitor.com/gateway.do
  3. , pid: 2088102169116018
  4. , appid: 2016073000123724
  5. , 商戶RSA私鑰: MIICdw******rLZis=
  6. , 商戶RSA公鑰: MIGfMA******IDAQAB
  7. , 支付寶RSA公鑰: MIGfMA******IDAQAB
  8. , 查詢重試次數: 5
  9. , 查詢間隔(毫秒): 5000
  10. , 撤銷嘗試次數: 3
  11. , 撤銷重試間隔(毫秒): 2000
  12. , 交易保障調度延遲(秒): 5
  13. , 交易保障調度間隔(秒): 900
  14. }
  15. [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"}}
  16. [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="}
  17. [acts_pay]|2016-11-04 15:23:37:878|支付寶預下單成功: )
  18. [acts_pay]|2016-11-04 15:23:37:878|code:10000, msg:Success
  19. [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="}
  20. [acts_pay]|2016-11-04 15:23:37:878|filePath:D:\qr.png

最後下載沙箱錢包就可以完成手機支付了。

下載地址

如何實現異步通知?

相關參數說明

用戶會用手機掃碼給支付寶付款,然後支付寶收到之後會發送一條支付成功的消息給我們設置的notify_url

  1. import java.io.BufferedOutputStream;
  2. import java.util.Enumeration;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Controller;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RequestMethod;
  11. import com.acts.web.acc.service.IWeixinPayService;
  12. import com.acts.web.common.utils.LogUtil;
  13. import com.alipay.api.AlipayApiException;
  14. import com.alipay.api.internal.util.AlipaySignature;
  15. import com.alipay.demo.trade.config.Configs;
  16. @Controller
  17. @RequestMapping(value = "alipay")
  18. public class AliPayController {
  19. //初始化參數 不然signVerified會驗證失敗
  20. static {
  21. Configs.init("zfbinfo.properties");
  22. }
  23. /**
  24. * 支付寶支付回調
  25. * @Author 小柒
  26. * @param request
  27. * @param response
  28. * @throws Exception
  29. * void
  30. * @Date 2016年10月31日 更新日誌 2016年10月31日 小柒 首次創建
  31. *
  32. */
  33. @SuppressWarnings("unchecked")
  34. @RequestMapping(value = "pay",method = RequestMethod.POST)
  35. public void alipay_notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
  36. LogUtil.info("支付寶付款異步通知!");
  37. String message = "success";
  38. Map<String, String> params = new HashMap<String, String>();
  39. // 取出所有參數是爲了驗證簽名
  40. Enumeration<String> parameterNames = request.getParameterNames();
  41. while (parameterNames.hasMoreElements()) {
  42. String parameterName = parameterNames.nextElement();
  43. params.put(parameterName, request.getParameter(parameterName));
  44. }
  45. //驗證簽名
  46. boolean signVerified = false;
  47. try {
  48. signVerified = AlipaySignature.rsaCheckV1(params, Configs.getAlipayPublicKey(), "UTF-8");
  49. } catch (AlipayApiException e) {
  50. e.printStackTrace();
  51. message = "failed";
  52. }
  53. if (signVerified) {
  54. LogUtil.info("驗證簽名成功!");
  55. // 若參數中的appid和填入的appid不相同,則爲異常通知
  56. if (!Configs.getAppid().equals(params.get("app_id"))) {
  57. LogUtil.info("與付款時的appid不同,此爲異常通知,應忽略!");
  58. message = "failed";
  59. }else{
  60. String outtradeno = params.get("out_trade_no");
  61. LogUtil.info(outtradeno + "號訂單回調通知。");
  62. //在數據庫中查找訂單號對應的訂單,並將其金額與數據庫中的金額對比,若對不上,也爲異常通知
  63. String status = params.get("trade_status");
  64. if (status.equals("WAIT_BUYER_PAY")) { // 如果狀態是正在等待用戶付款
  65. } else if (status.equals("TRADE_CLOSED")) { // 如果狀態是未付款交易超時關閉,或支付完成後全額退款
  66. } else if (status.equals("TRADE_SUCCESS") || status.equals("TRADE_FINISHED")) { // 如果狀態是已經支付成功
  67. //成功 更新狀態
  68. } else {
  69. weixinpayBack.updateAccOrder(outtradeno);
  70. }
  71. LogUtil.info(outtradeno + "訂單的狀態已經修改爲" + status);
  72. }
  73. } else { // 如果驗證簽名沒有通過
  74. message = "failed";
  75. LogUtil.info("驗證簽名失敗!");
  76. }
  77. BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
  78. out.write(message.getBytes());
  79. out.flush();
  80. out.close();
  81. }
  82. }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章