使用支付宝沙箱完成商品下单

使用支付宝沙箱完成商品下单

一:效果展示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二:代码实现

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("检测未接收到或未正确处理的支付回调通知失败");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/470305.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux设置socks代理

公司里绝大多数主机已经禁止外网访问&#xff0c;仅保留一台主机设置socks作为代理服务器。如下为对socks这一概念的学习整理 什么是socks 是OSI模型下会话层的协议&#xff0c;位于表示层与传输层之间&#xff0c;作用是&#xff1a; exchanges network packets between a c…

以往运维岗本人面试真题分享

以下是本人面试运维岗的一些面试经历&#xff0c;在此做个记录分享 目录 TCP/IP三次握手 IPtables IPtables四表五链都是什么&#xff1f; nat端口如何做&#xff1f; 开放本机的80端口该如何做&#xff1f; 如何在单用户模式下引导Centos&#xff1f; nginx轮询模式都有…

【Hadoop实训】Hive 数据操作①

目录 一、准备文件 1、创建表 2、 数据映射 二、HIVE的数据操作 1、基本查询 a、全表查询 b、选择特定字段查询 c、查询员工表总人数 d、查询员工表总工资额 e、查询5条员工表的信息 2、Where条件查询 a、查询工资等于5000的所有员工 b、查询工资在500到1000的员工信息 …

3.5【数据库系统】ER图

2、实体之间的关系 下面主要针对两个实体间的关系进行介绍 &#xff08;a&#xff09;一对一联系&#xff08;1:1&#xff09;如班级和班长&#xff0c;一个班级只有一个班长&#xff0c;一个班长只能在一个班级任职。 &#xff08;b&#xff09;一对多联系&#xff08;1&#…

笔记 | image may have poor performance,or fail,if run via emulation

在Docker Desktop中现象如图&#xff1a; 当你运行 AMD64 平台代码时&#xff08;Intel 和 AMD 芯&#xff09;&#xff0c;你的 Mac 必须模拟其CPU架构&#xff08;因为你自身是ARM&#xff09;。这通常会非常吃性能。 Docker Desktop 警告你在模拟 Intel/AMD x64 CPU 时性能可…

想租用显卡训练自己的网络?AutoDL保姆级使用教程(PyCharm版)

各位小伙伴们大家好~ 不知道各位同学在科研过程中是否有这样的苦恼 电脑无显卡。难不成我要用CPU跑实验吗&#xff1f;救救我吧电脑显卡算力太低。训练过程慢慢慢慢慢&#xff0c;等半天都出不来结果电脑显卡显存不够&#xff0c;batchsize稍微高一点点&#xff0c;就要爆显存…

Linux相关习题-gcc-gdb-冯诺依曼

1.将一个test.c文件仅仅进行汇编而不生成可执行程序的命令是&#xff1f; A.gcc -S test.c B.gcc -E test.c C.gcc -c test.c D.gcc test.c gcc常见选项&#xff1a; -c 汇编完成后停止&#xff0c;不进行链接 -E 预处理完成后停止&#xff0c;不进行编译 -S 编译完成后停止…

计算机毕业设计必看必学35755flask旅游景区热度可视化平台原创定制程序,java、PHP、python、小程序、文案全套、毕设成品等

flask旅游景点热度可视化平台 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对旅游景点热度…

Hadoop(环境搭建篇)

这里我用的是ubnatu22.4的系统&#xff0c;请大家严格按照这个系统来安装 一、网络设置 1、打开虚拟机的编辑&#xff0c;并选择虚拟网络编辑器 2、点击更改设置 3、更改IP 二、更改主机名 1、打开终端 2、输入以下命令 hostnamectl set-hostname master 3、然后关闭终端在…

Java 堆内存管理详解:`-Xms` 和 `-Xmx` 参数的使用与默认内存设置

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

Linux探秘坊-------1.系统核心的低语:基础指令的奥秘解析(1)

1.Linux的背景介绍 Linux 操作系统的发展历程充满了激情与创新喵&#xff5e;&#x1f380; 萌芽期 (1983 - 1991)&#xff1a;Linux 的历史可追溯到 1983 年&#xff0c;理查德斯托曼 (Richard Stallman) 发起 GNU 计划&#xff0c;目标是创建一个自由软件操作系统。1987 年发…

AI写作(二)NLP:开启自然语言处理的奇妙之旅(2/10)

一、NLP 的基本概念与任务 &#xff08;一&#xff09;自然语言处理的研究对象 自然语言处理&#xff08;NLP&#xff09;处于计算机科学、人工智能和语言学的交叉领域。它所聚焦的人类社会语言信息是无比丰富和复杂的&#xff0c;包括口语、书面语等各种形式。这种语言信息在…

使用CubeMX一键配置Freertos

一、配置参数 1.1 API信息 1.2 版本信息 版本信息 FreeRTOS版本为10.3.1 CMSIS-RTOS 版本为2.00 如果我们不用CubeMX配置的话 还是推荐移植正点原子的&#xff0c;因为它的裁剪头文件比较清晰 就是那个conf的头文件&#xff0c;一键配置的话很方便。可能会跟原版移植的Freert…

如何提高自动驾驶中惯性和卫星组合导航pbox的精度?

Mems纯惯导里程推算精度做到千分之一&#xff0c;两分钟航向精度保持0.001弧度&#xff0c;是如何做到的&#xff1f; 【飞迪sigma车规高精度组合导航系统在3.6km长隧道下穿测试&#xff0c;135s纯惯导航向保持精度小于0.06度&#xff0c;隧道内转弯轨迹和直线航位推算重合#智能…

10款PDF翻译工具的探索之旅:我的使用经历与工具特色!!

在如今的时代&#xff0c;PDF文件已经成为我们工作、学习和生活中不可或缺的一部分。但是&#xff0c;当遇到一些非母语或陌生语言的PDF文档时&#xff0c;这要怎么办呀&#xff01;这时候翻译工具就显得尤为重要了。这也是我所遇到过的难题&#xff0c;现在我将与大家分享几款…

MySQL_第13章_视图

1. 常见的数据库对象 2. 视图概述 2.1 为什么使用视图&#xff1f; 视图一方面可以使用表的一部分而不是所有的表&#xff0c;另一方面也可以针对不同的用户制定不同的查询视图。 2.2 视图的理解 视图是一种虚拟表&#xff0c;本身是不具有数据的&#xff0c;占用很少的内存…

【测试框架篇】单元测试框架pytest(1):环境安装和配置

一、pytest简介 Pytest是Python的一种单元测试框架&#xff0c;与Python自带的unittest测试框架类似&#xff0c;但是比 unittest框架使用起来更简洁&#xff0c;效率更高。 二、pytest特点 Pytest是一个非常成熟的Python测试框架,主要特点有以下几点&#xff1a; 非常容易…

Camera Tuning中AE/AWB/AF基础知识介绍

3A定义 3A是Camera ISP控制算法的一个重要组成部分&#xff0c;通常分为自动曝光&#xff08;AE&#xff09;、自动聚焦&#xff08;AF&#xff09;、自动白平衡&#xff08;AWB&#xff09;三个组件。 自动曝光&#xff08;Auto Exposure&#xff09; AE基本概念 曝光概念…

group_concat配置影响程序出bug

在 ThinkPHP 5 中&#xff0c;想要临时修改 MySQL 数据库的 group_concat_max_len 参数&#xff0c;可以使用 原生 SQL 执行 来修改该值。你可以通过 Db 类来执行 SQL 语句&#xff0c;从而修改会话&#xff08;Session&#xff09;级别的变量。 步骤 设置 group_concat_max_l…

linux 下查看程序启动的目录

以azkaban为例 第一步、ps -ef | grep azkaban 查询出进程号 第二步、cd /proc/ 第三步 、cd 进程号 第四部 ll 查看详情 查看jar 位置 查看jar 启动命令