Java web整合支付寶電腦支付介面(沙箱環境)
前言
這是博主的第一篇部落格,剛剛畢業,打算把工作中遇到的一些比較普遍的問題記錄下來,如果有寫的不好的地方或者寫錯的地方歡迎指出,一定會改!嘿嘿嘿。先來立個flag,我希望三年之後能夠成為一名不怕bug,熱愛生活,健康快樂的富婆!
這篇部落格主要介紹如何在Java web專案中整合支付寶的電腦支付介面(會稍微介紹一下伺服器整合APP支付介面)。目前支付寶介面更新很快,在博主查詢資料的時候,很多都是即時到賬介面,APP支付則是移動支付介面等,所以想結合現在的電腦支付介面寫一篇博文。如果專案要正式接入支付寶介面的話,是要企業與支付寶建立應用以及簽約獲取APPID等等的,如果我們只是個人想要學習或者測試的話,可以使用沙箱環境,不需要建立應用和簽約,預設有很多應用!!正式環境的開發順序是:建立應用—簽約(電腦支付、APP支付、當面付、手機網站支付等等都需要單獨簽約!!)
配置沙箱環境
一、如果你是一個新手小白想要在專案中接入支付寶介面,首先你要做的是,進入支付寶官網開放平臺。點選“快速開發”下的“開發接入”,跳轉到新頁面後點擊“開發服務”下的“沙箱”。
二、新頁面就是支付寶配置沙箱環境的文件以及使用說明,這裡告訴我們沙箱環境預設給了我們一個APPID,需要我們自己配置RSA2(SHA256)的應用公鑰,需要先下載支付寶生成祕鑰的工具,如果是RSA2簽名格式的話,記住要生成2048位的!!不要被示例圖給迷惑了,當初博主就是這裡弄錯了!生成之後,進入沙箱應用,上傳剛剛生成的應用公鑰,上傳成功後會生成支付寶公鑰,應用私鑰以及支付寶公鑰會在程式碼配置中用到。如需要更改祕鑰,使用下載的工具重新生成再上傳即可!
匯入支付寶電腦支付的demo
一、在沙箱應用的連結中,點選最下面的功能中的電腦網站支付,選擇左側SDK&DEMO的選單項,下載Java版的demo。
二、解壓剛剛下載的demo,匯入到eclipse中,目錄結構如下圖
三、修改AlipayConfig.java檔案,記住是商戶私鑰和支付寶公鑰!不要寫成應用公鑰!應用私鑰即商戶私鑰!
這裡的notify_url和return_url改成自己專案要返回頁面的地址,由於是沙箱環境,所以支付寶閘道器也有修改。return_url是指付款成功之後返回給使用者檢視的介面,如付款成功之後返回到商品詳情或者網站首頁等等。notify_url則是支付包與伺服器互動的頁面,使用者看不到,支付成功以notify_url返回的引數或者查詢訂單返回的引數為準。
四、修改配置成功之後,執行專案。
頁面如下:
1.
2.
3.**使用沙箱應用下面的沙箱賬號裡的買家賬號登入,付款!**錢會直接打到賣家賬戶中!這裡面的錢可以自己手動新增,很有滿足感 !!!!瞬間變富婆啊!!如果想用二維碼支付,則到沙箱應用中掃描二維碼下載沙箱版的支付寶,再用沙箱的買家賬號登入就可以付款了!目前支援安卓版。
4.由於博主並沒有寫頁面,使用的是支付寶預設的,所以返回的是一串json資料。
5.沙箱除錯常見的錯誤如下圖,如果報的錯誤不在這裡面,請自行百度!
到這裡如果能夠成功付款或者查詢訂單等等,就可以看出我們的配置是沒有問題的!接下來就要與我們的Java web專案整合並且存訂單資料到資料庫中!
整合介面到Java web專案
一、博主的框架是公司同事搭的SSM框架,但是與我在網上看的也有點不太一樣,不過沒關係,整合起來就那麼幾個檔案。首先把上面那個demo裡的Alipayconfig.java放在專案中(目錄隨專案而定),然後匯入alipay-sdk-javaXXX.jar、commons-logging-1.1.1.jar這兩個jar包到WebContent\WEB-INF\lib中,(具體路徑隨專案不同而改變)。
二、寫支付介面。這裡面可以對自己專案中的訂單表進行操作,先增加一條訂單,但是狀態為未付款。然後呼叫支付寶付款介面。
/**
* 獲取訂單資料介面
* @param request
* @param response
* @throws AlipayApiException
* @throws IOException
*/
@RequestMapping("viewOrder")
public void viewOrder(HttpServletRequest req, Model mod, HttpServletResponse rep,
@RequestParam(value = "goodId", required = true)Integer goodId) throws AlipayApiException, IOException{
CommonResponse cr = new CommonResponse();
User cu = ViewSessionManager.getUserSession();
if(cu == null){ //需要登入才能買東西
cr.setMessage("未登入");
cr.setData(null);
cr.setCode(3109);
}
//系統下單
OrderInfo param = new OrderInfo();
param.setGoodId(goodId);
payService.alipayOrder(cu, param); //生成訂單資訊,根據自己專案改動
//獲得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
//設定請求引數
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(AlipayConfig.return_url);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
//商戶訂單號,商戶網站訂單系統中唯一訂單號,必填
String out_trade_no = param.getTradeCode();
//付款金額,必填
String total_amount = param.getMoney().toString();
//訂單名稱,必填
String subject = param.getSubject();
//商品描述,可空
String body = param.getRemark();
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//請求
String result = alipayClient.pageExecute(alipayRequest).getBody();
rep.setContentType("text/html;charset=" + AlipayConfig.charset);
rep.getWriter().write(result);//直接將完整的表單html輸出到頁面
rep.getWriter().flush();
rep.getWriter().close();
}
三、寫同步通知return_url介面。這裡面可以更改訂單狀態,但是不以這個為準(若網速太卡或者使用者付完款關掉頁面,則不會跳到這個介面,所以訂單狀態可能就不會更改)
/**
* 回撥路徑return_url
* @param request
* @param response
* @throws AlipayApiException
* @throws UnsupportedEncodingException
*/
@RequestMapping("return_url.view")
public String returnUrl(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException, UnsupportedEncodingException{
//獲取支付寶POST過來反饋資訊
Map<String,String> params = new HashMap<String,String>();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//亂碼解決,這段程式碼在出現亂碼時使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//切記alipaypublickey是支付寶的公鑰,請去open.alipay.com對應應用下檢視。
//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset,AlipayConfig.sign_type);
if(signVerified) {
//商戶訂單號
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
//支付寶交易號
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
//付款金額
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
request.setAttribute("out_trade_no", out_trade_no);
request.setAttribute("trade_no", trade_no);
request.setAttribute("total_amount", total_amount);
log.info("訂單處理:系統訂單號" + out_trade_no + "支付寶交易號:" + trade_no);
//系統處理根據支付寶回撥更改訂單狀態或者其他關聯表的資料
OrderInfo order = payService.findOneByTradeCode(out_trade_no);
if(order == null){
signVerified = false;
request.setAttribute("signVerified", signVerified);
request.setAttribute("reason", "商戶訂單號不存在");
log.error("系統訂單:"+ out_trade_no + "不存在。");
}else{
if(!order.getMoney().toString().equals(total_amount)){
signVerified = false;
request.setAttribute("signVerified", signVerified);
request.setAttribute("reason", "付款金額不對");
return "notify_url";
}
if(order.getTradeStatus() == 1){//判斷當前訂單是否已處理,避免重複處理
log.info("系統訂單:"+ out_trade_no + "無需重複處理。");
}else{
order.setTradeStatus(1);//修改訂單狀態為已支付
Date payedAt = new Date();
order.setTransactionId(trade_no);
order.setPayedAt(payedAt);
payService.payOrder(order);
log.info("系統訂單:"+ out_trade_no + "成功支付。");
}
}
}else{
request.setAttribute("reason", "驗籤失敗");
}
request.setAttribute("signVerified", signVerified);
return "return_url";
}
四、非同步通知notify_url介面,與上面return_url的幾乎相同,只是最後輸出:
response.setContentType("text/html;charset=" + AlipayConfig.charset);
response.getWriter().write("success");//直接將完整的表單html輸出到頁面
response.getWriter().flush();
response.getWriter().close();
方法為void,還有一點需要注意的是:必須保證伺服器非同步通知頁面(notify_url)上無任何字元,如空格、HTML標籤、開發系統自帶丟擲的異常提示資訊等
電腦網站支付結果非同步通知
資料庫中訂單狀態的更新以這個介面中的為準!
五、查詢訂單介面。這裡的返回型別CommonResponse 是我們公司自己的,你們改成自己的返回型別就好.我們這個是ajax呼叫介面,然後將orderString的資料返回,實際上是json資料。
/**
* 支付寶交易查詢介面
* @param request
* @param response
* @throws UnsupportedEncodingException
* @throws AlipayApiException
*/
@RequestMapping("queryOrder")
@ResponseBody
public CommonResponse queryOrder(HttpServletRequest req, Model mod, HttpServletResponse rep,
@RequestParam(value = "tradeCode", required = true)String tradeCode,
@RequestParam(value = "tradeNo", required = true)String tradeNo) throws UnsupportedEncodingException, AlipayApiException{
CommonResponse cr = new CommonResponse();
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type); //獲得初始化的AlipayClient
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();//建立API對應的request類
request.setBizContent("{" +
" \"out_trade_no\":\""+tradeCode+"\"," +
" \"trade_no\":\""+tradeNo+"\"" +
" }");//設定業務引數
//根據response中的結果繼續業務邏輯處理
String orderString = null;
try {
//呼叫查詢方法
AlipayTradeQueryResponse response = alipayClient.execute(request);//通過alipayClient呼叫API,獲得對應的response類
orderString = response.getBody();//就是orderString 可以直接給客戶端請求,無需再做處理。
} catch (AlipayApiException e) {
e.printStackTrace();
}
cr.setData(orderString); //返回orderString
return cr;
}
伺服器端新增APP支付介面
與電腦支付介面類似,可以直接使用同一個jar包,不過在付款介面的入參裡需要新增token。下面是部分程式碼。我相信你們足夠聰明能夠改成自己的!
CommonResponse cr = new CommonResponse();
Cache<String, User> apiAccessTokenCache = cacheManager.getCache("apiAccessTokenCache");
User cu = apiAccessTokenCache.get(token);//登入時候產生的token,通過token獲得User
if(cu == null){
cr.setMessage("token值不對或已失效");
cr.setData(null);
cr.setCode(3109);
return cr;
}
//系統下單
OrderInfo param = new OrderInfo();
param.setGoodId(goodId);
payService.order(cu, param); //生成訂單資訊
//例項化客戶端
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
//例項化具體API對應的request類,類名稱和介面名稱對應,當前呼叫介面名稱:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已經封裝掉了公共引數,這裡只需要傳入業務引數。以下方法為sdk的model入參方式(model和biz_content同時存在的情況下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
String time = Long.toString(System.currentTimeMillis()/1000);
model.setBody(param.getRemark());
model.setSubject(param.getSubject());
model.setOutTradeNo(param.getTradeCode());
model.setTimeoutExpress(time);
model.setTotalAmount(param.getMoney()+"");
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
request.setReturnUrl(AlipayConfig.return_url);
request.setNotifyUrl(AlipayConfig.notify_url);
String orderString = null;
try {
//這裡和普通的介面呼叫不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
orderString = response.getBody();//就是orderString 可以直接給客戶端請求,無需再做處理。
} catch (AlipayApiException e) {
e.printStackTrace();
}
cr.setData(orderString); //返回orderString
return cr;
轉換成正式環境
需要修改配置檔案中的APPID,支付寶公鑰,應用私鑰,以及支付寶閘道器!有時改了閘道器之後跳轉的還是沙箱環境的閘道器,需要clean一下伺服器(我的是Tomcat)
結語
上面就是博主總結的Java web專案整合支付寶電腦支付介面!希望這一篇博文能夠解決你的問題,祝好運!如果有疑問可以給我留言,看到了如果我會的問題就會回答!☺☺