SpringBoot整合支付宝沙箱支付
环境说明:SpringBoot3.0.2
支付宝沙箱地址:沙箱地址
获取配置信息
因支付需要回调地址,回调地址必须是公网,如果有公网的话,那直接在下面配置文件填写自己的公网,没有的话,就需要我们借助第三方工具来进行回调。具体操作请看这篇博客
引入依赖
<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.22.110.ALL</version>
</dependency>
配置文件
yz:alipay:gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.doappId: 你的appidprivateKey: 你的私钥format: jsonpublicKey: 你的公钥charset: UTF-8signType: RSA2returnUrl: http://xxxxxx.natappfree.cc/api/pay/finish # 回调成功后,调用的地址notifyUrl: http://xxxxxx.natappfree.cc/api/pay/notify # 回调地址expireTime: 15mreturn:url: http://localhost:8000/account/center # 这个就写你支付成功跳转的页面
配置类
@Data
@Configuration
@ConfigurationProperties(prefix = "yz.alipay")
public class AliPayProperties {private String gatewayUrl;private String appId;private String privateKey;private String format;private String publicKey;private String charset;private String signType;private String notifyUrl;private String returnUrl;private String expireTime;
}
@Data
@Configuration
@ConfigurationProperties(prefix = "yz.return")
public class ReturnProperties {private String url;
}
订单信息
@Data
public class PayOrderRequest {private static final long serialVersionUID = 3191241716373120793L;@Schema(description = "订单号")private Long id;@Schema(description = "用户id")private Long uid;@Schema(description = "主题")private String subject;@Schema(description = "充值金额")private Long amount;@Schema(description = "实际金额")private Float realAmount;@Schema(description = "优惠率")private Float discountRate;
}
支付的回调信息,封装成类,
@Data
public class AlipayNotifyDto {private String gmt_create;private String charset;private String gmt_payment;private String notify_time;private String subject;private String sign;private String buyer_id;private String invoice_amount;private String version;private String notify_id;private String fund_bill_list;private String notify_type;private String out_trade_no;private String total_amount;private String trade_status;private String trade_no;private String auth_app_id;private String receipt_amount;private String point_amount;private String buyer_pay_amount;private String app_id;private String sign_type;private String seller_id;
}
Facade接口
@Tag(name = "支付接口")
public interface PayFacade {@Operation(summary = "支付")@Parameters(value = {@Parameter(name = "uid", description = "用户id", in = ParameterIn.HEADER)})@PostMapping("/pay/")BaseResponse<String> reCharge(@RequestHeader Long uid, @RequestBody PayOrderRequest payOrderRequest);@Operation(summary = "支付宝异步通知,支付宝支付后,会回调该接口")@PostMapping("/pay/notify")String notify(AlipayNotifyDto alipayNotifyDto);@Operation(summary = "同步跳转,告诉你是否调用成功,不能拿来判断支付成功")@GetMapping("/pay/finish")void finish(HttpServletResponse response);
}
Controller类
@Slf4j
@RestController
@RequiredArgsConstructor
public class PayController implements PayFacade {private final PayService payService;@Overridepublic BaseResponse<String> reCharge(Long uid, PayOrderRequest payOrderRequest) {if(payOrderRequest == null || uid == null){throw new RuntimeException("参数不能为空");}String res = payService.reCharge(uid, payOrderRequest);return ResultUtils.success("",res);}public String notify(AlipayNotifyDto alipayNotifyDto) {if(alipayNotifyDto == null){throw new RuntimeException("参数不能为空");}String res = payService.notifyUrl(alipayNotifyDto);return res;}public void finish(HttpServletResponse response) {try{response.sendRedirect(returnProperties.getUrl());} catch (Exception e){log.error("【同步跳转,告诉你是否调用成功,不能拿来判断支付成功】",e);}}
}
Service类
@Slf4j
@Service
@RequiredArgsConstructor
public class PayServiceImpl extends ServiceImpl<PayMapper, PayFlow> implements PayService {private final AliPayProperties aliPayProperties;private final PayMessageProducer payMessageProducer;private final UserMessageProducer userMessageProducer;private final PromotionMessageProducer promotionMessageProducer;private final OrderMessageProducer orderMessageProducer;// 携带订单信息获取支付url@Overridepublic String reCharge(Long uid, PayOrderRequest payOrderRequest) {try{log.info("payOrderRequest支付信息:{}", payOrderRequest);//保存流水---交给mq处理payMessageProducer.sendMessage(JSON.toJSONString(payOrderRequest));// 交易String path = sendPayment(payOrderRequest);return path;}catch (Exception e){log.info("支付异常", e);throw new RuntimeException("系统出错");}}// 处理回调逻辑@Overridepublic String notifyUrl(AlipayNotifyDto alipayNotifyDto) {log.info("setTradeStatus: {}", alipayNotifyDto.getTrade_status());log.info("回调了");String payStatus = "fail";boolean signVerified = false;Map<String, String> mp = JSON.parseObject(JSON.toJSONString(alipayNotifyDto), Map.class);try {signVerified = AlipaySignature.rsaCheckV1(mp, aliPayProperties.getPublicKey(), aliPayProperties.getCharset(), aliPayProperties.getSignType());log.info("【异步通知签名验证】" + signVerified);}catch (AlipayApiException e){System.out.println("【异步签名异常】" + e.getErrMsg());return payStatus;}if(signVerified && "TRADE_SUCCESS".equals(alipayNotifyDto.getTrade_status())){// 更新流水---这块应该也交给mq处理,不想写了,就这样吧updatePayFlowSuccess(alipayNotifyDto.getOut_trade_no());// 更新订单---交给mq处理orderMessageProducer.sendMessage(alipayNotifyDto.getOut_trade_no());// 消费优惠卷---交给mq处理promotionMessageProducer.sendMessage(alipayNotifyDto.getOut_trade_no());// 用户金币新增---交给mq处理userMessageProducer.sendMessage(alipayNotifyDto.getOut_trade_no());payStatus = "success";}log.info("【异步通知签名验证】" + payStatus);return payStatus;}@Overridepublic void returnUrl(HttpServletRequest request) throws UnsupportedEncodingException {}// 更新支付流水private void updatePayFlowSuccess(String outTradeNo){QueryWrapper<PayFlow> queryWrapper = new QueryWrapper<>();queryWrapper.eq("out_trade_no", Long.parseLong(outTradeNo));PayFlow payFlow = PayFlow.builder().build();payFlow.setTradeStatus(PayConstant.TRADE_SUCCESS);payFlow.setPaySuccess(true);boolean update = this.update(payFlow, queryWrapper);if(!update){log.info("更新流水失败");throw new RuntimeException("更新流水失败");}}// 支付配置private AlipayConfig getAlipayConfig() {AlipayConfig alipayConfig = new AlipayConfig();alipayConfig.setServerUrl(aliPayProperties.getGatewayUrl());alipayConfig.setAlipayPublicKey(aliPayProperties.getPublicKey());alipayConfig.setPrivateKey(aliPayProperties.getPrivateKey());alipayConfig.setAppId(aliPayProperties.getAppId());alipayConfig.setFormat(aliPayProperties.getFormat());alipayConfig.setCharset(aliPayProperties.getCharset());alipayConfig.setSignType(aliPayProperties.getSignType());return alipayConfig;}// 返回支付urlprivate String sendPayment(PayOrderRequest payOrderRequest){try {AlipayClient alipayClient = new DefaultAlipayClient(this.getAlipayConfig());AlipayTradePagePayRequest request = this.getAlipayTradePagePayRequest(payOrderRequest);request.setReturnUrl(aliPayProperties.getReturnUrl());request.setNotifyUrl(aliPayProperties.getNotifyUrl());// 调用SDK生成表单AlipayTradePagePayResponse alipayTradePagePayResponse = alipayClient.pageExecute(request, "GET");String body = alipayTradePagePayResponse.getBody();return body;} catch (Exception e) {e.printStackTrace();}return null;}private AlipayTradePagePayRequest getAlipayTradePagePayRequest(PayOrderRequest payOrderRequest) {AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();//异步接收地址,仅支持http/https,公网可访问request.setNotifyUrl("");//同步跳转地址,仅支持http/httpsrequest.setReturnUrl("");/******必传参数******/JSONObject bizContent = new JSONObject();//商户订单号,商家自定义,保持唯一性bizContent.put("out_trade_no", payOrderRequest.getId());//支付金额,最小值0.01元bizContent.put("total_amount", payOrderRequest.getRealAmount());//订单标题,不可使用特殊符号bizContent.put("subject", payOrderRequest.getSubject());//电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAYbizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");bizContent.put("timeout_express", aliPayProperties.getExpireTime());request.setBizContent(bizContent.toString());return request;}
}