一. APP支付
APP支付適用於商家在 App 應用中集成支付寶支付功能。 商家APP調用支付寶提供的 SDK,SDK 再調用支付寶APP內的支付模塊。如果用戶已安裝支付寶 APP,商家 APP 會跳轉到支付寶中完成支付,支付完後跳回到商家APP內,最後展示支付結果。如果用戶沒有安裝支付寶 APP,商家 APP 內會調起支付寶網頁支付收銀臺,用戶登錄支付寶賬戶,支付完後展示支付結果。 目前支持手機系統有:iOS(蘋果)、Android(安卓)。
【用戶已安裝支付寶支付流程】
◆ 用戶在商家 App 中選擇商品下單、確認購買,進入支付環節,選擇支付寶,用戶點擊確認支付,如圖1
◆ 進入到支付寶頁面,調起支付寶支付,出現確認支付界面,如圖2
◆ 用戶確認收款方和金額,點擊立即支付後出現輸入密碼界面,如圖3
◆ 輸入正確密碼後,支付寶端顯示支付結果,如圖 4; 5自動回跳到商家 App 中,商家根據付款結果個性化展示訂單處理結果,如圖 5。
【用戶未安裝支付寶支付流程】
◆ 用戶在商家 App 中選擇商品下單、確認購買,進入支付環節,選擇支付寶,用戶點擊確認支付,如圖 6;
◆ 用戶未安裝支付寶客戶端,則調起支付寶網頁支付收銀臺,用戶登錄支付寶賬戶,如圖 7;
◆ 登錄成功後,進入確認付款頁面,如圖 8;
◆ 用戶點擊確認付款,進入支付密碼頁面,如下圖 9; 5.用戶輸入密碼,完成支付,展示支付結果,如圖 10
【接入前準備】
參考官網:https://opendocs.alipay.com/open/204/105297
【Alipay通用工具類】
package com.maiji.cloud.utils;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alipay.api.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayFundTransOrderQueryRequest;
import com.alipay.api.request.AlipayFundTransToaccountTransferRequest;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayFundTransOrderQueryResponse;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.maiji.cloud.request.shopingmall.AlipayRefundReqData;
import com.maiji.cloud.request.shopingmall.AlipayRefundReqData.RefundReqInner;
import org.apache.commons.lang.StringUtils;
public class AlipayUtil {
public static final String APP_ID = "";
// 支付寶支付私鑰
public static final String PRIVATE_KEY = "";
// 支付寶支付公鑰
public static final String PUBLIC_KEY = "";
// 支付成功回調url
public static final String NOTIFY_URL = "";
public static String createSign(Map parameters) throws Exception {
List<String> keys = new ArrayList<String>(parameters.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
Object value = parameters.get(key);
// value = URLEncoder.encode(value, "UTF-8");
if(StringUtils.isNotBlank(parameters.get(key)+"") && value != null) {
if (i == keys.size() - 1) {//拼接時,不包括最後一個&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
}
return prestr;
}
public static String createSignValues(Map parameters) throws Exception {
List<String> keys = new ArrayList<String>(parameters.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
Object value = parameters.get(key);
if(StringUtils.isNotBlank(parameters.get(key)+"") && value != null) {
value = URLEncoder.encode(value+"", "UTF-8");
if (i == keys.size() - 1) {//拼接時,不包括最後一個&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
}
return prestr;
}
public static AlipayFundTransToaccountTransferResponse alipayWithdraw(String ioUuid,String realName,String account,Double amount) throws AlipayApiException{
Map<String, Object> map = new HashMap<String, Object>();
map.put("out_biz_no", ioUuid);
map.put("payee_type", "ALIPAY_LOGONID");
map.put("payee_account", account);
map.put("payee_real_name", realName);
map.put("amount", amount);
map.put("remark", "提現");
AlipayClient alipayClient = new DefaultAlipayClient ("https://openapi.alipay.com/gateway.do", AlipayUtil.APP_ID,
AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY,"RSA2");
AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
request.setBizContent(JSON.toJSONString(map));
AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
return response;
}
public static AlipayTradeRefundResponse alipayRefund(String orderNo,String payId, Double refundMoney) throws Exception {
String nowTime = TimeUtil.date2String(new Date(), null);
AlipayRefundReqData aliPayResData = new AlipayRefundReqData();
aliPayResData.setApp_id(AlipayUtil.APP_ID);
aliPayResData.setCharset("utf-8");
aliPayResData.setMethod("alipay.trade.refund");
aliPayResData.setSign_type("RSA2");
aliPayResData.setTimestamp(nowTime);
aliPayResData.setVersion("1.0");
RefundReqInner aliPayResInner = aliPayResData.dataInstance();
aliPayResInner.setOut_trade_no(orderNo);
aliPayResInner.setRefund_amount(refundMoney);
aliPayResInner.setTrade_no(payId);
aliPayResInner.setOut_request_no(orderNo);
Map paramMap = WXUtil.objectToMap(aliPayResData);
paramMap.remove("biz_content");
paramMap.put("biz_content", JSON.toJSON(aliPayResInner));
String signString = AlipayUtil.createSign(paramMap);
String sign = AlipaySignature.rsaSign(signString, AlipayUtil.PRIVATE_KEY, "utf-8", "RSA2");
aliPayResData.setSign(sign);
paramMap.put("sign", sign);
AlipayClient alipayClient = new DefaultAlipayClient ("https://openapi.alipay.com/gateway.do",
AlipayUtil.APP_ID,AlipayUtil.PRIVATE_KEY,"json","utf-8",AlipayUtil.PUBLIC_KEY,"RSA2");
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
request.setBizContent( JSON.toJSONString(aliPayResInner));
AlipayTradeRefundResponse response = alipayClient.execute(request);
if (!"Y".equals(response.getFundChange())) {
throw new Exception("調用退款接口退款未成功");
}
return response;
}
}
【app支付接口2.0】
@NoArgsConstructor
@Data
@Accessors(chain = true)
public class AlipayDto{
@ApiModelProperty(value="總金額")
private double totalMoney;
@ApiModelProperty(value="訂單號")
private String out_trade_no;
}
@PostMapping("alipay")
public BaseDataResDto<AlipayTradeAppPayResponse> alipay(@RequestBody AlipayDto param, @RequestHeader("token") String token) throws Exception {
return capitalMainLogService.alipay(param, token);
}
@Override
public BaseDataResDto<AlipayTradeAppPayResponse> alipay(AlipayDto param, String token) throws Exception {
// 實例化客戶端
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", AlipayUtil.APP_ID,
AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY, "RSA2");
// 實例化具體API對應的request類,類名稱和接口名稱對應,當前調用接口名稱:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
// SDK已經封裝掉了公共參數,這裏只需要傳入業務參數。以下方法爲sdk的model入參方式(model和biz_content同時存在的情況下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject("商品支付");
model.setBody("購買商品-請儘快付款");
model.setOutTradeNo(param.getOut_trade_no());
model.setTotalAmount(param.getTotalMoney() + "");
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
request.setNotifyUrl(AlipayUtil.NOTIFY_URL);
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if(response.isSuccess()){
return new BaseDataResDto(Status.SUCCESS).setData(response);
}
return new BaseDataResDto(Status.ERROR);
}
【支付寶支付成功回調】
對於App支付產生的交易,支付寶會根據原始支付 API 中傳入的異步通知地址 notify_url,通過 POST 請求的形式將支付結果作爲參數通知到商戶系統,詳見官方文檔:https://opendocs.alipay.com/open/204/105301
@PostMapping("alipayCallBack")
public String alipayCallBack(HttpServletRequest request) {
Map<String, String> parameters = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
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"); 這段代碼在出現亂碼時使用
parameters.put(key, valueStr);
}
return capitalMainLogService.alipayCallBack(parameters);
}
@Override
public String alipayCallBack(Map<String, String> param) throws AlipayApiException{
boolean signVerified = AlipaySignature.rsaCheckV1(param, AlipayUtil.PUBLIC_KEY, param.get("charset"), param.get("sign_type")); // 調用SDK驗證簽名
if (!signVerified) return "failure";
ShopingOrder shopingOrder = orderDao.getByOrderNo(param.get("out_trade_no"));
// 已經付款
if (Arrays.asList(1, 2, 3, 5, 6).contains(shopingOrder.getStatus())) {
return "success";
}
// 判斷金額是否一致
if(!StringUtils.equals(shopingOrder.getAmount(), param.get("total_amount"))){
return "failure"
}
if ("TRADE_SUCCESS".equals( param.get("trade_status"))) {
// 具體業務邏輯,如修改訂單狀態,流水記錄,積分等...
}
return "success";
}
【查詢訂單】
該接口提供所有支付寶支付訂單的查詢,商戶可以通過該接口主動查詢訂單狀態,完成下一步的業務邏輯。 需要調用查詢接口的情況: 當商戶後臺、網絡、服務器等出現異常,商戶系統最終未接收到支付通知; 調用支付接口後,返回系統錯誤或未知交易狀態情況; 調用alipay.trade.pay,返回INPROCESS的狀態; 調用alipay.trade.cancel之前,需確認支付狀態。
@RestController
public class AlipayController {
@PostMapping("/alipayOrderQuery")
public BaseDataResDto<Map<String, Object>> alipayOrderQuery(@RequestBody BaseReqDto<String> baseReqDto){
String out_trade_no = baseReqDto.getData(); // 商戶訂單號
// 訂單支付時傳入的商戶訂單號,和支付寶交易號不能同時爲空,trade_no,out_trade_no如果同時存在優先取trade_no
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
AlipayUtil.APP_ID, AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY, "RSA2");
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+"\"trade_no\":\""+ "" +"\"}");
try{
AlipayTradeQueryResponse response = alipayClient.execute(request);
if(response.isSuccess()){
return new BaseDataResDto(Status.SUCCESS).setData(response);
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return new BaseDataResDto(Status.ERROR);
}
}
【退款】
當交易發生之後一段時間內,由於買家或者賣家的原因需要退款時,賣家可以通過退款接口將支付款退還給買家,支付寶將在收到退款請求並且驗證成功之後,按照退款規則將支付款按原路退到買家帳號上。 交易超過約定時間(簽約時設置的可退款時間)的訂單無法進行退款 支付寶退款支持單筆交易分多次退款,多次退款需要提交原支付訂單的商戶訂單號和設置不同的退款單號。一筆退款失敗後重新提交,要採用原來的退款單號。總退款金額不能超過用戶實際支付金額。
這裏的交易退款接口是指統一收單交易退款接口(alipay.trade.refund),統一收單交易退款接口本身接口不支持設置notify_url參數,因此退款導致觸發的異步通知是發送到支付接口中設置的notify_url。
交易退款接口是否會觸發異步通知:https://opensupport.alipay.com/support/helpcenter/193/201602484851
@Override
public BaseResDto executeRefund(String orderRefundId) throws Exception{
ShoppingOrderRefundEntity shoppingOrderRefund = shopingOrderRefundService.selectById(orderRefundId).setRefundMiddleTime(new Date()).setStatus(3);//退款中
String orderId = shoppingOrderRefund.getOrderId();
ShopingOrder shopingOrder = shopingOrderMapper.selectById(orderId) .setRefundStatus(3);//退款中
Double refundMoney = shoppingOrderRefund.getRefundMoney();
Double amount = shopingOrder.getAmount();
if (refundManey > amount)
return BaseResDto.baseResDto(Status.ERROR, "退款金額錯誤!");
AlipayTradeRefundResponse response = AlipayUtil.alipayRefund(shopingOrder.getOrderNo(), shopingOrder.getPayId(), refundManey);
if(response.isSuccess()){
return new BaseDataResDto(Status.SUCCESS).setData(response);
}
// 下面接入自己的業務邏輯...
return new BaseResDto(Status.SUCCESS);
}
【查詢退款結果】
官方文檔:https://opendocs.alipay.com/apis/api_1/alipay.trade.fastpay.refund.query
@RestController
public class AliPayController {
@PostMapping("/alipayRefundQuery")
public BaseDataResDto<Map<String,Object>> alipayRefundQuery(@RequestBody Map<String,Object> param){
Object outTradeNo = param.get("outTradeNo");
Object outRequestNo = param.get("outRequestNo");
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
AlipayUtil.APP_ID, AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY, "RSA2");
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
request.setBizContent("{\"out_trade_no\":\""+ outTradeNo +"\","
+"\"out_request_no\":\""+ outRequestNo +"\"}");
try{
AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
if(response.isSuccess()){
return new BaseDataResDto(Status.SUCCESS).setData(response);
}
} catch (AlipayApiException e){
e.printStackTrace();
}
return new BaseDataResDto(Status.ERROR);
}
}
【轉到到支付寶賬戶】(提現)
官方文檔:https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer
public static AlipayFundTransToaccountTransferResponse alipayWithdraw(String orderNo,String realName,String account,Double amount) throws AlipayApiException{
Map<String, Object> map = new HashMap<String, Object>();
map.put("out_biz_no", orderNo);
map.put("payee_type", "ALIPAY_LOGONID");
map.put("payee_account", account);
map.put("payee_real_name", realName);
map.put("amount", amount);
map.put("remark", "提現");
AlipayClient alipayClient = new DefaultAlipayClient ("https://openapi.alipay.com/gateway.do", AlipayUtil.APP_ID,
AlipayUtil.PRIVATE_KEY, "json", "utf-8", AlipayUtil.PUBLIC_KEY,"RSA2");
AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
request.setBizContent(JSON.toJSONString(map));
AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
return response;
}
public static void main (String[] args) throws Exception {
AlipayFundTransToaccountTransferResponse response = AlipayUtil.alipayWithdraw(UUID_MD5.getUUID(), "姓名", "手機號碼",0.1 );
System.out.println(JSON.toJSONString(response));
if(response.isSuccess()){
System.out.println("調用成功");
} else {
System.out.println("調用失敗");
}
}
二. 電腦網站支付
@RestController
public class AlipayPC {
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
AlipayUtil.APP_ID,AlipayUtil.PRIVATE_KEY,"json","utf-8",AlipayUtil.PUBLIC_KEY,"RSA2");
@PostMapping("/alipayPC")
public String alipayPC() throws Exception{
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
/** 同步通知,支付完成後,支付成功頁面*/
alipayRequest.setReturnUrl("自定義");
/** 異步通知,支付完成後,需要進行的異步處理*/
alipayRequest.setNotifyUrl(AlipayUtil.NOTIFY_URL);
String subject = "測試付款";
alipayRequest.setBizContent("{\"out_trade_no\":\""+ 1234567 +"\","
+ "\"total_amount\":\""+ 0.1 +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ "商品費用,請儘快付款" +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
/**轉換格式 (這裏生成一個表單,會自動提交) */
return alipayClient.pageExecute(alipayRequest).getBody().replace('\"','\'').replace('\n',' ');
}
}
將接口的form表單返給前端,前端直接新開一個頁面即可跳到支付寶網關支付頁面 ~
電腦網站支付,手機網站支付,APP支付,三種支付方式的查詢訂單,退款,查詢退款等的API是一致的,沒什麼特殊情況,支付成功回調也是一致的,完全可以配成一個異步回調 ~
最後附上支付寶沙箱環境的鏈接:https://opendocs.alipay.com/open/200/105311