java支付寶支付
-
- 3 、接收支付結果通知
1.申請簽約
目的是得到開發使用的合作夥伴身份(PID)和MD5秘鑰,申請位址(即時到賬收款): https://b.alipay.com/order/productDetail.htm?productId=2015110218012942
————————————————————————————————————————————————————————
申請方式在開放平台的文檔上有詳細說明,這裡就不再贅述。
2、接入支付接口
在得到PID和秘鑰後就可以接入接口了,首先在開放平台中下載下傳官方的demo(java+MD5版本),支付寶的demo做的非常好,下載下傳下來直接配置下jdk就可以運作了。如果遇到Java compiler level does not match錯誤,說明你用的eclipse或myeclipse的jdk編譯版本與demo的JDK編譯版本不一緻,修改下jdk編譯版本就可以了。其實就用到了4個類,如下圖
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2csQzZU1EMBRkT4FleYhnRzwEMW1mY1RzRapnTtxkb5ckYplTeMZTTINGMShUYfRHelRHLwEzX39GZhh2css2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3Pn5GcuIjN1UDNzUTMxMTMwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
可以選擇把支付功能單獨做一個項目,在其他項目調用接口就可以支付,也可以整合到自己的項目裡,為了好維護我整合到自己的項目裡了。把這四個類放到自己的項目中,引入相應的jar包
2.1、demo中類的說明
AlipayConfig.java類主要是配置參數資訊的類
package com.fahai.pay.alipay;
import com.fahai.utils.ProInfoUtil;
/* *
*類名:AlipayConfig
*功能:基礎配置類
*詳細:設定帳戶有關資訊及傳回路徑
*版本:3.4
*修改日期:2016-03-08
*說明:
*以下代碼隻是為了友善商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,并非一定要使用該代碼。
*該代碼僅供學習和研究支付寶接口使用,隻是提供一個參考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓請在這裡配置您的基本資訊↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 合作身份者ID,簽約賬号,以2088開頭由16位純數字組成的字元串,檢視位址:https://b.alipay.com/order/pidAndKey.htm
public static String partner = "你自己的PID";
// 收款支付寶賬号,以2088開頭由16位純數字組成的字元串,一般情況下收款賬号就是簽約賬号
public static String seller_id = partner;
// MD5密鑰,安全檢驗碼,由數字和字母組成的32位字元串,檢視位址:https://b.alipay.com/order/pidAndKey.htm
public static String key = "你自己的MD5秘鑰";
// 伺服器異步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常通路
//異步通知頁面,就是接受支付寶支付結果傳回資訊的Controller,可以處理自己的支付後的邏輯
//測試環境
public static String notify_url = ProInfoUtil.getInstance().getProperty("project_url")+"order/pay/aliPayOrder";
// 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常通路
//同步跳轉的頁面,就是支付寶支付成功後頁面跳轉的url
public static String return_url = ProInfoUtil.getInstance().getProperty("project_url")+"order/pay/payResponse";
// 簽名方式
public static String sign_type = "MD5";
// 調試用,建立TXT日志檔案夾路徑,見AlipayCore.java類中的logResult(String sWord)列印方法。
public static String log_path = "C:\\";
// 字元編碼格式 目前支援 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 = "";
//↑↑↑↑↑↑↑↑↑↑請在這裡配置防釣魚資訊,如果沒開通防釣魚功能,為空即可 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
AlipayCore.java是整理參數的工具類
package com.alipay.util;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.PartSource;
import com.alipay.config.AlipayConfig;
/* *
*類名:AlipayFunction
*功能:支付寶接口公用函數類
*詳細:該類是請求、通知傳回兩個檔案所調用的公用函數核心處理檔案,不需要修改
*版本:3.3
*日期:2012-08-14
*說明:
*以下代碼隻是為了友善商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,并非一定要使用該代碼。
*該代碼僅供學習和研究支付寶接口使用,隻是提供一個參考。
*/
public class AlipayCore {
/**
* 除去數組中的空值和簽名參數
* @param sArray 簽名參數組
* @return 去掉空值與簽名參數後的新簽名參數組
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result = new HashMap<String, String>();
if (sArray == null || sArray.size() <= 0) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
return result;
}
/**
* 把數組所有元素排序,并按照“參數=參數值”的模式用“&”字元拼接成字元串
* @param params 需要排序并參與字元拼接的參數組
* @return 拼接後字元串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {//拼接時,不包括最後一個&字元
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
/**
* 寫日志,友善測試(看網站需求,也可以改成把記錄存入資料庫)
* @param sWord 要寫入日志裡的文本内容
*/
public static void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(AlipayConfig.log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(sWord);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 生成檔案摘要
* @param strFilePath 檔案路徑
* @param file_digest_type 摘要算法
* @return 檔案摘要結果
*/
public static String getAbstract(String strFilePath, String file_digest_type) throws IOException {
PartSource file = new FilePartSource(new File(strFilePath));
if(file_digest_type.equals("MD5")){
return DigestUtils.md5Hex(file.createInputStream());
}
else if(file_digest_type.equals("SHA")) {
return DigestUtils.sha256Hex(file.createInputStream());
}
else {
return "";
}
}
}
AlipayNotify.java是驗證簽名的類
package com.alipay.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import com.alipay.config.AlipayConfig;
import com.alipay.sign.MD5;
/* *
*類名:AlipayNotify
*功能:支付寶通知處理類
*詳細:處理支付寶各接口通知傳回
*版本:3.3
*日期:2012-08-17
*說明:
*以下代碼隻是為了友善商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,并非一定要使用該代碼。
*該代碼僅供學習和研究支付寶接口使用,隻是提供一個參考
*************************注意*************************
*調試通知傳回時,可檢視或改寫log日志的寫入TXT裡的資料,來檢查通知傳回是否正常
*/
public class AlipayNotify {
/**
* 支付寶消息驗證位址
*/
private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";
/**
* 驗證消息是否是支付寶發出的合法消息
* @param params 通知傳回來的參數數組
* @return 驗證結果
*/
public static boolean verify(Map<String, String> params) {
//判斷responsetTxt是否為true,isSign是否為true
//responsetTxt的結果不是true,與伺服器設定問題、合作身份者ID、notify_id一分鐘失效有關
//isSign不是true,與安全校驗碼、請求時的參數格式(如:帶自定義參數等)、編碼格式有關
String responseTxt = "false";
if(params.get("notify_id") != null) {
String notify_id = params.get("notify_id");
responseTxt = verifyResponse(notify_id);
}
String sign = "";
if(params.get("sign") != null) {sign = params.get("sign");}
boolean isSign = getSignVeryfy(params, sign);
//寫日志記錄(若要調試,請取消下面兩行注釋)
//String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 傳回回來的參數:" + AlipayCore.createLinkString(params);
//AlipayCore.logResult(sWord);
if (isSign && responseTxt.equals("true")) {
return true;
} else {
return false;
}
}
/**
* 根據回報回來的資訊,生成簽名結果
* @param Params 通知傳回來的參數數組
* @param sign 比對的簽名結果
* @return 生成的簽名結果
*/
private static boolean getSignVeryfy(Map<String, String> Params, String sign) {
//過濾空值、sign與sign_type參數
Map<String, String> sParaNew = AlipayCore.paraFilter(Params);
//擷取待簽名字元串
String preSignStr = AlipayCore.createLinkString(sParaNew);
//獲得簽名驗證結果
boolean isSign = false;
if(AlipayConfig.sign_type.equals("MD5") ) {
isSign = MD5.verify(preSignStr, sign, AlipayConfig.key, AlipayConfig.input_charset);
}
return isSign;
}
/**
* 擷取遠端伺服器ATN結果,驗證傳回URL
* @param notify_id 通知校驗ID
* @return 伺服器ATN結果
* 驗證結果集:
* invalid指令參數不對 出現這個錯誤,請檢測傳回進行中partner和key是否為空
* true 傳回正确資訊
* false 請檢查防火牆或者是伺服器阻止端口問題以及驗證時間是否超過一分鐘
*/
private static String verifyResponse(String notify_id) {
//擷取遠端伺服器ATN結果,驗證是否是支付寶伺服器發來的請求
String partner = AlipayConfig.partner;
String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "¬ify_id=" + notify_id;
return checkUrl(veryfy_url);
}
/**
* 擷取遠端伺服器ATN結果
* @param urlvalue 指定URL路徑位址
* @return 伺服器ATN結果
* 驗證結果集:
* invalid指令參數不對 出現這個錯誤,請檢測傳回進行中partner和key是否為空
* true 傳回正确資訊
* false 請檢查防火牆或者是伺服器阻止端口問題以及驗證時間是否超過一分鐘
*/
private static String checkUrl(String urlvalue) {
String inputLine = "";
try {
URL url = new URL(urlvalue);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection
.getInputStream()));
inputLine = in.readLine().toString();
} catch (Exception e) {
e.printStackTrace();
inputLine = "";
}
return inputLine;
}
}
AlipaySubmit.java模拟form表單請求支付寶支付接口的類
package com.alipay.util;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import com.alipay.config.AlipayConfig;
import com.alipay.sign.MD5;
/* *
*類名:AlipaySubmit
*功能:支付寶各接口請求送出類
*詳細:構造支付寶各接口表單HTML文本,擷取遠端HTTP資料
*版本:3.3
*日期:2012-08-13
*說明:
*以下代碼隻是為了友善商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,并非一定要使用該代碼。
*該代碼僅供學習和研究支付寶接口使用,隻是提供一個參考。
*/
public class AlipaySubmit {
/**
* 支付寶提供給商戶的服務接入網關URL(新)
*/
private static final String ALIPAY_GATEWAY_NEW = "https://mapi.alipay.com/gateway.do?";
/**
* 生成簽名結果
* @param sPara 要簽名的數組
* @return 簽名結果字元串
*/
public static String buildRequestMysign(Map<String, String> sPara) {
String prestr = AlipayCore.createLinkString(sPara); //把數組所有元素,按照“參數=參數值”的模式用“&”字元拼接成字元串
String mysign = "";
if(AlipayConfig.sign_type.equals("MD5") ) {
mysign = MD5.sign(prestr, AlipayConfig.key, AlipayConfig.input_charset);
}
return mysign;
}
/**
* 生成要請求給支付寶的參數數組
* @param sParaTemp 請求前的參數數組
* @return 要請求的參數數組
*/
private static Map<String, String> buildRequestPara(Map<String, String> sParaTemp) {
//除去數組中的空值和簽名參數
Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
//生成簽名結果
String mysign = buildRequestMysign(sPara);
//簽名結果與簽名方式加入請求送出參數組中
sPara.put("sign", mysign);
sPara.put("sign_type", AlipayConfig.sign_type);
return sPara;
}
/**
* 建立請求,以表單HTML形式構造(預設)
* @param sParaTemp 請求參數數組
* @param strMethod 送出方式。兩個值可選:post、get
* @param strButtonName 确認按鈕顯示文字
* @return 送出表單HTML文本
*/
public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName) {
//待請求參數數組
Map<String, String> sPara = buildRequestPara(sParaTemp);
List<String> keys = new ArrayList<String>(sPara.keySet());
StringBuffer sbHtml = new StringBuffer();
sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\"" + ALIPAY_GATEWAY_NEW
+ "_input_charset=" + AlipayConfig.input_charset + "\" method=\"" + strMethod
+ "\">");
for (int i = 0; i < keys.size(); i++) {
String name = (String) keys.get(i);
String value = (String) sPara.get(name);
sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
}
//submit按鈕控件請不要含有name屬性
sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");
sbHtml.append("<script>document.forms['alipaysubmit'].submit();</script>");
return sbHtml.toString();
}
/**
* 用于防釣魚,調用接口query_timestamp來擷取時間戳的處理函數
* 注意:遠端解析XML出錯,與伺服器是否支援SSL等配置有關
* @return 時間戳字元串
* @throws IOException
* @throws DocumentException
* @throws MalformedURLException
*/
public static String query_timestamp() throws MalformedURLException,
DocumentException, IOException {
//構造通路query_timestamp接口的URL串
String strUrl = ALIPAY_GATEWAY_NEW + "service=query_timestamp&partner=" + AlipayConfig.partner + "&_input_charset" +AlipayConfig.input_charset;
StringBuffer result = new StringBuffer();
SAXReader reader = new SAXReader();
Document doc = reader.read(new URL(strUrl).openStream());
List<Node> nodeList = doc.selectNodes("//alipay/*");
for (Node node : nodeList) {
// 截取部分不需要解析的資訊
if (node.getName().equals("is_success") && node.getText().equals("T")) {
// 判斷是否有成功标示
List<Node> nodeList1 = doc.selectNodes("//response/timestamp/*");
for (Node node1 : nodeList1) {
result.append(node1.getText());
}
}
}
return result.toString();
}
}
這幾個類調用支付寶接口的是AlipaySubmit,在網頁選好購買的商品時,在系統中生成訂單,然後進行支付,浏覽器跳轉到支付寶支付網站,Controller中代碼為:
/**
* 支付寶支付頁面
*
* @return
* @throws IOException
*/
@RequestMapping(value = "/aliPay")
public void aliPay(HttpServletRequest request, HttpServletResponse response) throws IOException {
LOGGER.info("支付寶支付頁面");
//商戶訂單号,商戶網站訂單系統中唯一訂單号,必填
String orderNo = request.getParameter("orderNo");
//訂單名稱,必填
String subjectName = request.getParameter("subjectName");
//付款金額,必填
String total_fee = request.getParameter("fee");
//商品描述,可空
String body = "法海風控 " + subjectName;
if ("money".equals(body)) {
body = "法海風控 餘額充值";
}
//把請求參數打包成map
Map<String, String> sParaTemp = new HashMap<String, String>();
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("anti_phishing_key", AlipayConfig.anti_phishing_key);
sParaTemp.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);
sParaTemp.put("out_trade_no", orderNo);
sParaTemp.put("subject", subjectName);
sParaTemp.put("total_fee", total_fee);
sParaTemp.put("body", body);
//其他業務參數根據線上開發文檔,添加參數.文檔位址:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.O9yorI&treeId=62&articleId=103740&docType=1
//如sParaTemp.put("參數名","參數值");
//建立請求
String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get","确認");
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
System.out.println(sHtmlText);
out.println(sHtmlText);
}
請求aliPay會跳轉到支付寶支付頁面:
3 、接收支付結果通知
在配置好notify_url之後,支付結果會請求相應的接口,我的是order/pay/aliPayOrder,代碼如下
/**
* 支付寶支付訂單
* @return
* @throws IOException
*/
@ResponseBody
@RequestMapping(value = "pay/aliPayOrder", method = RequestMethod.POST)
public void aliPayOrder(HttpServletRequest request,HttpServletResponse response) throws IOException {
LOGGER.info("支付訂單");
//從request中獲得參數Map,并傳回可讀的Map
Map<String, String> params = RequestUtil.getParameterMap(request);
LOGGER.info(params.toString());
//驗證支付寶簽名
boolean aliSign = AlipayNotify.verify(params);
if (aliSign) {//驗證成功
//交易狀态
String tradeStatus = params.get("trade_status");
//訂單編号
String orderNo = params.get("out_trade_no");
//支付單号
String payNo = params.get("trade_no");
//支付賬号
String payAccount = params.get("buyer_email");
//支付金額
String totalFee = params.get("total_fee");
//收款支付寶賬号
String sellerId = params.get("seller_id");
if (Constant.ALIPAY_TRADE_SUCCESS.equals(tradeStatus)) {//支付寶支付狀态為成功
//驗證支付寶傳回資訊與請求資訊一緻
if (ProInfoUtil.getInstance().getProperty("alipay_partner").equals(sellerId)) {
//訂單處理狀态
String orderHandleStatus = "error";
//驗證訂單未做支付處理
Order order = orderService.queryOrderByOrderNo(orderNo);
//訂單已支付
if (Constant.DEALSTATUS_PAY.equals(order.getDealStatus())) {
response.getWriter().print("success");
return;
}
if (null != order && Double.parseDouble(totalFee) == order.getDealPrice() &&
Constant.DEALSTATUS_NOT_PAY.equals(order.getDealStatus())) {//驗證金額是否和訂單一緻
//更新訂單為已支付、更新使用者套餐餘額、添加使用者充值記錄、添加使用者餘額支出記錄
order.setDealStatus(Constant.DEALSTATUS_PAY);
order.setPayNo(payNo);
order.setPayType(Constant.ALIPAY);
order.setPayAccount(payAccount);
try {
//支付成功處理支付業務
boolean result = orderService.payOrder(order);
if (result) {
//成功後向支付寶傳回成功标志
LOGGER.info("支付寶支付成功");
orderHandleStatus = "success";
response.getWriter().print("success");
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.info("支付寶支付失敗");
response.getWriter().print("fail");
}
}
//添加支付資訊
Map<String, Object> map = new HashMap<String, Object>();
map.put("params", params.toString());
map.put("payType", Constant.ALIPAY);
map.put("orderNo", orderNo);
map.put("handleStatus", orderHandleStatus);
orderService.addPayInfo(map);
}
}
} else {//驗證失敗
LOGGER.info("支付寶傳回驗證失敗");
response.getWriter().print("fail");
}
}
/**
* 從request中獲得參數Map,并傳回可讀的Map
*
* @param request
* @return
*/
@SuppressWarnings("unchecked")
public static Map getParameterMap(HttpServletRequest request) {
// 參數Map
Map properties = request.getParameterMap();
// 傳回值Map
Map<String, String> returnMap = new HashMap<String, String>();
Iterator entries = properties.entrySet().iterator();
Map.Entry entry;
String name = "";
String value = "";
while (entries.hasNext()) {
entry = (Map.Entry) entries.next();
name = (String) entry.getKey();
Object valueObj = entry.getValue();
if(null == valueObj){
value = "";
}else if(valueObj instanceof String[]){
String[] values = (String[])valueObj;
for(int i=0;i<values.length;i++){
value = values[i] + ",";
}
value = value.substring(0, value.length()-1);
}else{
value = valueObj.toString();
}
returnMap.put(name, value);
}
return returnMap;
}
至此,支付寶支付功能已經做完了,其中有幾個細節需要添加,比如支付時查詢訂單狀态是否已經支付,是否過期等等,可以根據自己的需求去完善。
支付寶的接入還是很順利的,如果熟練的話一兩天就可以完成了,剛開始寫部落格,有錯誤或者不明白的地方歡迎大家指出一起交流學習,共同進步。
由于篇幅問題,我在下一章介紹微信支付的接入。
轉至https://blog.csdn.net/qukaiwei/article/details/53925799 侵權聯系删除