支付宝支付
阅官方文档 https://opendocs.alipay.com/open/02e7gq
个人支付案例git地址[微信支付/支付宝支付/华为支付/苹果支付/小米支付]:https://gitee.com/wazk2008/demo-pay
前置:引入支付宝官方的jar
<!-- alipay sdk -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.13.33.ALL</version>
</dependency>
yml配置
alipay:
app_id: 自己的appid
private_key: 商户应用私钥
app_cert_path: 商户应用公钥地址
alipay_cert_path: 支付宝公钥地址
alipay_root_cert_path: 支付宝根证书地址
notify_url: www.baidu.com
return_url: www.baidu.com
alipay_gateway_url: https://openapi.alipaydev.com/gateway.do
charset: UTF-8
format: json
sign_type: RSA2
配置类
@Component
@Slf4j
@Data
public class AlipayConfig {
@Value("${alipay.app_id}")
private String appId;
// 商户应用私钥
@Value("${alipay.private_key}")
private String appPrivateKey;
// 商户应用公钥证书地址
@Value("${alipay.app_cert_path}")
private String appPublicCertPath;
// 支付宝公钥证书地址
@Value("${alipay.alipay_cert_path}")
private String alipayPublicCertPath;
// 支付宝根证书地址
@Value("${alipay.alipay_root_cert_path}")
private String alipayRootCertPath;
@Value("${alipay.notify_url}")
private String notifyUrl;
@Value("${alipay.return_url}")
private String returnUrl;
@Value("${alipay.alipay_gateway_url}")
private String alipayGatewayUrl;
@Value("${alipay.charset}")
private String charset;
@Value("${alipay.format}")
private String format;
@Value("${alipay.sign_type}")
private String signType;
/**
* 设置支付宝客户端
* @return
*/
@Bean
public AlipayClient getAlipayClient() {
//构造client
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
//设置网关地址
certAlipayRequest.setServerUrl(alipayGatewayUrl);
//设置应用Id
certAlipayRequest.setAppId(appId);
//设置应用私钥
certAlipayRequest.setPrivateKey(appPrivateKey);
//设置请求格式,固定值json
certAlipayRequest.setFormat(format);
//设置字符集
certAlipayRequest.setCharset(charset);
//设置签名类型
certAlipayRequest.setSignType(signType);
//设置应用公钥证书路径
certAlipayRequest.setCertPath(appPublicCertPath);
//设置支付宝公钥证书路径
certAlipayRequest.setAlipayPublicCertPath(alipayPublicCertPath);
//设置支付宝根证书路径
certAlipayRequest.setRootCertPath(alipayRootCertPath);
//构造client
try {
return new DefaultAlipayClient(certAlipayRequest);
} catch (AlipayApiException e) {
log.error("构建AlipayClient对象失败");
e.printStackTrace();
return null;
}
}
}
1.下单支付(跳转支付宝app进行支付)
接口名称: 统一下单
接口地址: https://opendocs.alipay.com/open/02e7gq?scene=20
个人支付代码demo
@RestController
@RequestMapping(value = "/pay")
@Slf4j
public class PayController {
@Autowired
private AliPayService aliPayService;
@PostMapping(value = "/ali")
public Result<String> payAli (@RequestBody PayAliRequest payAliRequest) {
try {
log.info("支付宝支付接口开始执行,请求参数:{}", JSON.toJSONString(payAliRequest));
String payAliResponse = aliPayService.pay(payAliRequest);
log.info("支付宝付接口执行成功,请求参数:{},响应参数:{}", JSON.toJSONString(payAliRequest), JSON.toJSON(payAliResponse));
return Result.getSuccess(payAliResponse);
} catch (ResponseException e) {
log.error("支付宝支付接口执行失败1,请求参数:{},异常原因:{}", JSON.toJSONString(payAliRequest), e.getMessage());
e.printStackTrace();
return Result.getFail(e);
} catch (Exception e) {
log.error("支付宝支付接口执行失败2,请求参数:{},异常原因:{}", JSON.toJSONString(payAliRequest), e.getMessage());
e.printStackTrace();
return Result.getFail(ResultCode.INTERNAL_SERVER_ERROR);
}
}
}
@Service
@Slf4j
public class AliPayService {
@Autowired
private AlipayConfig alipayConfig;
@Autowired
private AlipayClient alipayClient;
// 支付宝支付,获取支付提交form表单字符串
public String pay (PayAliRequest payAliRequest) {
// 这里可以写各种校验
// todo 校验该比订单的支付金额是否合法
// todo 校验该笔订单的状态是否为待支付
AlipayRequest alipayRequest = getAlipayRequest(payAliRequest);
AlipayResponse aliPayResponse = null;
try {
// 支付宝返回数据是一个form表单
aliPayResponse = alipayClient.pageExecute(alipayRequest);
// 支付宝返回数据是一个url地址
// aliPayResponse = alipayClient.pageExecute(alipayRequest, "GET");
} catch (AlipayApiException e) {
log.error("请求支付宝进行支付操作失败,支付宝支付请求参数为:{},请求支付宝的参数为:{}", JSON.toJSONString(payAliRequest), JSON.toJSONString(alipayRequest));
e.printStackTrace();
throw new ResponseException(ResultCode.ALI_PAY_ERROR);
}
String alipayResponseBody = aliPayResponse.getBody();
log.info("请求支付宝进行支付操作成功,支付宝支付请求参数为:{},请求支付宝的参数为:{},支付宝的响应参数为:{}", JSON.toJSONString(payAliRequest), JSON.toJSONString(alipayRequest), alipayResponseBody);
if (!StringUtils.isEmpty(alipayResponseBody)) {
// todo 这里可以保存用户支付宝支付的唤醒记录
return alipayResponseBody;
} else {
throw new ResponseException(ResultCode.ALI_PAY_ERROR);
}
}
// 获取请求支付宝支付的参数
public AlipayRequest getAlipayRequest (PayAliRequest payAliRequest) {
boolean isApp = payAliRequest.getIsApp() > 0;
if (isApp) {
return getAlipayAppRequest(payAliRequest);
} else {
return getAlipayWapRequest(payAliRequest);
}
}
// 获取app的支付宝支付请求参数
private AlipayRequest getAlipayAppRequest (PayAliRequest payAliRequest) {
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject(payAliRequest.getSubject());
model.setBody(payAliRequest.getSubject());
model.setOutTradeNo(payAliRequest.getOrderNo());
model.setTimeoutExpress("30m");
model.setTotalAmount(String.valueOf(CalcUtil.div(payAliRequest.getFee(), 100)));
model.setProductCode("QUICK_MSECURITY_PAY"); // 固定值
// 花呗分期相关的参数
String hbFqNum = payAliRequest.getHbFqNum();
if (isHbFq(hbFqNum)) {
ExtendParams hbModel = new ExtendParams();
hbModel.setHbFqNum(hbFqNum); // 花呗分期量 3 6 12
hbModel.setHbFqSellerPercent(String.valueOf(100)); // 分期手续费,0表示用户承担,100表示商户承担
model.setExtendParams(hbModel);
}
request.setBizModel(model);
request.setNotifyUrl(alipayConfig.getNotifyUrl());
request.setReturnUrl(alipayConfig.getReturnUrl());
return request;
}
// 获取H5的支付宝支付请求参数
private AlipayRequest getAlipayWapRequest (PayAliRequest payAliRequest) {
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setBody(payAliRequest.getSubject());
model.setSubject(payAliRequest.getSubject());
model.setOutTradeNo(payAliRequest.getOrderNo());
model.setTimeoutExpress("30m");
model.setTotalAmount(String.valueOf(CalcUtil.div(payAliRequest.getFee(), 100)));
model.setProductCode("QUICK_WAP_WAY"); // 固定值
// 花呗分期相关的参数
String hbFqNum = payAliRequest.getHbFqNum();
if (isHbFq(hbFqNum)) {
ExtendParams hbModel = new ExtendParams();
hbModel.setHbFqNum(hbFqNum); // 花呗分期量 3 6 12
hbModel.setHbFqSellerPercent(String.valueOf(100)); // 分期手续费,0表示用户承担,100表示商户承担
model.setExtendParams(hbModel);
}
request.setBizModel(model);
request.setNotifyUrl(alipayConfig.getNotifyUrl());
request.setReturnUrl(alipayConfig.getReturnUrl());
return request;
}
// 判断是否花呗分期
private boolean isHbFq (String hbFqNum) {
return "3".equals(hbFqNum) || "6".equals(hbFqNum) || "12".equals(hbFqNum);
}
}
2.支付回调
@RestController
@RequestMapping(value = "/pay")
@Slf4j
public class PayController {
@Autowired
private AliPayService aliPayService;
@PostMapping(value = "/aliPayCallback")
public Result<Object> payAliCallback (@RequestBody PayAliCallbackRequest payAliCallbackRequest) {
try {
log.info("支付宝支付回调接口开始执行,请求参数:{}", JSON.toJSON(payAliCallbackRequest));
aliPayService.payCallback(payAliCallbackRequest);
log.info("支付宝支付回调接口执行成功,请求参数:{}", JSON.toJSON(payAliCallbackRequest));
return Result.getSuccess();
} catch (ResponseException e) {
log.error("支付宝支付回调接口执行失败1,请求参数:{},异常原因:{}", JSON.toJSON(payAliCallbackRequest), e.getMessage());
e.printStackTrace();
return Result.getFail(e.getCode(), e.getMessage());
} catch (Exception e) {
log.error("支付宝支付回调接口执行失败2,请求参数:{},异常原因:{}", JSON.toJSON(payAliCallbackRequest), e.getMessage());
e.printStackTrace();
return Result.getFail(ResultCode.INTERNAL_SERVER_ERROR);
}
}
}
@Service
@Slf4j
public class AliPayService {
@Autowired
private AlipayConfig alipayConfig;
@Autowired
private AlipayClient alipayClient;
// 支付宝支付回调
public void payCallback (PayAliCallbackRequest payAliCallbackRequest) throws IllegalAccessException, AlipayApiException, ParseException {
// 将 alipayCreateOrderCallbackRequest 转为 map格式
Map<String, String> params = convertPayAliCallbackRequestToMap(payAliCallbackRequest);
// 获取支付宝传入的参数并进行校验
boolean validation = validateRequestParam(params);
if (validation) {
// todo 校验订单是否存在
String outTradeNo = payAliCallbackRequest.getOut_trade_no();
// todo 校验订单是否已支付
// todo 校验支付金额
Double buyerAmount = Double.valueOf(payAliCallbackRequest.getTotal_amount());
// todo 修改支付和订单的状态
// 支付宝的支付交易流水号
String transactionId = payAliCallbackRequest.getTrade_no();
// 用户支付时间
Date payTime = DateUtils.parseDate(payAliCallbackRequest.getGmt_payment(), "yyyy-MM-dd HH:mm:ss");
// 用户是否使用了蚂蚁花呗付款
boolean isUsedAlipayHb = isAliPayHb(payAliCallbackRequest.getFund_bill_list());
// todo 修改订单和支付信息
} else {
log.error("支付宝支付回调,签名校验失败,支付回调请求参数:{}", JSON.toJSONString(payAliCallbackRequest));
throw new ResponseException(ResultCode.ALI_PAY_SIGN_ERROR);
}
}
private Map<String, String> convertPayAliCallbackRequestToMap (PayAliCallbackRequest payAliCallbackRequest) throws IllegalAccessException {
Map<String, String> map = new HashMap<>();
Class<? extends PayAliCallbackRequest> clazz = payAliCallbackRequest.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String key = field.getName();
Object val = field.get(payAliCallbackRequest);
if (!Objects.isNull(val)) {
map.put(key, String.valueOf(val));
}
}
return map;
}
private Boolean validateRequestParam(Map<String, String> params) throws AlipayApiException {
// 签名验证(对支付宝返回的数据验证,确定是支付宝返回的)
return AlipaySignature.rsaCertCheckV1(params, alipayConfig.getAlipayPublicCertPath(), alipayConfig.getCharset(), alipayConfig.getSignType());
}
// 获取是否支付宝花呗支付
private boolean isAliPayHb (String fundBillList) {
if (StringUtils.isEmpty(fundBillList)) {
return false;
}
JSONArray jsonArray = JSON.parseArray(fundBillList);
for (Object obj : jsonArray) {
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(obj));
String fundChannel = jsonObject.getString("fundChannel");
if ("PCREDIT".equals(fundChannel)) {
return true;
}
}
return false;
}
}
3.支付退款
@RestController
@RequestMapping(value = "/pay")
@Slf4j
public class RefundController {
@Autowired
private RefundService refundService;
@PostMapping(value = "/orderRefund")
public Result<Object> orderRefund (@RequestBody OrderRefundRequest orderRefundRequest) {
try {
log.info("订单退款接口开始执行,请求参数:{}", JSON.toJSONString(orderRefundRequest));
refundService.orderRefund(orderRefundRequest);
log.info("订单退款接口执行完成,请求参数:{}", JSON.toJSONString(orderRefundRequest));
return Result.getSuccess();
} catch (ResponseException e) {
log.error("订单退款接口执行失败1,请求参数:{},异常原因:{}", JSON.toJSONString(orderRefundRequest), e.getMessage());
e.printStackTrace();
return Result.getFail(e.getCode(), e.getMessage());
} catch (Exception e) {
log.error("订单退款接口执行失败2,请求参数:{},异常原因:{}", JSON.toJSONString(orderRefundRequest), e.getMessage());
e.printStackTrace();
return Result.getFail(ResultCode.INTERNAL_SERVER_ERROR);
}
}
}
@Service
@Slf4j
public class RefundService {
@Autowired
private WechatService wechatService;
// 订单退款
public void orderRefund (OrderRefundRequest orderRefundRequest) {
String orderNo = orderRefundRequest.getOrderNo();
Integer curRefundFee = orderRefundRequest.getFee();
// 获取该订单的支付详情
OrderPayInfo payInfo = getOrderPayInfo(orderNo);
// 获取该订单的支付金额
int orderPaidFee = getOrderPaidFee(orderNo);
// 获取该订单的已退金额
int orderRefundedFee = getOrderRefundedFee(orderNo);
if (curRefundFee+orderRefundedFee > orderPaidFee) {
// 订单可退余额不足
log.info("订单可退余额不足,退款请求参数:{}", JSON.toJSONString(orderRefundRequest));
throw new ResponseException(ResultCode.REFUND_FEE_NOT_ENOUGH_ERROR);
}
// 获取该订单的支付方式
PayTypeEnum payTypeEnum = getOrderPayTypeEnum(orderNo);
switch (payTypeEnum) {
case WECHAT_PAY:
wechatService.refund(payInfo, curRefundFee);
break;
case ALI_PAY:
break;
case HUAWEI_PAY:
break;
case APPLE_PAY:
break;
case MI_PAY:
break;
default:
break;
}
}
// 获取订单的支付详情
private OrderPayInfo getOrderPayInfo (String orderNo) {
return new OrderPayInfo();
}
// 获取订单的支付金额
private int getOrderPaidFee (String orderNo) {
return 0;
}
// 获取订单的已退金额
private int getOrderRefundedFee (String orderNo) {
return 0;
}
// 获取订单的支付方式
private PayTypeEnum getOrderPayTypeEnum (String orderNo) {
return PayTypeEnum.WECHAT_PAY;
}
}
@Service
@Slf4j
public class AliPayService {
@Autowired
private AlipayConfig alipayConfig;
@Autowired
private AlipayClient alipayClient;
// 支付宝退款
public void refund (OrderPayInfo payInfo, Integer refundFee) {
// 从订单支付详payInfo中获取支付时微信的transaction_id
String payTransactionId = "";
// 生成退款时,请求微信的 out_refund_no
String outRefundNo = "";
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("trade_no", payTransactionId);
bizContent.put("refund_amount", CalcUtil.div(refundFee, 100));
bizContent.put("out_request_no", outRefundNo);
request.setBizContent(bizContent.toString());
try {
AlipayTradeRefundResponse response = alipayClient.certificateExecute(request);
if (response.isSuccess()) {
// todo 退款申请成功,记录退款数据到db
} else {
log.error("支付宝退款失败");
throw new ResponseException(ResultCode.ALI_PAY_REFUND_ERROR);
}
} catch (Exception e) {
log.error("支付宝退款请求接口失败");
throw new ResponseException(ResultCode.ALI_PAY_REFUND_ERROR);
}
}
}