爲避免大家和我同樣被支付接口搞得焦頭爛額,寫一個從申請開始到能收到錢爲止的詳細教程,實際上各個語言均可以用來集成支付接口,我用java來舉例。html
正所謂知己知彼,百戰不殆。首先,咱們來看一看支付寶平臺給咱們的說明。java
進行如上操做後,來到以下圖所示的頁面web
沒有商家支付寶帳號的須要註冊,須要營業執照,經營信息,網址信息,聯繫人等等數據(圖裏說的很詳細)安全
服務開通後,咱們就能夠集成了,咱們點擊如何集成查看文檔bash
如圖所示,咱們能夠直接下載demo,進行快速集成,這是最方便的辦法了,我會採用這種方法,但使用即時到帳接口首先須要簽約,點擊如何簽約,支付寶就教你怎麼籤,無非就是填表,審覈。但這一步很重要,由於咱們須要生成的密鑰組成參數向支付寶發出請求(下面會詳細說)。服務器
簽約成功以後,咱們須要合做夥伴PID和MD5密鑰,在以下頁面獲取(圖我從官網文檔截得)
微信
前期全部準備都作好了,再總結一下前期須要的東西:markdown
咱們來繼續,解壓demo,選擇java utf-8版本,導入項目session
支付的流程爲異步
商家要傳遞給支付寶的參數列表在前面給的開發文檔中也能找到,支付寶提示的參數有必填和沒必要填兩種,能夠本身選擇。
在demo src的com\alipay\config包下有AlipayConfig類。大部分參數能夠在其中配置,在使用時直接用就能夠了,爲了維護方便,咱們能夠用配置文件的方法寫到文件裏,動態讀取。可是有一些參數須要注意:
訂單號須要本身隨機生成, sign簽名是動態生成的。
package com.alipay.config;
/* * *類名:AlipayConfig *功能:基礎配置類 *詳細:設置賬戶有關信息及返回路徑 *版本:3.4 *修改日期:2016-03-08 *說明: *如下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶能夠根據本身網站的須要,按照技術文檔編寫,並不是必定要使用該代碼。 *該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。 */
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓請在這裏配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 合做身份者ID,簽約帳號,以2088開頭由16位純數字組成的字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
public static String partner = "2088好幾個數字";
// 收款支付寶帳號,以2088開頭由16位純數字組成的字符串,通常狀況下收款帳號就是簽約帳號
public static String seller_id = partner;
// MD5密鑰,安全檢驗碼,由數字和字母組成的32位字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
public static String key = "好長一串數字和字母";
// 服務器異步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網能夠正常訪問
public static String notify_url = "http://www.wechat.com/AliPayTest/pay_notify_url"; // 體會到個人幽默感了嗎
// 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網能夠正常訪問
public static String return_url = "http://www.wechat.com/AliPayTest/pay_return_url";
// 簽名方式
public static String sign_type = "MD5";
// 調試用,建立TXT日誌文件夾路徑,見AlipayCore.java類中的logResult(String sWord)打印方法。
public static String log_path = "F:\\";
// 字符編碼格式 目前支持 gbk 或 utf-8
public static String input_charset = "utf-8";
// 支付類型 ,無需修改
public static String payment_type = "1";
// 調用的接口名,無需修改
public static String service = "create_direct_pay_by_user";
//↑↑↑↑↑↑↑↑↑↑請在這裏配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
//↓↓↓↓↓↓↓↓↓↓ 請在這裏配置防釣魚信息,若是沒開通防釣魚功能,爲空便可 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 防釣魚時間戳 若要使用請調用類文件submit中的query_timestamp函數
public static String anti_phishing_key = "";
// 客戶端的IP地址 非局域網的外網IP地址,如:221.0.0.1
public static String exter_invoke_ip = "";
//↑↑↑↑↑↑↑↑↑↑請在這裏配置防釣魚信息,若是沒開通防釣魚功能,爲空便可 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
寫一個配置類獲取配置
public class Property {
private static Properties p = new Properties();
static {
InputStream in = AlipayConfig.class.getResourceAsStream("alipay.properties");
try {
p.load(in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static public String getProperty(String property) {
return p.getProperty(property);
}
}
而後將參數配置寫到alipay.properties的文件,有參數要修改的時候不用從新編譯代碼,只要修改配置文件就能夠了。
前面主要是配置,按支付寶提供的文檔配置好各類參數後,配置這一步就完成了。
後面咱們來說一講下訂單。
買家下訂單以後,咱們接收請求參數,再加上配置的參數生成要發送給支付寶的訂單Map集合。
// 訂單號:
String out_trade_no = "test20170213145553";
//把請求參數打包成Map
Map<String, String> sParaTemp = new HashMap<>();
sParaTemp.put("service", AlipayConfig.service);
sParaTemp.put("partner", AlipayConfig.partner);
sParaTemp.put("seller_id", AlipayConfig.seller_id);
sParaTemp.put("_input_charset", AlipayConfig.input_charset);
sParaTemp.put("payment_type", AlipayConfig.payment_type);
sParaTemp.put("notify_url", AlipayConfig.notify_url);
sParaTemp.put("return_url", AlipayConfig.return_url);
sParaTemp.put("out_trade_no", out_trade_no);
sParaTemp.put("subject", subject);
sParaTemp.put("total_fee", total_fee);
sParaTemp.put("body", desc);
調用支付寶demo給的類的buildRequest靜態方法,將剛纔的sParaTemp傳遞過去,生成String類型的字符串,這個字符串實際上是個超連接,直接放地址欄上就直接將參數發送給支付寶了。
注:支付寶demo在幕後作的工做有不少,分爲如下幾步,若不想用demo的能夠本身在官網查閱文檔實現
組裝待簽名字符串
- 篩選
大部分支付寶接口中要剔除sign_type、sign兩個參數,個別接口只剔除sign參數。存在空值的參數必須剔除。排序
在參數集合中,根據參數(不是參數對應的值)的第一個字符的鍵值ASCII碼遞增排序,若是遇到相同字符則按照第二個字符的鍵值ASCII碼遞增排序,以此類推。拼接
在參數集合中,把每一個參數及其值組合成「參數=參數值」的格式(在無線產品手機安全支付中,每一個參數的組合格式是「參數=」參數值」」),而且把這些參數用&字符鏈接起來調用簽名函數
- 簽名函數
調用md5加密函數,對已經與MD5密鑰拼接好的新字符串作加密運算簽名結果的用途
- 獲得的簽名結果也是一串字符串,這串字符串即是sign參數的值,把這串字符串賦值於sign參數。
String sHtmlText = AlipaySubmit.buildRequest(sParaTemp, "post", "確認"); // 這裏使用支付寶demo給的類來生成請求參數,這點支付寶比微信的文檔方便太多了
直接用response.write()或者我用的struts,用action跳轉到頁面再用字符串連接跳轉就好了。
PrintWriter out = response.getWriter(); out.print(sHtmlText);
struts 支付action:
context.put("jump", sHtmlText);
return "send"; // 跳轉給支付寶
struts.xml
<result name="send">/WEB-INF/jump.jsp</result>
jump.jsp:
${requestScope.jump }
這樣就會直接跳轉了。
支付寶接到參數就會生成二維碼,客戶支付後商戶後臺就會收到支付寶發送的返回參數,有sign驗證簽名,訂單號,價格等等信息,若出錯的話支付寶也會返回錯誤碼ILLEGAL_SIGN
等,可在支付寶歷史公共錯誤碼
頁面查看。
若支付成功,支付寶有同步和異步兩種方式回調商戶網站,這個看當初怎麼配置的,配置的時候會把回調地址寫在請求參數裏發給支付寶,若在本身的電腦上調試,通常會調用同步方法,在服務器上會調用異步方法。
// 服務器異步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網能夠正常訪問
public static String notify_url = "http://www.wechat.com/pay_notify_url"; // 請再次感覺個人幽默感
// 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網能夠正常訪問
public static String return_url = "http://www.wechat.com/pay_return_url";
假如你的配置是這樣寫的,支付寶就會調用同步urlhttp://www.wechat.com/pay_notify_url
或者異步urlhttp://www.wechat.com/pay_return_url
並附帶請求參數,你在接到請求參數後,須要和你本身的信息比對,若成功的話,就算這筆訂單完成了。
異步回調
public void notifyUrl() {
Map<String, Object> requestParams = ActionContext.getContext().getParameters();
Map<String, String> params = null;
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
} catch (IOException e) {
log.warn("支付寶異步支付獲取輸出流失敗:" + e.getMessage());
return;
}
try {
params = tradeService.splitParam(requestParams);// 分割參數
} catch (UnsupportedEncodingException e) {
log.warn("alipay sign convert failed: " + e.getMessage());
return ;
}
//獲取支付寶的通知返回參數,可參考技術文檔中頁面跳轉同步通知參數列表(如下僅供參考)//
if (AlipayNotify.verify(params)){ //驗證成功
if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
Order verifiedAlipay = tradeService.verifiedAlipay(seller_id, out_trade_no, total_fee);
if (verifiedAlipay == null) {
log.warn("參數驗證失敗");
return; // 跳轉支付失敗頁
}
writer.print("success"); //請不要修改或刪除
return;
} else {
log.warn("沒有完成訂單");
return;
}
} else {
log.warn("支付寶異步支付驗證失敗");
writer.print("fail"); //請不要修改或刪除
}
}
同步回調
public String returnUrl() {
Map<String, Object> requestParams = ActionContext.getContext().getParameters();
Map<String, String> params = null;
try {
params = tradeService.splitParam(requestParams);
if (params == null) {
log.warn("簽名驗證失敗-參數列表爲空");
return TRADEERROR; // 跳轉支付失敗頁
}
} catch (UnsupportedEncodingException e) {
log.warn("alipay sign convert failed: " + e.getMessage());
}
if (AlipayNotify.verify(params)) {// 驗證成功
if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
Order verifiedAlipay = tradeService.verifiedAlipay(seller_id, out_trade_no, total_fee);
if (verifiedAlipay == null) {
log.warn("參數驗證失敗");
return TRADEERROR; // 跳轉支付失敗頁
}
putSession(S_TRADE_FINISHED, verifiedAlipay);// 將支付詳情放入session域中,到頁面顯示
} else {
log.warn("沒有完成訂單");
return TRADEERROR; // 跳轉支付失敗頁
}
} else {// 驗證失敗
log.warn("簽名驗證失敗");
return TRADEERROR; // 跳轉支付失敗頁
}
return TRADESUCCESS;
}
而後就所有完畢了。
這個文章寫的並不太滿意,這是我作過支付寶接口好久之後才寫的文章,好多東西都記不起來了,以前記得還在官網看過支付寶支付流程的序列圖,那個很清晰,如今我也沒找到。 這幾天也一直在忙,每次都是寫幾個字就不得不忙別的了,斷斷續續先後也有點連不起來,之後有時間我會再把這篇文章細化修改一下,看了有不懂的地方也歡迎留言,我看到會盡可能解答