Java 支付寶使用公鑰證書加簽 進行支付-查詢-退款-轉賬


1、引入相關依賴 pom.xml

        <!--ali支付-->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.9.79.ALL</version>
        </dependency>
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.10</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.2.1</version>
        </dependency>
        <!--ali支付end-->

2、配置文件 application.yml

[注意]  證書文件必須放到服務器上,放到項目里的證書,在win環境下可以根據路徑找到,但打成jar包后就找不到項目里的證書文件了,解決辦法是判斷服務器環境,根據不同環境拼接證書路徑

證書文件目錄結構如下,在Linux服務器上部署時,將pay文件夾和jar包放到同一個文件夾下

  

# 支付
pay:
  alipay:
    gatewayUrl: https://openapi.alipay.com/gateway.do
    appid: *****
    appPrivateKey: 
    alipayPublicKey:
    appCertPath: /pay/alipay/cert/appCertPublicKey****.crt
    alipayCertPath: /pay/alipay/cert/alipayCertPublicKey_RSA2.crt
    alipayRootCertPath: /pay/alipay/cert/alipayRootCert.crt
    returnUrl: ****
    notifyUrl: **** 

3、讀取配置文件

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.annotation.PostConstruct;


@Data
@Slf4j
@ConfigurationProperties(prefix = "pay.alipay")
public class AlipayProperties {

    /**
     * 支付寶gatewayUrl
     */
    private String gatewayUrl;
    /**
     * 商戶應用id
     */
    private String appid;
    /**
     * RSA私鑰,用於對商戶請求報文加簽
     */
    private String appPrivateKey;
    /**
     * 支付寶RSA公鑰,用於驗簽支付寶應答
     */
    private String alipayPublicKey;

    /**
     * 簽名類型
     */
    private String signType = "RSA2";

    /**
     * 格式
     */
    private String formate = "json";
    /**
     * 編碼
     */
    private String charset = "UTF-8";

    /**
     * 同步地址
     */
    private String returnUrl;

    /**
     * 異步地址
     */
    private String notifyUrl;

    /**
     * 應用公鑰證書
     */
    private String appCertPath;

    /**
     * 支付寶公鑰證書
     */
    private String alipayCertPath;

    /**
     * 支付寶根證書
     */
    private String alipayRootCertPath;


    /**
     * 最大查詢次數
     */
    private static int maxQueryRetry = 5;
    /**
     * 查詢間隔(毫秒)
     */
    private static long queryDuration = 5000;
    /**
     * 最大撤銷次數
     */
    private static int maxCancelRetry = 3;
    /**
     * 撤銷間隔(毫秒)
     */
    private static long cancelDuration = 3000;

    private AlipayProperties() {
    }

    /**
     * PostContruct是spring框架的注解,在方法上加該注解會在項目啟動的時候執行該方法,也可以理解為在spring容器初始化的時候執行該方法。
     */
    @PostConstruct
    public void init() {
        log.info(description());
    }

    public String description() {
        StringBuilder sb = new StringBuilder("\n支付寶Configs{");
        sb.append("支付寶網關: ").append(gatewayUrl).append("\n");
        sb.append(", appid: ").append(appid).append("\n");
        sb.append(", 商戶RSA私鑰: ").append(getKeyDescription(appPrivateKey)).append("\n");
        sb.append(", 應用公鑰證書: ").append(appCertPath).append("\n");
        sb.append(", 支付寶公鑰證書: ").append(alipayCertPath).append("\n");
        sb.append(", 支付寶根證書: ").append(alipayRootCertPath).append("\n");
        sb.append(", 支付寶RSA公鑰: ").append(getKeyDescription(alipayPublicKey)).append("\n");
        sb.append(", 簽名類型: ").append(signType).append("\n");
        sb.append(", 查詢重試次數: ").append(maxQueryRetry).append("\n");
        sb.append(", 查詢間隔(毫秒): ").append(queryDuration).append("\n");
        sb.append(", 撤銷嘗試次數: ").append(maxCancelRetry).append("\n");
        sb.append(", 撤銷重試間隔(毫秒): ").append(cancelDuration).append("\n");
        sb.append("}");
        return sb.toString();
    }

    private String getKeyDescription(String key) {
        int showLength = 6;
        if (StringUtils.isNotEmpty(key) && key.length() > showLength) {
            return new StringBuilder(key.substring(0, showLength)).append("******")
                    .append(key.substring(key.length() - showLength)).toString();
        }
        return null;
    }
}

4、在啟動時初始化支付寶客戶端

import com.alipay.api.AlipayApiException;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;

import java.io.IOException;


@Configuration
@Slf4j
@EnableConfigurationProperties(AlipayProperties.class)
public class AlipayConfiguration {

    @Autowired
    private AlipayProperties properties;

//    /**
//     * 支付寶接口加簽模式為公鑰時使用
//     *
//     * @return
//     */
//    @Bean
//    public AlipayClient alipayClient() {
//        return new DefaultAlipayClient(properties.getGatewayUrl(),
//                properties.getAppid(),
//                properties.getAppPrivateKey(),
//                properties.getFormate(),
//                properties.getCharset(),
//                properties.getAlipayPublicKey(),
//                properties.getSignType());
//    }

    /**
     * 支付寶接口加簽模式為公鑰證書時使用
     *
     * @return
     */
    @Bean
    public DefaultAlipayClient alipayClient() throws AlipayApiException, IOException {
        log.info("---------創建支付寶客戶端--------");

        //判斷系統環境
        String osName = System.getProperty("os.name");
        log.info("-------系統環境--------" + osName);
        String serverpath = null;
        if (osName.startsWith("Windows")) {
            // windows
            serverpath = ResourceUtils.getURL("classpath:").getPath();
            serverpath += "static/app";
            log.info("-------------win文件路徑----------" + serverpath);
//        } else if (osName.startsWith("Mac OS")) {
//            // 蘋果
        } else {
            // unix or linux
            serverpath = System.getProperty("user.dir");
            log.info("-------------unix or linux文件路徑----------" + serverpath);
        }
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        certAlipayRequest.setServerUrl(properties.getGatewayUrl());
        certAlipayRequest.setAppId(properties.getAppid());
        certAlipayRequest.setFormat(properties.getFormate());
        certAlipayRequest.setPrivateKey(properties.getAppPrivateKey());
        certAlipayRequest.setCharset(properties.getCharset());
        certAlipayRequest.setSignType(properties.getSignType());
        certAlipayRequest.setCertPath(serverpath + properties.getAppCertPath());
        certAlipayRequest.setAlipayPublicCertPath(serverpath + properties.getAlipayCertPath());
        certAlipayRequest.setRootCertPath(serverpath + properties.getAlipayRootCertPath());
        return new DefaultAlipayClient(certAlipayRequest);
    }
}

5、APP支付

import cn.hutool.core.date.DateUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayFundTransPayModel;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

/**
 * app支付
 */
@Slf4j
@Controller
@RequestMapping("/app/alipay/app")
public class AlipayAppPayController {

    @Autowired
    private AlipayProperties alipayProperties;
    @Autowired
    private AlipayClient alipayClient;
    @Autowired
    private AlipayProperties aliPayProperties;

    /**
     * 去支付
     * <p>
     *
     * @param response
     * @throws Exception
     */
    @RequestMapping("/createOrder")
    public void createOrder(HttpServletRequest request, HttpServletResponse response,
                            @RequestBody Map<String, String> mapParams) throws AlipayApiException {
       
        BaseResault resPo = new BaseResault();
        // 訂單模型
        String productCode = "QUICK_MSECURITY_PAY";
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        Order bean = orderService.getOrderByNum(mapParams.get("orderNum"));
        model.setOutTradeNo(bean.getOrderNum());
        log.info("----訂單編號----" + model.getOutTradeNo());
        model.setSubject(bean.getShopName());
        log.info("----subject----" + model.getSubject());
        model.setTotalAmount(bean.getTotalMoney().toString());
        log.info("----金額----" + model.getTotalAmount());
        model.setTimeoutExpress("30m");
        model.setProductCode(productCode);
        AlipayTradeAppPayRequest appPayRequest = new AlipayTradeAppPayRequest();
        appPayRequest.setNotifyUrl(‘服務器域名’ + alipayProperties.getNotifyUrl());
        log.info("----notifyUrl----" + appPayRequest.getNotifyUrl());
        appPayRequest.setBizModel(model);
        //實例化具體API對應的request類,類名稱和接口名稱對應,當前調用接口名稱:alipay.trade.app.pay
        AlipayTradeAppPayResponse ali_response = alipayClient.sdkExecute(appPayRequest); //這里和使用公鑰加簽支付使用的方法不同
        //就是orderString 可以直接給客戶端請求,無需再做處理。
        log.info("---------支付寶返回---------{}", ali_response.getBody());
        resPo.setObj(ali_response.getBody());
        resPo.setCode(ConstantApp.AppTokenValidResult.SUCCESS.getCode());
        resPo.setMessage(ConstantApp.AppTokenValidResult.SUCCESS.getMessage());
        ResponseJsonUtils.json(response, resPo);
    }
} 

6、查詢訂單

    /**
     * 訂單查詢(最主要用於查詢訂單的支付狀態)
     *
     * @param orderNo 商戶訂單號
     * @return
     */
    @GetMapping("/query")
    public void query(HttpServletRequest request, HttpServletResponse response, String orderNo) throws AlipayApiException {
        BaseResault resPo = new BaseResault();
        AlipayTradeQueryRequest aliRequest = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel bizModel = new AlipayTradeQueryModel();
        bizModel.setTradeNo(orderNo);
        aliRequest.setBizModel(bizModel);
        AlipayTradeQueryResponse aliResponse = alipayClient.certificateExecute(aliRequest);
        if (aliResponse.isSuccess()) {
            log.info("查詢支付寶支付狀態-----調用成功{}", aliResponse.getBody());
            if (aliResponse.getTradeStatus().equals("TRADE_SUCCESS") || aliResponse.getTradeStatus().equals("TRADE_FINISHED")) {
                //訂單編號
                String orderNum = aliResponse.getOutTradeNo();
                // 處理業務
                resPo.setObj(aliResponse.getBody());
                resPo.setCode(ConstantApp.AppTokenValidResult.SUCCESS.getCode());
                resPo.setMessage(ConstantApp.AppTokenValidResult.SUCCESS.getMessage());
                ResponseJsonUtils.json(response, resPo);
                return;
            } else {
                log.error("支付寶訂單" + orderNo + "交易失敗,交易狀態:" + aliResponse.getTradeStatus());
                resPo.setObj(aliResponse.getBody());
                resPo.setCode(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getCode());
                resPo.setMessage(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getMessage());
                ResponseJsonUtils.json(response, resPo);
                return;
            }
        } else {
            log.info("查詢支付寶支付狀態------調用失敗{}", aliResponse.getBody());
            resPo.setObj(aliResponse.getBody());
            resPo.setCode(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getCode());
            resPo.setMessage(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getMessage());
            ResponseJsonUtils.json(response, resPo);
            return;
        }
    }

7、轉賬給用戶

 /**
     * alipay.fund.trans.uni.transfer(單筆轉賬接口)
     * <p>文檔地址
     * https://docs.open.alipay.com/api_28/alipay.fund.trans.uni.transfer/
     *
     * @param response
     * @throws Exception
     */
    @RequestMapping("/alipayToUser")
    public void alipayToUser(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException {
        BaseResault resPo = new BaseResault();
        //轉賬金額  不能少於0.1元
        String money = request.getParameter("money");
       //支付寶賬號,支持郵箱、電話號碼
        String phone = request.getParameter("phone");
        //支付寶賬戶名稱
        String name = request.getParameter("name");
        // 訂單模型
        AlipayFundTransPayModel model = new AlipayFundTransPayModel();
        //商戶端的唯一訂單號
        String orderNum = UUID.randomUUID().toString().replace("-", "");
        log.info("============支付寶轉賬給用戶-訂單號:" + orderNum + "==============");
        model.setOutBizNo(orderNum);
        //訂單總金額,單位為元,精確到小數點后兩位
        model.setTransAmount(money);
        //業務產品碼,
        //收發現金紅包固定為:STD_RED_PACKET;
        //單筆無密轉賬到支付寶賬戶固定為:TRANS_ACCOUNT_NO_PWD;
        //單筆無密轉賬到銀行卡固定為:TRANS_BANKCARD_NO_PWD
        model.setProductCode("TRANS_ACCOUNT_NO_PWD");
        //收款方信息
        Participant participant = new Participant();
        //參與方的唯一標識
        participant.setIdentity(phone);
        //參與方的標識類型,目前支持如下類型:
        //1、ALIPAY_USER_ID 支付寶的會員ID
        //2、ALIPAY_LOGON_ID:支付寶登錄號,支持郵箱和手機號格式
        participant.setIdentityType("ALIPAY_LOGON_ID");
        //參與方真實姓名,如果非空,將校驗收款支付寶賬號姓名一致性。當identity_type=ALIPAY_LOGON_ID時,本字段必填。
        participant.setName(name);
        model.setPayeeInfo(participant);
        //業務備注
        model.setRemark("轉賬測試");
        model.setBizScene("DIRECT_TRANSFER");
        AlipayFundTransUniTransferRequest appPayRequest = new AlipayFundTransUniTransferRequest();
        appPayRequest.setBizModel(model);
        //實例化具體API對應的request類,類名稱和接口名稱對應,當前調用接口名稱:alipay.fund.trans.uni.transfer(單筆轉賬接口)
        AlipayFundTransUniTransferResponse ali_response = alipayClient.certificateExecute(appPayRequest);
        if (ali_response.isSuccess()) {
            log.info("========支付寶轉賬給用戶-調用成功=======" + ali_response.getBody());
            resPo.setObj(ali_response.getBody());
            resPo.setCode(ConstantApp.AppTokenValidResult.SUCCESS.getCode());
            resPo.setMessage(ConstantApp.AppTokenValidResult.SUCCESS.getMessage());
            ResponseJsonUtils.json(response, resPo);
        } else {
            log.info("========支付寶轉賬給用戶-調用失敗=======" + ali_response.getBody());
            resPo.setObj(ali_response.getBody());
            resPo.setCode(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getCode());
            resPo.setMessage(ConstantApp.AppTokenValidResult.SYSTEM_ERROR.getMessage());
            ResponseJsonUtils.json(response, resPo);
        }
    }

8、異步通知

 /**
     * 證書支付異步通知
     * https://docs.open.alipay.com/194/103296
     */
    @RequestMapping("/notify")
    public String notify(HttpServletRequest request) {
        log.info("-----------支付異步通知----------------");
        Map requestParams = request.getParameterMap();
        log.info(">>>支付寶回調參數:" + requestParams);
        Map<String, String> params = new HashMap<>();
        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] + ",";
            }
            //亂碼解決,這段代碼在出現亂碼時使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        log.info(">>>支付寶回調參數解析:" + params);
        try {
            String alipayCertPath = System.getProperty("user.dir") + aliPayProperties.getAlipayCertPath();
            //切記alipaypublickey是支付寶的公鑰,請去open.alipay.com對應應用下查看。
            boolean flag = AlipaySignature.rsaCertCheckV1(
                    params,
                    alipayCertPath,
                    aliPayProperties.getCharset(),
                    aliPayProperties.getSignType());
            if (flag) {
                log.info(">>>支付寶回調簽名認證成功");
                //商戶訂單號
                String out_trade_no = params.get("out_trade_no");
                //交易狀態
                String trade_status = params.get("trade_status");
                //交易金額
                String amount = params.get("total_amount");
                //商戶app_id
                String app_id = params.get("app_id");

                if ("TRADE_SUCCESS".equals(trade_status) || "TRADE_FINISHED".equals(trade_status)) {
                    /**
                     * 自己的業務處理,一定要判斷是否已經處理過訂單
                     */
                    log.info("-----------^v^支付成功^v^------------");
                  
                    }
                } else {
                    log.error("沒有處理支付寶回調業務,支付寶交易狀態:{},params:{}", trade_status, params);
                }
            } else {
                log.info("支付寶回調簽名認證失敗,signVerified=false, params:{}", params);
                return "failure";
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            log.info("支付寶回調簽名認證失敗,signVerified=false, params:{}", params);
            return "failure";
        }
        return "success";
    }

9、退款

 /**
     * 同一訂單多次退款
     *
     * @param orderNo 商戶訂單號
     * @return
     */
    @Override
    public String multipleRefunds(String orderNo, String refundAmount, String refundReason) throws AlipayApiException {
        log.info("------------支付寶退款開始--------------");
        AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        // 商戶訂單號
        model.setOutTradeNo(orderNo);
        // 退款金額 單位元
        model.setRefundAmount(refundAmount);
        // 退款原因
        model.setRefundReason(refundReason);
        // 退款訂單號(同一個訂單可以分多次部分退款,當分多次時必傳)
        model.setOutRequestNo(UUID.randomUUID().toString());
        log.info("------退款參數-----{}-----{}-----{}------{}", orderNo, refundAmount, refundReason, model.getOutRequestNo());
        alipayRequest.setBizModel(model);
        AlipayTradeRefundResponse alipayResponse = alipayClient.certificateExecute(alipayRequest);
        log.info("--------支付寶退款結果---------" + alipayResponse.getBody() + "---------訂單號--" + orderNo);
        return alipayResponse.getBody();
    }

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM