場景
最近在做支付寶的接口對接,之前做過一個版本,但是由於申請了新的賬號以前舊的的接口對接就不能使用了
所以就開始對接新的版本接口對接,在這里也記錄一下讓那些還沒有對接的兄弟少走點彎路。
准備
先申請一個企業賬戶
https://memberprod.alipay.com/account/reg/enterpriseIndex.htm
創建應用
去支付寶的開放平台
https://open.alipay.com/platform/home.htm
添加應用:
https://docs.open.alipay.com/200/105310
按照這個鏈接的文檔一步一步操作;
賬號准備好之后,就可以了解相關的接口了,以(即時到賬)支付接口為例。
閱讀接口文檔
https://docs.open.alipay.com/270/alipay.trade.page.pay/
下載SDK
https://docs.open.alipay.com/54/103419
選擇java版本
將sdk集成進入項目中
sdk中有一個jar包
將這個jar包上傳到私服上去。
參考這個:
http://blog.csdn.net/huchunlinnk/article/details/17789175
項目引入sdk中的關鍵jar包
這里的gav的寫法取決於你上傳私服的時候的填寫
<dependency>
<groupId>com.alipay</groupId>
<artifactId>alipay-api</artifactId>
<version>1.0.0</version>
</dependency>
處理AlipayConfig對象
取消AlipayConfig配置文件中的部分常量
打開下載sdk應該可以找到
將這個類中的前幾個靜態常量變成非靜態的,以便可以支持多個配置對象。
/* *
*類名:AlipayConfig
*功能:基礎配置類
*詳細:設置帳戶有關信息及返回路徑
*修改日期:2017-04-05
*說明:
*以下代碼只是為了方便商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,並非一定要使用該代碼。
*該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。
*/
@Data
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓請在這里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號
public String app_id = "";
// 商戶私鑰,您的PKCS8格式RSA2私鑰
public String merchant_private_key = "";
// 支付寶公鑰,查看地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。
public String alipay_public_key = "";
// 服務器異步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問
public String notify_url = "";
// 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問
public String return_url = "";
// 簽名方式
public static String sign_type = "RSA2";
// 字符編碼格式
public static String charset = "utf-8";
// 支付寶網關
public static String gatewayUrl = "https://openapi.alipay.com/gateway.do";
//日志路徑
public static String log_path = "C:\\";
}
編寫請求支付的接口
后端向支付寶發起支付的請求,在這個同時需要傳遞必要參數,下面我們就來編寫
如何向支付寶發起支付。
編寫conroller層
/**
* 阿里支付控制器
*
* @author WangSen(wangsenhehe@126.com)
* @Date 2017年8月16日
*/
@Controller
@RequestMapping("/alipay")
public class AliPayController {
@Autowired
private AlipayViewService alipayViewService;
/**
* 跳轉到去支付的jsp頁面
*
* @param orderId 訂單號
*
* @param payAccountType 支付賬號類型
*
* @param model 模型
*
* @throws Exception
*/
@RequestMapping
public void gotopay(long orderId, Model model) throws Exception {
alipayViewService.setGoToPayInfo(orderId, model);
}
}
編寫viewService層
/**
* 阿里支付頁面服務類
*
* @author WangSen(wangsenhehe@126.com)
* @Date 2017年8月16日
*/
@Service
public class AlipayViewService {
@Autowired
private NewAlipayBusinessService newAlipayBusinessService;
/**
* 設置去支付信息
*
* @param orderId 訂單id
* @param model 模型
* @param payAccountType 支付賬號類型
*
* @return 構建的字符串
*/
public void setGoToPayInfo(long orderId, Model model) throws Exception {
model.addAttribute("htmlStr", newAlipayBusinessService.buildPayRequest(orderId, payPrice, "報名費", "略"));
}
}
編寫service層
/**
* 新的阿里支付頁面服務類
* <p>
* 阿里升級接口之后使用這個服務類
*
* @author WangSen(wangsenhehe@126.com)
* @Date 2017年8月16日
*/
@Data
public class NewAlipayBusinessService {
/**
* 阿里的配置文件對象
*/
private AlipayConfig alipayConfig;
/**
* 構建支付請求
*
* @param orderId 訂單號
* @param payPrice 付款金額
* @param orderName 訂單名稱
* @param body 商品描述
*
* @return html字符串
*/
public String buildPayRequest(long orderId, long payPrice, String orderName, String body) throws Exception {
//獲得初始化的AlipayClient
AlipayClient alipayClient = getAlipayClient();
//設置請求參數
String bizContent = getBizContent(ConvertUtil.obj2str(orderId), AmountUtils.changeF2Y(payPrice), orderName,
body);
return alipayClient.pageExecute(setAlipayRequestParameters(bizContent)).getBody();
}
/**
* 設置阿里支付請求參數
*
* @param bizContent 包含關鍵參數的json字符串
*
* @return AlipayTradePagePayRequest對象
*/
private AlipayTradePagePayRequest setAlipayRequestParameters(String bizContent) {
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(alipayConfig.getReturn_url());
alipayRequest.setNotifyUrl(alipayConfig.getNotify_url());
alipayRequest.setBizContent(bizContent);
return alipayRequest;
}
private AlipayClient getAlipayClient() {
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, alipayConfig.getApp_id(),
alipayConfig.getMerchant_private_key(), "json", AlipayConfig.charset,
alipayConfig.getAlipay_public_key(), AlipayConfig.sign_type);
return alipayClient;
}
/**
* 獲取業務的關鍵內容
*
* @param out_trade_no 訂單號
* @param total_amount 付款金額
* @param subject 訂單名稱
* @param body 商品描述
*
* @return 拼接之后的json字符串
*/
private String getBizContent(String out_trade_no, String total_amount, String subject, String body) {
ExceptionUtil.checkEmpty(out_trade_no, "訂單號不能為空");
ExceptionUtil.checkEmpty(total_amount, "價格不能為空");
ExceptionUtil.checkEmpty(subject, "訂單名稱不能為空");
ExceptionUtil.checkEmpty(body, "商品描述不能為空");
StringBuffer sb = new StringBuffer();
sb.append("{");
sb.append("\"out_trade_no\":\"").append(out_trade_no).append("\",");
sb.append("\"total_amount\":\"").append(total_amount).append("\",");
sb.append("\"subject\":\"").append(subject).append("\",");
sb.append("\"body\":\"").append(body).append("\",");
sb.append("\"product_code\":\"FAST_INSTANT_TRADE_PAY\"");
sb.append("}");
return sb.toString();
}
}
通過xml文件配置支付對象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!-- 數理化支付配置對象 -->
<bean id="alipayConfig" class="com.we.business.pay.newalipay.config.AlipayConfig">
<property name="app_id" value="你的appId"/>
<property name="merchant_private_key" value="你的私鑰"/>
<property name="alipay_public_key" value="支付寶公鑰"/>
<property name="notify_url" value="支付完成的異步通知地址"/>
<property name="return_url" value="支付完成跳轉的地址"/>
</bean>
<!-- 阿里支付業務服務類 -->
<bean id="newAlipayBusinessService" class="com.we.business.pay.service.NewAlipayBusinessService">
<property name="alipayConfig" ref="alipayConfig"/>
</bean>
</beans>
編寫jsp頁面
<%@page import="com.we.core.common.util.DateTimeUtil"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>支付寶即時到賬交易接口</title>
</head>
${htmlStr }
<body>
</body>
</html>
測試
頁面訪問:localhost:8091/alipay/gotopay.json?orderId=232323
就可以看到支付頁面了;
編寫支付的異步通知接口
用戶支付完成之后支付寶會通過你配置的的notify_url的值進行回調,
我需要編寫這個邏輯,以完成整個的支付流程。
編寫controller層
/**
* 阿里支付控制器
*
* @author WangSen(wangsenhehe@126.com)
* @Date 2017年8月16日
*/
@Controller
@RequestMapping("/alipay")
public class AliPayController {
@Autowired
private AlipayViewService alipayViewService;
/**
* 支付完成
*/
@Void
@NotSso
@ResponseBody
@RequestMapping
public void payFinish() throws Exception {
alipayViewService.payFinish();
}
}
編寫viewService層
/**
* 阿里支付頁面服務類
*
* @author WangSen(wangsenhehe@126.com)
* @Date 2017年8月16日
*/
@Service
public class AlipayViewService {
@Autowired
private NewAlipayBusinessService newAlipayBusinessService;
/**
* 支付完成
*
* @throws IOException io異常
*/
public void payFinish() throws Exception {
newAlipayBusinessService.payFinish();
}
}
編寫處理支付完成的servie
/**
* 新的阿里支付頁面服務類
* <p>
* 阿里升級接口之后使用這個服務類
*
* @author WangSen(wangsenhehe@126.com)
* @Date 2017年8月16日
*/
@Data
public class NewAlipayBusinessService {
/**
* 阿里的配置文件對象
*/
private AlipayConfig alipayConfig;
/**
* 支付完成
*
* @throws Exception 異常對象
*/
public void payFinish() throws Exception {
HttpServletRequest request = MvcUtil.getRequest();
PrintWriter out = MvcUtil.getResponse().getWriter();
//獲取支付寶POST過來反饋信息
Map<String, String> params = getParames(request);
if (!isSuccess(params)) {
fail(out);
return;
}
long orderId = getOrderId(params);
long payPrice = getTotalFee(params);
try {
//編輯你支付完成之后的邏輯
success(out);
} catch (Exception e) {
fail(out);
}
}
/**
* 獲取訂單id
*
* @param params 請求參數
* @return 訂單id
*/
private long getOrderId(Map<String, String> params) {
String order_no = params.get("out_trade_no");
return ConvertUtil.obj2long(order_no);
}
/**
* 獲取總金額
*
* @param params 請求參數
* @return 總金額
*/
private long getTotalFee(Map<String, String> params) {
String total_fee = params.get("total_amount");
return ConvertUtil.obj2long(AmountUtils.changeY2F(total_fee));
}
/**
* 校驗支付寶支付是否成功
*
* @param params http請求
* @return 成功即為真
* @throws AlipayApiException
*/
private boolean isSuccess(final Map<String, String> params) throws AlipayApiException {
boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipay_public_key(),
AlipayConfig.charset, AlipayConfig.sign_type); //調用SDK驗證簽名
if (!signVerified) {
return false;
}
//交易狀態
String trade_status = params.get("trade_status");
if (!trade_status.equals("TRADE_FINISHED") && !trade_status.equals("TRADE_SUCCESS")) {
return false;
}
return true;
}
/**
* 成功
*
* TODO 重構方法名
* @param out 輸出流
*/
private void success(PrintWriter out) {
out.println("success");
}
/**
* 失敗
*
* TODO 重構方法名
* @param out 輸出流
*/
private void fail(PrintWriter out) {
out.println("fail");
}
/**
* 獲取參數
*
* @param request HttpServletRequest對象
*
* @return 返回支付寶攜帶的參數
*/
private Map<String, String> getParames(HttpServletRequest request) {
Map<String, String> params = new HashMap<String, String>();
@SuppressWarnings("unchecked")
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = ConvertUtil.obj2str(iter.next());
@SuppressWarnings("cast")
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);
}
return params;
}
}
做的過程可能用到的資源
支付接口以及異步通知接口的參數詳解:
https://docs.open.alipay.com/270/105902/
生成公鑰私鑰的步驟
https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1
服務端的sdk
https://docs.open.alipay.com/203/105910