Java - 支付寶支付

一. 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章