应用配置
- 登录支付宝平台,签约需要的服务
-
切换到开放平台,创建应用并审核上线
image.png
支付配置
获取方式已在字段注释中
package org.weapon.core.pay.alipay.config;
import lombok.Data;
import java.io.Serializable;
/**
* 支付配置信息
*
* @author lieber
*/
@Data
public class AliPayConfig implements Serializable {
/**
* 设置应用Id,创建应用时支付宝提供,后续可查看
*/
private String appId;
/**
* 设置应用私钥,设置“接口加签方式”时设置的,后续不能查看,设置时牢记,文件名为:域名_私钥.txt, 非->应用私钥2048.txt
*/
private String privateKey;
/**
* 应用公钥证书内容 appCertPublicKey_xxx.cert
*/
private String appCertContent;
/**
* 支付宝公钥证书内容 -- alipayCertPublicKey_RSA2.cert
*/
private String aliPayCertContent;
/**
* 支付宝根证书内容 alipayRootCert.cert
*/
private String aliPayRootCertContent;
/**
* 可直接从证书中获取
*/
private String publicKey;
}
各个方式的支付及查询处理
引入支付宝SDK
<!-- 支付宝sdk -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.10.97.ALL</version>
</dependency>
<!-- 二维码sdk -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.1.0</version>
</dependency>
通用实体
- 下单结果,唤起支付参数
package org.weapon.core.pay.center.ali;
import lombok.Builder;
import lombok.Data;
/**
* @author lieber
*/
@Data
@Builder
public class OrderResult {
private String body;
}
- 下单参数
package org.weapon.core.pay.center.ali;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
/**
* @author lieber
*/
@Data
public class PayParam {
/** 商品标题/交易标题/订单标题/订单关键字 */
@NotBlank(message = "subject不能为空")
private String subject;
/** 外部订单号 */
@NotBlank(message = "系统订单号不能为空")
private String outTradeNo;
/** 支付总金额,单位分 */
@Min(0)
@Max(Integer.MAX_VALUE)
private Integer totalAmount;
/** 用户付款中途退出返回商户网站的地址 */
private String quitUrl;
/** 原样返回字符 */
private String addition;
/** 商品描述,不必填 */
private String body;
/** 通知地址 */
@NotBlank(message = "通知地址不能为空")
private String notifyUrl;
@NotBlank(message = "回跳地址不能为空")
private String redirectUrl;
/** 支付者 */
private String payerIp;
private Integer timeoutExpress;
}
- 退款参数
package org.weapon.core.pay.center.ali;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
/**
* @author lieber
*/
@Data
public class RefundParam {
/**
* 商户订单号
*/
@NotBlank(message = "原订单号不能为空")
private String outTradeNo;
/**
* 商户退款单号
*/
@NotBlank(message = "退款订单号不能为空")
private String outRefundNo;
/**
* 退款金额
*/
@Min(0)
@Max(Integer.MAX_VALUE)
private int refund;
/**
* 原订单总额
*/
@Min(0)
@Max(Integer.MAX_VALUE)
private int total;
/**
* 退款原因;非必传
*/
private String reason;
@NotBlank(message = "通知地址不能为空")
private String notifyUrl;
}
- 退款查询参数
package org.weapon.core.pay.center.ali;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* @author lieber
*/
@Data
public class RefundQueryParam implements Serializable {
/**
* 原订单外部交易订单号
*/
@NotBlank(message = "原订单号不能为空")
private String outTradeNo;
/**
* 退款外部交易订单号
*/
@NotBlank(message = "退款订单号不能为空")
private String outRefundNo;
}
工具类
- 生成二维码
package org.weapon.core.pay.center.ali;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Hashtable;
import java.util.Map;
@Slf4j
public class QrCodeUtil {
private static final int BLACK = 0xFF000000;
private static final int WHITE = 0xFFFFFFFF;
private static String toBufferedImage(BitMatrix matrix) throws IOException {
int width = matrix.getWidth();
int height = matrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
}
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(image, "png", stream);
return Base64.getEncoder().encodeToString(stream.toByteArray());
}
/**
* 将内容contents生成长宽均为width的图片
*/
public static String getQrCodeImage(String contents, int width) {
return getQrCodeImage(contents, width, width);
}
/**
* 将内容contents生成长为width,宽为width的图片
*/
public static String getQrCodeImage(String contents, int width, int height) {
try {
Map<EncodeHintType, Object> hints = new Hashtable<>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.CHARACTER_SET, "UTF8");
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, hints);
return toBufferedImage(bitMatrix);
} catch (Exception e) {
log.error("create QR code error!", e);
return null;
}
}
}
- 获取支付配置,可使用配置文件、数据库等
package org.weapon.core.pay.center.ali;
/**
* @author lieber
*/
public interface IAliPayConfigService {
/**
* 获取支付宝支付配置
*
* @return 配置
*/
AliPayConfig getConfig();
}
支付宝支付及相关处理对接
package org.weapon.core.pay.center.ali;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* @author lieber
*/
@Slf4j
public class AliPay {
private final static String SERVER_URL = "https://openapi.alipay.com/gateway.do";
private final static String SIGN_TYPE = "RSA2";
private final static String FORMAT = "json";
private final BigDecimal ONE_HUNDRED = new BigDecimal(100);
private IAliPayConfigService configService;
public AliPay(IAliPayConfigService configService) {
this.configService = configService;
}
private AlipayClient client = null;
/**
* 扫码支付
*
* @param payParam 支付参数
* @return 结果
* @throws AlipayApiException 支付宝调用异常
*/
public OrderResult callNative(PayParam payParam) throws AlipayApiException {
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
model.setBody(payParam.getBody());
model.setSubject(payParam.getSubject());
model.setOutTradeNo(payParam.getOutTradeNo());
String timeoutExpress = this.timeoutExpress(payParam.getTimeoutExpress());
model.setTimeoutExpress(timeoutExpress);
model.setQrCodeTimeoutExpress(timeoutExpress);
model.setTotalAmount(new BigDecimal(payParam.getTotalAmount()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
model.setProductCode("FACE_TO_FACE_PAYMENT");
request.setBizModel(model);
request.setNotifyUrl(payParam.getNotifyUrl());
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradePrecreateResponse response = this.getClient().sdkExecute(request);
return OrderResult.builder().body(QrCodeUtil.getQrCodeImage(response.getQrCode(), 400)).build();
}
/**
* APP支付
*
* @param payParam 支付参数
* @return 结果
* @throws AlipayApiException 支付宝调用异常
*/
public OrderResult callApp(PayParam payParam) throws AlipayApiException {
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
if (payParam.getAddition() != null) {
try {
model.setPassbackParams(URLEncoder.encode(payParam.getAddition(), StandardCharsets.UTF_8.name()));
} catch (UnsupportedEncodingException e) {
log.error("加密参数出现异常:{} exception: ", payParam.getAddition(), e);
}
}
model.setBody(payParam.getBody());
model.setSubject(payParam.getSubject());
model.setOutTradeNo(payParam.getOutTradeNo());
model.setTimeoutExpress(this.timeoutExpress(payParam.getTimeoutExpress()));
model.setTotalAmount(new BigDecimal(payParam.getTotalAmount()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
request.setNotifyUrl(payParam.getNotifyUrl());
request.setReturnUrl(payParam.getRedirectUrl());
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = this.getClient().sdkExecute(request);
return OrderResult.builder().body(response.getBody()).build();
}
/**
* H5支付
*
* @param payParam 支付参数
* @return 结果
* @throws AlipayApiException 支付宝调用异常
*/
public OrderResult callH5(PayParam payParam) throws AlipayApiException {
AlipayTradeWapPayModel wapPayModel = new AlipayTradeWapPayModel();
if (payParam.getAddition() != null) {
try {
wapPayModel.setPassbackParams(URLEncoder.encode(payParam.getAddition(), StandardCharsets.UTF_8.name()));
} catch (UnsupportedEncodingException e) {
log.error("加密参数出现异常:{} exception: ", payParam.getAddition(), e);
}
}
wapPayModel.setOutTradeNo(payParam.getOutTradeNo());
wapPayModel.setSubject(payParam.getSubject());
wapPayModel.setTotalAmount(new BigDecimal(payParam.getTotalAmount()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
wapPayModel.setBody(payParam.getBody());
wapPayModel.setTimeoutExpress(this.timeoutExpress(payParam.getTimeoutExpress()));
wapPayModel.setProductCode("QUICK_WAP_WAY");
if (null != payParam.getQuitUrl()) {
wapPayModel.setQuitUrl(payParam.getQuitUrl());
}
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setBizModel(wapPayModel);
request.setNotifyUrl(payParam.getNotifyUrl());
request.setReturnUrl(payParam.getRedirectUrl());
AlipayTradeWapPayResponse response = this.getClient().pageExecute(request, "GET");
return OrderResult.builder().body(response.getBody()).build();
}
/**
* PC支付
*
* @param payParam 支付参数
* @return 结果
* @throws AlipayApiException 支付宝调用异常
*/
public OrderResult callPc(PayParam payParam) throws AlipayApiException {
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
if (payParam.getAddition() != null) {
try {
model.setPassbackParams(URLEncoder.encode(payParam.getAddition(), StandardCharsets.UTF_8.name()));
} catch (UnsupportedEncodingException e) {
log.error("加密参数出现异常:{} exception: ", payParam.getAddition(), e);
}
}
model.setBody(payParam.getBody());
model.setSubject(payParam.getSubject());
model.setOutTradeNo(payParam.getOutTradeNo());
model.setTimeoutExpress(this.timeoutExpress(payParam.getTimeoutExpress()));
model.setTotalAmount(new BigDecimal(payParam.getTotalAmount()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
model.setProductCode("FAST_INSTANT_TRADE_PAY");
request.setBizModel(model);
request.setNotifyUrl(payParam.getNotifyUrl());
request.setReturnUrl(payParam.getRedirectUrl());
AlipayTradePagePayResponse response = this.getClient().pageExecute(request);
return OrderResult.builder().body(response.getBody()).build();
}
/**
* 订单查询
*
* @param outTradeNo 订单号
* @return 订单信息
* @throws AlipayApiException 支付宝调用异常
*/
public AlipayTradeQueryResponse query(String outTradeNo) throws AlipayApiException {
AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(outTradeNo);
queryRequest.setBizModel(model);
AlipayTradeQueryResponse response = this.getClient().certificateExecute(queryRequest);
return response;
}
/**
* 退款
*
* @param refundParam 退款参数
* @return 退款响应
* @throws AlipayApiException 支付宝调用异常
*/
public AlipayTradeRefundResponse refund(RefundParam refundParam) throws AlipayApiException {
AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.setOutTradeNo(refundParam.getOutTradeNo());
model.setOutRequestNo(refundParam.getOutRefundNo());
model.setRefundReason(refundParam.getReason());
model.setRefundAmount(new BigDecimal(refundParam.getRefund()).divide(ONE_HUNDRED, 2, BigDecimal.ROUND_DOWN).toPlainString());
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
request.setBizModel(model);
request.setNotifyUrl(refundParam.getNotifyUrl());
AlipayTradeRefundResponse response = this.getClient().certificateExecute(request);
return response;
}
/**
* 退款查询
*
* @param refundQueryParam 退款查询参数
* @return 退款单信息
* @throws AlipayApiException 支付宝调用异常
*/
public AlipayTradeFastpayRefundQueryResponse queryRefund(RefundQueryParam refundQueryParam) throws AlipayApiException {
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
model.setOutTradeNo(refundQueryParam.getOutTradeNo());
model.setOutRequestNo(refundQueryParam.getOutRefundNo());
request.setBizModel(model);
AlipayTradeFastpayRefundQueryResponse response = this.getClient().certificateExecute(request);
return response;
}
/**
* 支付结果通知处理
*
* @param request 请求
* @return 处理成功时返回数据信息,否则返回空
*/
public Map<String, String> payNotify(HttpServletRequest request) {
Map<String, String> body = this.getRequestBody(request);
boolean flag = this.verifySign(body);
log.info("验签结果为:{}", flag);
if (!flag) {
return null;
}
return body;
}
/**
* 支付宝通知响应
*
* @param success 是否处理成功
* @return 响应字符串
*/
public String response(boolean success) {
return success ? "success" : "fail";
}
private boolean verifySign(Map<String, String> notifyParam) {
AliPayConfig config = this.configService.getConfig();
String signType = notifyParam.get("sign_type");
boolean flag = false;
try {
flag = AlipaySignature.rsaCheckV1(notifyParam, config.getPublicKey(), StandardCharsets.UTF_8.name(), signType);
} catch (AlipayApiException e) {
log.error("验证签名出现异常:{} {}", e, notifyParam);
}
return flag;
}
private Map<String, String> getRequestBody(HttpServletRequest request) {
Map<String, String> params = new HashMap<>(10);
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
String[] values = requestParams.get(name);
StringBuilder stringBuilder = new StringBuilder();
for (String val : values) {
stringBuilder.append(val).append(",");
}
params.put(name, stringBuilder.substring(0, stringBuilder.length() - 1));
}
return params;
}
private AlipayClient getClient() {
if (client == null) {
client = buildClient();
if (client == null) {
throw new IllegalArgumentException("参数错误");
}
}
return client;
}
private AlipayClient buildClient() {
AliPayConfig config = configService.getConfig();
if (config == null) {
return null;
}
// 构造client
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
// 设置网关地址
certAlipayRequest.setServerUrl(SERVER_URL);
// 设置应用Id
certAlipayRequest.setAppId(config.getAppId());
// 设置应用私钥
certAlipayRequest.setPrivateKey(config.getPrivateKey());
// 设置请求格式,固定值json
certAlipayRequest.setFormat(FORMAT);
// 设置字符集
certAlipayRequest.setCharset(StandardCharsets.UTF_8.name());
// 设置签名类型
certAlipayRequest.setSignType(SIGN_TYPE);
// 设置应用公钥证书路径
certAlipayRequest.setCertContent(config.getAppCertContent());
// 设置支付宝公钥证书路径
certAlipayRequest.setAlipayPublicCertContent(config.getAliPayCertContent());
// 设置支付宝根证书路径
certAlipayRequest.setRootCertContent(config.getAliPayRootCertContent());
// 构造client
try {
return new DefaultAlipayClient(certAlipayRequest);
} catch (AlipayApiException e) {
log.error("创建AliPayClient异常:{}", e);
return null;
}
}
private String timeoutExpress(Integer timeoutExpress) {
if (timeoutExpress == null) {
return "15m";
}
return String.format("%dm", timeoutExpress);
}
}
到此即可使用,上述中二维码和H5支付均已投入使用,APP和PC(未签约)支付尚未验证是否能唤起。希望对大家有帮助。
网友评论