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