使用支付宝沙箱完成商品下单
一:效果展示:
二:代码实现
1:准备工作:
申请支付宝沙箱账户:
登录 - 支付宝
然后要下载密钥密钥工具来生成密钥;
2:流程分析:
先是用户下单,然后我们接收到用户传入的商品id和用户id,然后我们去创建数据库中的订单,再去调用支付宝的接口,创建支付宝订单,创建完成后,用户付款完成,支付宝会回调,我们在回掉时修改订单的状态就行;然后还要有两个定时任务,一个去检查订单有没有超时的,超时的自动关闭,一个去检查有没有订单已经支付了,但是我们没有接收到支付宝的回调,我们在修改状态
3:代码实现
创建订单:
private final OrderDao orderDao;
private final ProductRPC productRPC;
private final AlipayClient alipayClient;
private final EventBus eventBus;
@Value("${alipay.return_url}")
private String returnUrl;
@Value("${alipay.notify_url}")
private String notifyUrl;@Override
//创建订单
public PayOrderRes createOrder(ShopCartReq shopCartReq) throws AlipayApiException {//现根据用户id和商品去查询有没有调单的情况;PayOrder payOrderReq = PayOrder.builder().userId(shopCartReq.getUserId()).productId(shopCartReq.getProductId()).build();PayOrder unPayOrder = orderDao.queryUnPayOrder(payOrderReq);//如果查询存在但是是等待支付的状态我们直接去返回支付的地址,跳转支付if (unPayOrder != null && Constants.OrderStatusEnum.PAY_WAIT.equals(unPayOrder.getStatus())) {log.info("查询到未支付商品,userId,{},productId,{},orderId,{}", unPayOrder.getUserId(),unPayOrder.getProductId(), unPayOrder.getOrderId());return PayOrderRes.builder().orderId(unPayOrder.getOrderId()).payUrl(unPayOrder.getPayUrl()).build();//如果订单存在而且是已创建的状态说明订单创建了但是支付宝订单没有创建,这种情况要重新调用支付宝的接口创建订单} else if (unPayOrder != null && Constants.OrderStatusEnum.CREATE.equals(unPayOrder.getStatus())) {// 调用支付宝沙盒创建支付单PayOrder payOrder = doPayOrder(unPayOrder.getProductId(), unPayOrder.getProductName(), unPayOrder.getOrderId(), unPayOrder.getTotalAmount());log.info("重新创建拉起订单成功,{}", JSON.toJSONString(payOrder));return PayOrderRes.builder().payUrl(payOrder.getPayUrl()).orderId(payOrder.getOrderId()).build();}//查询商品,创建订单;ProductVO productVO = productRPC.queryByProductId(shopCartReq.getProductId());String id = RandomStringUtils.randomNumeric(16);orderDao.insert(PayOrder.builder().orderId(id).orderTime(new Date()).userId(shopCartReq.getUserId()).status(Constants.OrderStatusEnum.CREATE.getCode()).productId(shopCartReq.getProductId()).productName(productVO.getProductName()).totalAmount(productVO.getPrice()).build());//创建支付宝订单PayOrder payOrder = doPayOrder(productVO.getProductId(), productVO.getProductName(), id, productVO.getPrice());return PayOrderRes.builder().orderId(id).payUrl(payOrder.getPayUrl()).build();
}
创建支付宝订单的方法:
public PayOrder doPayOrder(String productId, String productName, String orderId, BigDecimal totalMount) throws AlipayApiException {AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();//设置回调urlrequest.setNotifyUrl(notifyUrl);request.setReturnUrl(returnUrl);JSONObject bizContent = new JSONObject();bizContent.put("out_trade_no", orderId);bizContent.put("total_amount", totalMount.toString());bizContent.put("subject", productName);bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");request.setBizContent(bizContent.toString());String form = alipayClient.pageExecute(request).getBody();//封装一个订单对象调用数据库方法改变订单的状态,从创建到等待付款PayOrder payOrder = new PayOrder();payOrder.setOrderId(orderId);payOrder.setPayUrl(form);payOrder.setStatus(Constants.OrderStatusEnum.PAY_WAIT.getCode());orderDao.updateOrderStatusUrl(payOrder);return payOrder;
}
然后就是创建订单的controller接口:
@PostMapping("create_pay_order")
public Response<String> createOrder(@RequestBody ShopCartReq shopCartReq) {try {PayOrderRes order = orderService.createOrder(shopCartReq);log.info("创建订单成功order,{}", JSON.toJSONString(order));return Response.<String>builder().code(Constants.ResponseCode.SUCCESS.getCode()).info(Constants.ResponseCode.SUCCESS.getInfo()).data(order.getPayUrl()).build();} catch (Exception e) {log.error("创建订单失败,userId,{},e,{}", shopCartReq.getUserId(), e);return Response.<String>builder().code(Constants.ResponseCode.UN_ERROR.getCode()).info(Constants.ResponseCode.UN_ERROR.getInfo()).build();}
}
设置回调地址:
notify_url: http://wfw-awei.nat300.top/api/v1/alipay/alipay_notify_url
return_url: https://gaga.plus
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
然后写回调的controller方法:
@PostMapping("alipay_notify_url")
public String checkOrderSucess(HttpServletRequest request) {try {log.info("接收到支付回调,支付状态:{}", request.getParameter("trade_status"));//判断回调的支付状态,如果是已支付我们再进行下一步操作if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {Map<String, String> params = new HashMap<>();Map<String, String[]> parameterMap = request.getParameterMap();for (String name : parameterMap.keySet()) {params.put(name, request.getParameter(name));}String tradeNo = params.get("out_trade_no");String payment = params.get("gmt_payment");String alipayTradeNo = params.get("trade_no");String sign = params.get("sign");String content = AlipaySignature.getSignCheckContentV1(params);log.info("验签开始");//验签boolean check = AlipaySignature.rsa256CheckContent(content, sign, alipayPublicKey, "UTF-8");log.info("验签结果:{}",check);if (check){log.info("支付回调,交易名称,{}",params.get("subject"));log.info("支付回调,交易状态,{}",params.get("trade_status"));log.info("支付回调,支付宝交易凭证号,{}",alipayTradeNo);log.info("支付回调,商户订单号,{}",tradeNo);log.info("支付回调,校验金额,{}",params.get("total_amount"));log.info("支付回调,买家在支付宝的唯一id,{}",params.get("buyer_id"));log.info("支付回调,买家付款时间,{}",payment);log.info("支付回调,买家付款金额,{}",params.get("buyer_pay_amount"));log.info("支付回调,更新订单,{}",tradeNo);//验签成功将订单的状态更改orderService.checkOrderPaySucess(tradeNo);}}return "sucess";} catch (Exception e) {log.error("创建订单异常{}",e);return "false";}
}
更改订单状态的service方法:
@Override
public void checkOrderPaySucess(String tradeNo) {log.info("修改订单,{}",tradeNo);PayOrder payOrder = new PayOrder();payOrder.setOrderId(tradeNo);payOrder.setStatus(Constants.OrderStatusEnum.PAY_SUCCESS.getCode());orderDao.updateOrderStatus(payOrder);eventBus.post(JSONObject.toJSONString(payOrder));
}
这个evenbus相当于一个消息队列,但是是本地的;
设置eventbus:
创建一个接收者:
@Component
@Slf4j
public class OrderSuccessLisener {@Subscribepublic void handleEvent(String successMessage){log.info("支付操作完成,可以发货,返利,等");}
}
将接收者注册:
@Bean
public EventBus eventBus(OrderSuccessLisener orderSuccessLisener){EventBus eventBus = new EventBus();eventBus.register(orderSuccessLisener);return eventBus;
}
然后上面的就是发送消息的方法;post,接收者加上 @Subscribe表示订阅
还有两个任务:
1:修改超时订单:
@Scheduled(cron = "0 0/10 * * * ?")//每10分钟执行一次方法public void exec() {try {//查询所有超时的订单List<String> orderIds = orderService.queryTimeOutOrder();//没有直接返回if (orderIds.isEmpty() || orderIds == null) {log.info("定时任务,无超时订单");return;}//将所有超时订单的状态改为closefor (String orderId : orderIds) {orderService.checkOrderPayClose(orderId);log.info("定时订单,超时订单关闭,orderid:{}", orderId);}} catch (Exception e) {log.error("定时任务,超时关闭失败{}",e);}
2:查找支付成功未回调的,数据库状态未更改的订单
@Scheduled(cron = "0/3 * * * * ?")
//每3秒执行一次
public void exec() throws AlipayApiException {try {//查询下单超过1分种,且还是等待支付状态的订单List<String> orderIds = orderService.queryNoPayNotifyOrder();//如果没有直接返回if (orderIds.isEmpty() || orderIds == null) {log.info("无未支付成功订单");return;}//遍历每一个订单,查看支付宝订单状态,如果是付款成功我们就更改数据库状态(可能是回调出现问题)for (String orderId : orderIds) {AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();AlipayTradeQueryModel alipayTradeQueryModel = new AlipayTradeQueryModel();alipayTradeQueryModel.setOutTradeNo(orderId);request.setBizModel(alipayTradeQueryModel);String code = alipayClient.execute(request).getCode();//支付成功,更改状态if (code.equals("10000")){orderService.checkOrderPaySucess(orderId);}}} catch (AlipayApiException e) {log.info("检测未接收到或未正确处理的支付回调通知失败");}
}
e = alipayClient.execute(request).getCode();//支付成功,更改状态if (code.equals("10000")){orderService.checkOrderPaySucess(orderId);}}} catch (AlipayApiException e) {log.info("检测未接收到或未正确处理的支付回调通知失败");}
}