支付寶調用流程
開發前的准備工作
配置應用網關
應用網關里面填寫的值就是商戶后台的異步回調地址.也就是在支付寶付完款之后,由支付寶調用商戶,便於商戶驗證訂單各信息和更新訂單狀態
授權回調地址
授權回調地址的值是指用戶在使用支付寶付款成功后從支付寶跳轉到商戶自己的頁面.
接口加密方式
這個用於商戶的簽名加密,有RSA2和RSA兩種算法類型,默認是RSA2.開發人員可以使用支付寶提供的生成方式自己生成,然后將公鑰上傳到支付寶開放平台.這里提供支付寶生成密鑰的教程和下載地址
現在提供一張的截圖,顯示配置的位置.到達方式:螞蟻金服開放文檔—> 右上角賬戶中的賬號管理—>開發者中心—>我的應用(應用) —> 選擇自己將要配置的應用名稱點擊右邊的查看—>應用信息(就是截圖看到的頁面了)
支付寶支付流程:
前端調用商戶后台支付接口
商戶后台支付接口進行一些必要的業務邏輯上的處理之后調用支付寶的支付接口(原始支付API).需要的參數詳見支付寶支付接口的參數說明.支付寶會返回一個form表單.商戶后台支付接口將表單返回給前台.
前台將表單提交給支付寶,喚起支付寶客戶端進行支付.
支付成功后支付寶會根據原始支付API中傳入的異步通知地址notify_url,通過POST請求的方式將支付結果作為參數通知到商戶后台系統.
商戶后台系統在接到支付寶的異步通知后要在驗證自己本身業務邏輯之外嚴格驗證通知數據的正確性.
商戶需要驗證該通知數據中的out_trade_no是否為商戶系統中創建的訂單號
判斷total_amount是否確實為該訂單的實際金額(即商戶訂單創建時的金額)
校驗通知中的seller_id(或者seller_email) 是否為out_trade_no這筆單據的對應的操作方(有的時候,一個商戶可能有多個seller_id/seller_email)
驗證app_id是否為該商戶本身
上述1、2、3、4有任何一個驗證不通過,則表明本次通知是異常通知,務必忽略.詳情參見https://docs.open.alipay.com/203/105286/ 異步返回的驗簽
判斷沒有任何錯誤,打印success.否則支付寶服務器會不斷重發通知,直至超過24小時22分鍾.
開發需要准備的物料
pom.xml添加支付寶依賴
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.alipay</groupId>
<artifactId>alipay-sdk</artifactId>
<version>java20170307171631</version>
</dependency>
支付寶方面的參數
代碼展示
商戶后台支付接口
/**
* 支付寶支付
* @param req
* @param response
* @throws OperationFailedException
*/
@RequestMapping("alipay/Prepay")
@ResponseBody
public void aliPrepay(@RequestBody AliPrepayReq req, HttpServletResponse response) throws OperationFailedException {
Set<ConstraintViolation<AliPrepayReq>> constraintViolationSet = validator.validate(req);
if (constraintViolationSet.size() > 0) {
throw new OperationFailedException(constraintViolationSet.iterator().next().getMessage());
}
LibraOrder libraOrder = libraService.findLibraOrderByHashCode(req.getOutTradeNo());
Product product = productRepository.findOne(Long.valueOf(libraOrder.getProduct().getId()));
if(product == null) { throw new OperationFailedException("該商品不存在"); }
try{
String form = aliPayService.generateAliPay(req);
if(!"err".equals(form)){
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(form);// 直接將完整的表單html輸出到頁面
response.getWriter().flush();
}
}catch (Exception e){
throw new OperationFailedException("支付失敗");
}
}
/**
* 支付寶方面訂單號獲取
* @param aliPrepayReq
* @return
* @throws OperationFailedException
*/
public String generateAliPay(AliPrepayReq aliPrepayReq) throws OperationFailedException {
AlipayClient alipayClient = new DefaultAlipayClient(aliPayUrl,
appId, rsaPrivateKey, "json", "UTF-8", rsaPublicKey, "RSA2"); //獲得初始化的AlipayClient
Map<String, String> param = new HashMap<>();
AlipayTradeWapPayRequest alipay_request = new AlipayTradeWapPayRequest();
// 封裝請求支付信息
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo(aliPrepayReq.getOutTradeNo());
model.setSubject(aliPrepayReq.getSubject());
Integer totalAmount = aliPrepayReq.getTotalAmount();
model.setTotalAmount(String.valueOf(totalAmount / 100.0));
// model.setBody(aliPrepayReq.getBody());
model.setProductCode(aliPrepayReq.getProductCode());
alipay_request.setBizModel(model);
// 設置異步通知地址
alipay_request.setNotifyUrl(notifyUrl);
// 設置同步地址
alipay_request.setReturnUrl(returnUrl);
String form = "";
try {
LOGGER.info("alipay_request = "+jsonService.toJson(alipay_request));
// 調用SDK生成表單
form = alipayClient.pageExecute(alipay_request).getBody();
LOGGER.info("form = "+form);
} catch (Exception e) {
throw new OperationFailedException("支付出錯");
}
return form;
}
異步回調接口
/**
* 支付寶方面異步回調
* @param request
* @return
* @throws OperationFailedException
*/
@RequestMapping("alipay/notifyCallback")
public String AlipayCallBack(HttpServletRequest request) throws OperationFailedException {
//獲取支付寶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] + ",";
}
//亂碼解決,這段代碼在出現亂碼時使用。如果mysign和sign不相等也可以使用這段代碼轉化
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
params.put(name, valueStr);
}
LOGGER.info("params = "+jsonService.toJson(params));
boolean verify_result = aliPayService.rsaCheck(params);
if(verify_result && aliPayService.dealWithAliPayChargeOrder(params)){
return "success";
}
return "false";
}
/**
* 訂單驗證
* @param params
* @return
*/
@Transactional
public boolean dealWithAliPayChargeOrder(Map<String, String> params) {
String outTradeNo = params.get("out_trade_no");
LibraOrder libraOrder = libraOrderRepository.findByHashCode(outTradeNo);
if (libraOrder == null) {
LOGGER.info("訂單" + outTradeNo + "不存在");
return false;
}
boolean locked = distributeLocker.lock(USER_ALIPAY_DEAL_LOCK_KEY);
try {
if (locked) {
String totalAmount = params.get("total_amount");
double totalPrice = Double.parseDouble(totalAmount) * 100;
if(libraOrder.getTotalPrice() != Math.round(totalPrice)){
LOGGER.info("alipay和訂單金額不一致");
return false;
}
if(totalPrice < 0 || libraOrder.getTotalPrice() < 0){
LOGGER.info("alipay或訂單金額為負");
return false;
}
//set done
libraOrder.setPayAt(new Date());
libraOrder.setUpdateAt(new Date());
libraOrder.setStatus(ClientOrderStatus.PAYED);
//后台訂單狀態初始化為 等待接待
libraOrder.setServiceStatus(ServiceStatus.SERVICE_WAIT);
libraOrderRepository.save(libraOrder);
}
} finally {
if (locked) {
//TODO + order.getUserId()
distributeLocker.unlock(USER_ALIPAY_DEAL_LOCK_KEY);
}
}
return false;
}
/**
* 驗證RSA簽名
* @param params
* @return
* @throws OperationFailedException
*/
public boolean rsaCheck(Map<String, String> params) throws OperationFailedException {
try {
boolean verify_result = AlipaySignature.rsaCheckV1(params, rsaPublicKey, charset, signType);
LOGGER.info("verify_result = "+verify_result);
return verify_result;
} catch (AlipayApiException e) {
throw new OperationFailedException("驗證簽名失敗");
}
}
---------------------