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(); }