支付項目採用springMvc+Dubbo架構實現,只對外提供接口
前期醞釀準備
最近項目中要上線支付功能、前段時間剛開發完微信的掃碼支付、不得不說微信開發團隊的文檔真是一個爛。但總算是對照着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");
}
/**
* 支付寶支付回調
* @Author 小柒
* @param request
* @param response
* @throws Exception
* void
* @Date 2016年10月31日 更新日誌 2016年10月31日 小柒 首次創建
*
*/
@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();
}
}