前言
記錄一下支付功能的一些方法, 僅供學習參考,不涉及商業內容,一些基本環境先說一下,後面就不再說了
jdk1.8 springBoot2.0 maven
支付寶支付對接流程
使用支付寶沙箱做演示
首先要登錄支付寶開放平臺-開發者中心-研發服務(沙箱)
- 設置RSA2(SHA256)密鑰(推薦), 這裏有公鑰證書和公鑰兩種方式,選擇不同的方式對調用alipay-sdk-java第三方sdk的方法也會不同,後面會區分標識.
- 下載支付寶沙箱app到手機,可以使用給你的沙箱賬號登錄支付寶沙箱app, 賬號有商家和買家的兩個賬號,錢都可以自己設置
- 引入支付寶第三方jar包, 公鑰證書文件放到項目目錄下
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.8.73.ALL</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.62</version>
</dependency>
- 編寫一個配置支付寶參數的類
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.IOException;
public class AlipayConfig {
// 應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號
public static final String APP_ID = "";
// 商戶私鑰,您的PKCS8格式RSA2私鑰
public static final String MERCHANT_PRIVATE_KEY = "";
// 簽名方式
public static final String SIGN_TYPE = "RSA2";
// 字符編碼格式
public static final String CHARSET = "UTF-8";
// json
public static final String FORMAT = "json";
// 支付寶網關(開發環境)
public static final String GATEWAY_URL = "https://openapi.alipaydev.com/gateway.do";
public static final String APP_CERT_PATH = "/static/CRT/appCertPublicKey_2016102300744804.crt";
public static final String ALIPAY_CERT_PATH = "/static/CRT/alipayCertPublicKey_RSA2.crt";
public static final String ALIPAY_ROOT_CERT_PATH = "/static/CRT/alipayRootCert.crt";
private volatile static AlipayClient alipayClient = null;
// 使用"公鑰證書"創建的阿里客戶端對象實例
public static AlipayClient getInstance() throws AlipayApiException, IOException {
if (alipayClient == null) {
synchronized (AlipayClient.class) {
if (alipayClient == null) {
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
File file = new File(ResourceUtils.getURL("classpath:").getPath());
certAlipayRequest.setCertPath(file.getAbsolutePath() + APP_CERT_PATH);
certAlipayRequest.setAlipayPublicCertPath(file.getAbsolutePath() + ALIPAY_CERT_PATH);
certAlipayRequest.setRootCertPath(file.getAbsolutePath() + ALIPAY_ROOT_CERT_PATH);
certAlipayRequest.setServerUrl(AlipayConfig.GATEWAY_URL);
certAlipayRequest.setAppId(AlipayConfig.APP_ID);
certAlipayRequest.setPrivateKey(AlipayConfig.MERCHANT_PRIVATE_KEY);
certAlipayRequest.setFormat(FORMAT);
certAlipayRequest.setCharset(AlipayConfig.CHARSET);
certAlipayRequest.setSignType(AlipayConfig.SIGN_TYPE);
alipayClient = new DefaultAlipayClient(certAlipayRequest);
}
}
}
return alipayClient;
}
private AlipayConfig() {
}
}
- 阿里支付的方法
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AlipayVo {
private String out_trade_no;
private String total_amount;
private String subject;
private String product_code;
private String passback_params;
}
/**
* 阿里支付接口
*
* @param outTradeNo 商號
* @param totalAmount 金額
* @param notifyUrl 回調地址
* @param passbackParams 附加數據
* @return 返回的字符串是生成的訂單,如果是app返回給前端喚起支付寶沙箱支付, 如果是網頁直接跳轉支付寶網頁支付頁面
*/
public static String alipay(String outTradeNo, String totalAmount, String notifyUrl, String passbackParams) {
try {
AlipayClient alipayClient = AlipayConfig.getInstance();
// 注意這裏是AlipayTradeAppPayRequest對象, 如果是網頁支付的話創建AlipayTradePagePayRequest對象
AlipayTradeAppPayRequest aliRequest = new AlipayTradeAppPayRequest();
aliRequest.setNotifyUrl(notifyUrl);
aliRequest.setBizContent(JSONObject.toJSONString(
AlipayVo.builder()
.out_trade_no(outTradeNo)
.subject("充值")
.product_code("FAST_INSTANT_TRADE_PAY")
.total_amount(totalAmount)
.passback_params(URLEncoder.encode(passbackParams, AlipayConfig.CHARSET))
.build()
));
try {
// 注意這裏是sdkExecute方法, 如果是網頁支付的話調用pageExecute方法
return alipayClient.sdkExecute(aliRequest).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
throw new RuntimeException("支付異常");
}
} catch (AlipayApiException | IOException e) {
e.printStackTrace();
throw new RuntimeException("創建支付實例異常");
}
}
- 支付完成之後,會被阿里調用回調的方法,注意這個方法必須是外網能夠訪問的, 可以在阿里雲linux服務器上面,運行一個後端服務, 接受回調的方法
@PostMapping(value = "/recharge")
@ApiOperation(value = "支付寶回調-充值")
public String callback(HttpServletRequest request) {
return PayUtils.notifyUrl(() -> {
//...業務邏輯
return null;
}, request);
}
/**
* 阿里支付完成回調方法
*
* @param supplier 業務邏輯
* @param request 請求
*/
public static String notifyUrl(Supplier<Void> supplier, HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
// 1.從支付寶回調的request域中取值
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
// 亂碼解決,這段代碼在出現亂碼時使用。如果mysign和sign不相等也可以使用這段代碼轉化
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
params.put(name, valueStr);
}
// 2.封裝必須參數
String out_trade_no = request.getParameter("out_trade_no");// 商戶訂單號
String subject = request.getParameter("subject");// 訂單內容
String tradeStatus = request.getParameter("trade_status");//交易狀態
log.debug("____________________支付寶返回參數start_______________________");
log.debug(JSON.toJSONString(params));
log.debug("out_trade_no: " + out_trade_no);
log.debug("subject: " + subject);
log.debug("tradeStatus: " + tradeStatus);
log.debug("____________________支付寶返回參數end_______________________");
// 3.簽名驗證(對支付寶返回的數據驗證,確定是支付寶返回的)
boolean signVerified = false;
try {
//3.1調用SDK驗證簽名
File file = new File(ResourceUtils.getURL("classpath:").getPath());
String alipayPublicCertPath = file.getAbsolutePath() + AlipayConfig.ALIPAY_CERT_PATH;
// 公鑰證書驗證方法rsaCertCheckV1
signVerified = AlipaySignature.rsaCertCheckV1(params, alipayPublicCertPath, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
} catch (AlipayApiException | FileNotFoundException e) {
e.printStackTrace();
log.error("驗籤異常");
}
// 4.對驗籤進行處理
if (signVerified) { //驗籤通過
if (tradeStatus != null && tradeStatus.equals("TRADE_SUCCESS")) { //只處理支付成功的訂單: 修改交易表狀態,支付成功
// 業務代碼
supplier.get();
log.debug("異步回調業務處理完畢.");
return "success";
} else {
log.error("驗籤通過, 但是狀態 tradeStatus is null or tradeStatus != TRADE_SUCCESS, params: " + params);
return "fail";
}
} else { //驗籤不通過
log.error("驗籤失敗");
return "fail";
}
}
- 回調方法執行完成之後,再次查詢支付寶訂單是否生成方法
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("支付寶訂單查詢vo")
public class AlipayQueryOrderVo {
private String out_trade_no;
private String trade_no;
private String org_pid;
private String[] query_options;
}
/**
* 支付寶支付訂單的查詢
*/
public static Boolean existsOrder(AlipayQueryOrderVo alipayQueryOrderVo) {
try {
AlipayClient alipayClient = AlipayConfig.getInstance();
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent(JSONObject.toJSONString(alipayQueryOrderVo));
AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);
if (response.isSuccess()) {
if (!"10000".equals(response.getCode()))
throw new RRException(response.getSubMsg());
return true;
}
throw new RuntimeException(response.getSubMsg());
} catch (AlipayApiException | IOException e) {
e.printStackTrace();
throw new RuntimeException("查詢支付訂單異常");
}
}
- 這樣支付寶支付流程就完成了