购物车demo全代码-对接支付宝沙箱环境

创建项目

vue create alipay-demo

AlipayDemo.vue

<template><div class="cart-container"><h2>商品列表</h2><table class="product-table"><tr><th>商品</th><th>价格</th><th>商品描述</th><th>操作</th></tr><tr v-for="item in products" :key="item.name"><td>{{ item.name }}</td><td>{{ item.price }}元</td><td>{{ item.description }}</td><td><button @click="addToCart(item)">添加到购物车</button></td></tr><tr v-if="products.length === 0"><td colspan="4">没有任何商品</td></tr></table><div class="cart"><h2>未支付商品</h2><table class="cart-table"><tr><th>商品</th><th>数量</th><th>价格</th><th>小计</th><th>操作</th></tr><tr v-for="(item, index) in unpaidProducts" :key="index"><td>{{ item.name }}</td><td><button @click="updateQuantity(index, -1)" :disabled="item.quantity <= 1">-</button>{{ item.quantity }}<button @click="updateQuantity(index, 1)">+</button></td><td>{{ item.price }}元</td><td>{{ item.price * item.quantity }}元</td><td><button @click="removeFromCart(index)">删除</button></td></tr><tr v-if="unpaidProducts.length === 0"><td colspan="5">没有任何商品</td></tr><tr v-if="unpaidProducts.length > 0"><td colspan="4">总价</td><td>{{ calculateTotal() }}元</td></tr></table><button @click="openAlipay">支付</button></div><div class="cart"><h2>已支付商品</h2><table class="cart-table"><tr><th>商品</th><th>数量</th><th>价格</th><th>小计</th><th>总金额</th><th>支付时间</th><th>支付订单号</th><th>操作</th></tr><tr v-for="(order, index) in paidOrders" :key="index"><td><div v-for="(item, idx) in order.items" :key="idx">{{ item.name }}<br></div></td><td><div v-for="(item, idx) in order.items" :key="idx">{{ item.quantity }}</div></td><td><div v-for="(item, idx) in order.items" :key="idx">{{ item.price }}元<br></div></td><td><div v-for="(item, idx) in order.items" :key="idx">{{ item.price * item.quantity }}元<br></div></td><td>{{ order.totalAmount }}元</td><td>{{ order.paymentTime }}</td><td>{{ order.orderNumber }}</td><td><button @click="removePaidOrder(index)">删除订单</button></td></tr><tr v-if="paidOrders.length === 0"><td colspan="8">没有任何已支付商品</td></tr></table></div></div>
</template><script>import axios from 'axios';export default {name: 'AlipayDemo',data() {return {products: [{ name: '图书', price: 30, description: '经典好书' },{ name: '衣服', price: 300, description: '时尚外套' },{ name: '零食', price: 20, description: '美味零食' },{ name: '鞋子', price: 500, description: '舒适运动鞋' },],paidProducts: [],unpaidProducts: JSON.parse(localStorage.getItem('unpaidProducts')) || [],paidOrders: JSON.parse(localStorage.getItem('paidOrders')) || [],};},watch: {unpaidProducts: {handler(newVal) {localStorage.setItem('unpaidProducts', JSON.stringify(newVal));},deep: true},paidOrders: {handler(newVal) {localStorage.setItem('paidOrders', JSON.stringify(newVal));},deep: true}},methods: {addToCart(product) {let found = this.unpaidProducts.find(p => p.name === product.name);if (found) {found.quantity++;} else {this.unpaidProducts.push({ ...product, quantity: 1 });}},updateQuantity(index, value) {const item = this.unpaidProducts[index];if (value === -1 && item.quantity > 1) {item.quantity--;} else if (value === 1) {item.quantity++;}},removeFromCart(index) {this.unpaidProducts.splice(index, 1);},calculateTotal() {return this.unpaidProducts.reduce((acc, product) => acc + product.price * product.quantity, 0);},openAlipay() {if (this.unpaidProducts.length === 0) {alert('购物车为空,无法支付!');return;}const total = this.calculateTotal(); // 假设这个方法返回商品总价if (confirm(`商品总价为:${total}元。是否确认支付?`)) {// 生成订单号const orderNumber = this.generateOrderNumber(); // 假设这个方法生成订单号axios.get('/api/alipay/pagepay', {params: {total_amount: total,order_num: orderNumber // 将订单号作为参数发送给后端},headers: {'Content-Type': 'application/json'}}).then(response => {// 创建一个新的窗口并打开const newWindow = window.open('', '_blank');newWindow.document.write(response.data);// 设置一个定时器,用于在60秒后关闭窗口,以防回调失败setTimeout(() => {if (newWindow && !newWindow.closed) {newWindow.close();}}, 60000); // 60秒超时关闭页面// 每6秒查询一次支付状态,最多查询10次let queryCount = 0; // 查询次数计数器const maxQueries = 10; // 最大查询次数const intervalId = setInterval(() => {queryCount++; // 每次查询时递增计数器if (queryCount > maxQueries) {clearInterval(intervalId); // 超过最大查询次数,停止查询alert('支付已取消');return;}axios.get('/api/alipay/query', {params: {orderNumber: orderNumber // 使用订单号查询支付状态}}).then(response => {console.log(response.data);if (response.data === 'paid') {clearInterval(intervalId); // 停止查询newWindow.close(); // 关闭支付页面// 处理支付成功的逻辑const paymentTime = this.getCurrentTime(); // 假设这个方法获取当前时间const paidOrder = {items: [...this.unpaidProducts],totalAmount: total,paymentTime,orderNumber,};this.paidOrders.push(paidOrder); // 将订单添加到已支付列表this.unpaidProducts = []; // 清空未支付商品列表alert('支付成功'); // 提示支付成功}}).catch(error => {console.error('Error:', error);});}, 6000);}).catch(error => {console.error('Error:', error);// 处理错误});}
},getCurrentTime() {const now = new Date();return now.getFullYear() + '-' + (now.getMonth() + 1).toString().padStart(2, '0') + '-' + now.getDate().toString().padStart(2, '0') + ' ' + now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');},generateOrderNumber() {return Math.floor(Math.random() * 100000) + 100000;},removePaidOrder(index) {this.paidOrders.splice(index, 1);},}
}
</script><style scoped>
body {font-family: Arial, sans-serif;background-color: #f4f4f4;margin: 0;padding: 20px;
}.cart-container {max-width: 800px;margin: auto;background: #fff;padding: 20px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}h2 {text-align: center;color: #333;
}.product-table, .cart-table {width: 100%;border-collapse: collapse;margin-bottom: 20px;
}.product-table, .cart-table, th, td {border: 1px solid #d3e2f2;
}th, td {padding: 10px;text-align: left;
}th {background-color: #e0f7fa;color: #333;
}button {background-color: #81d4fa;color: white;border: none;padding: 10px 20px;cursor: pointer;margin: 5px;border-radius: 4px;
}button:hover {background-color: #2bb8ff;
}.cart-table tr:nth-child(even) {background-color: #e3f2fd;
}.cart-table tr:hover {background-color: #cfe8fb;
}.cart-table th {background-color: #b3e0ef;color: #333;
}
</style>

配置跨域vue.config.js

module.exports = {devServer: {proxy: {'/api': {target: 'http://localhost:8080',changeOrigin: true,pathRewrite: {'^/api': '' // 去掉请求路径中的/api前缀}}}}
};

后端接口

package com.example.alipayback.alipay.controller;import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.example.alipayback.alipay.config.AlipayConfig;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.*;@RestController
@RequestMapping("/alipay")
public class AlipayController {// 处理GET请求,映射到 "/alipay/hello" 路径@GetMapping("/test")public String hello() {return "Hello Alipay!";}@GetMapping("/pagepay")public String openPayment(@RequestParam(name = "total_amount", required = true) String totalAmount,@RequestParam(name = "order_num", required = true) String orderNum) {try {String out_trade_no = orderNum;String subject = "商品服务";String body = "";// 获得初始化的AlipayClientAlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl,AlipayConfig.app_id,AlipayConfig.merchant_private_key,AlipayConfig.format,AlipayConfig.charset,AlipayConfig.alipay_public_key,AlipayConfig.sign_type);// 设置请求参数AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();alipayRequest.setReturnUrl(AlipayConfig.return_url);alipayRequest.setNotifyUrl(AlipayConfig.notify_url);alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","+ "\"total_amount\":\"" + totalAmount + "\","+ "\"subject\":\"" + subject + "\","+ "\"body\":\"" + body + "\","+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");// 请求String result = alipayClient.pageExecute(alipayRequest).getBody();// 这里可以将result输出到浏览器,或者重定向到支付宝支付页面// 例如:return "<html>...</html>"; 或者使用Response对象进行重定向return result;} catch (AlipayApiException e) {throw new RuntimeException("阿里云请求支付页面失败", e);}}@RequestMapping("/notify")public String notifyUrl(HttpServletRequest request) {try {System.out.println("异步通知接口被支付宝服务器调用了~~~~~~~~~~~~~~~~~~~~~~~~");//获取支付宝POST过来反馈信息Map<String, String> params = new HashMap<String, String>();Map<String, String[]> requestParams = request.getParameterMap();for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {String name = (String) iter.next();String[] values = (String[]) requestParams.get(name);String valueStr = "";for (int i = 0; i < values.length; i++) {valueStr = (i == values.length - 1) ? valueStr + values[i]: valueStr + values[i] + ",";}params.put(name, valueStr);}//二次验证签名的方法 //调用SDK验证签名boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, "utf-8", "RSA2");System.out.println("signVerified:" + signVerified);//——请在这里编写您的程序(以下代码仅作参考)——/* 实际验证过程建议商户务必添加以下校验:1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)4、验证app_id是否为该商户本身。*/if (signVerified) {//验证成功//商户订单号String out_trade_no = request.getParameter("out_trade_no");//支付宝交易 流水号 2024072422001489450503509425String trade_no = request.getParameter("trade_no");//交易状态String trade_status = request.getParameter("trade_status");//交易金额String total_amount = request.getParameter("total_amount");if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {return "success";}}} catch (AlipayApiException e) {e.printStackTrace();}return "fail";}public String generateOrderId() {long timestamp = System.currentTimeMillis();int random = new Random().nextInt(1000);String out_trade_no = "ORDER" + timestamp + "-" + random;return out_trade_no;}// 查询支付状态接口@GetMapping("/query")public ResponseEntity<String> checkPaymentStatus(@RequestParam String orderNumber) {// 初始化SDK// 获得初始化的AlipayClientAlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl,AlipayConfig.app_id,AlipayConfig.merchant_private_key,AlipayConfig.format,AlipayConfig.charset,AlipayConfig.alipay_public_key,AlipayConfig.sign_type);// 构造请求参数以调用接口AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();AlipayTradeQueryModel model = new AlipayTradeQueryModel();// 设置订单支付时传入的商户订单号model.setOutTradeNo(orderNumber);// 设置查询选项List<String> queryOptions = new ArrayList<>();queryOptions.add("trade_settle_info");model.setQueryOptions(queryOptions);request.setBizModel(model);// 调用支付宝接口查询订单状态String result = null;try {AlipayTradeQueryResponse response = alipayClient.execute(request);if (response == null || !response.isSuccess()) {return ResponseEntity.status(500).body("Failed to get response from Alipay");}// 检查交易状态String tradeStatus = response.getTradeStatus();if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {return ResponseEntity.ok("paid");} else {return ResponseEntity.status(404).body("Payment is not completed");}} catch (Exception e) {e.printStackTrace();return ResponseEntity.status(500).body("Error during payment status check");}}}

部署到容器中

docker run -d   --name nginx -p 80:80  -v /root/nginx/html:/usr/share/nginx/html   -v /root/nginx/nginx.conf:/etc/nginx/nginx.conf nginx

效果在这里插入图片描述
在沙箱环境支付后
在这里插入图片描述

将未支付商品列表添加到已支付商品列表中
在这里插入图片描述

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

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

相关文章

【CANOE】【学习】【DecodeString】字节转为中文字符输出

系列文章目录 文章目录 系列文章目录前言一、DecodeString 转为中文字节输出二、代码举例1.代码Demo2.DecodeString 函数说明函数语法&#xff1a;参数说明&#xff1a;返回值&#xff1a;使用示例&#xff1a;示例代码&#xff1a; 说明&#xff1a; 前言 有时候使用的时候&a…

超全超详细使用SAM进行高效图像分割标注(GPU加速推理)

一、前言 &#x1f449; 在计算机视觉任务中&#xff0c;图像分割 是重要的基础工作&#xff0c;但人工标注往往耗时耗力。Meta推出的 SAM&#xff08;Segment Anything Model&#xff09;&#xff0c;大幅提升了分割效率和精度&#xff0c;让标注工作更加轻松。本篇博客将详细…

JavaEE 重要的API阅读

JavaEE API阅读 目的是为了应对学校考试&#xff0c;主要关注的是类的继承关系、抛出错误的类型、包名、包结构等等知识。此帖用于记录。 PageContext抽象类 包名及继承关系 继承自JspContext类。PageContext 实例提供对与某个 JSP 页⾯关联的所有名称空间的访问&#xff0…

【Python · PyTorch】卷积神经网络(基础概念)

【Python PyTorch】卷积神经网络 CNN&#xff08;基础概念&#xff09; 0. 生物学相似性1. 概念1.1 定义1.2 优势1.2.1 权重共享1.2.2 局部连接1.2.3 层次结构 1.3 结构1.4 数据预处理1.4.1 标签编码① One-Hot编码 / 独热编码② Word Embedding / 词嵌入 1.4.2 归一化① Min-…

Python爬虫----python爬虫基础

一、python爬虫基础-爬虫简介 1、现实生活中实际爬虫有哪些&#xff1f; 2、什么是网络爬虫&#xff1f; 3、什么是通用爬虫和聚焦爬虫&#xff1f; 4、为什么要用python写爬虫程序 5、环境和工具 二、python爬虫基础-http协议和chrome抓包工具 1、什么是http和https协议…

Python学习笔记(2)正则表达式

正则表达式是一个特殊的字符序列&#xff0c;它能帮助你方便的检查一个字符串是否与某种模式匹配。 在 Python 中&#xff0c;使用 re 模块提供的函数来处理正则表达式&#xff0c;允许你在字符串中进行模式匹配、搜索和替换操作。 1 正则表达式 正则表达式(Regular Expressi…

整数唯一分解定理

整数唯一分解定理&#xff0c;也称为算术基本定理&#xff0c;是由德国数学家高斯在其著作《算术研究》中首次提出的。本文回顾整数唯一分解定理以及对应的几个重要结论。 一、整数唯一分解定理 整数唯一分解定理&#xff0c;也称为算术基本定理&#xff0c;是数论中的一个重…

小版本大不同 | Navicat 17 新增 TiDB 功能

近日&#xff0c;Navicat 17 迎来了小版本更新。此次版本新增了对 PingCap 公司的 TiDB 开源分布式关系型数据库的支持&#xff0c;进一步拓展了 Navicat 的兼容边界。即日起&#xff0c;Navicat 17 所有用户可免费升级至最新版本&#xff0c;通过 Navicat 工具实现 TiDB 数据库…

【珠海科技学院主办,暨南大学协办 | IEEE出版 | EI检索稳定 】2024年健康大数据与智能医疗国际会议(ICHIH 2024)

#IEEE出版|EI稳定检索#主讲嘉宾阵容强大&#xff01;多位外籍专家出席报告 2024健康大数据与智能医疗国际会议&#xff08;ICHIH 2024&#xff09;2024 International Conference on Health Big Data and Intelligent Healthcare 会议简介 2024健康大数据与智能医疗国际会议…

ADS项目笔记 1. 低噪声放大器LNA天线一体化设计

在传统射频结构的设计中&#xff0c;天线模块和有源电路部分相互分离&#xff0c;两者之间通过 50 Ω 传输线级联&#xff0c;这种设计需要在有源电路和天线之间建立无源网络&#xff0c;包括天线模块的输入匹配网络以及有源电路的匹配网络。这些无源网络不仅增加了系统的插入损…

客厅打苍蝇fly测试总结1116

项目介绍:本项目是关系食品安全重大项目&#xff0c;针对屋子里有苍蝇的问题&#xff0c;通过分析苍蝇特性及对场景分类&#xff0c;设计测试用例16条&#xff0c;有效击杀苍蝇17头&#xff0c;房间里面已经看不到苍蝇的活动痕迹。比较传统蚊拍击打容易在物体表面形成难看且赃的…

物理hack

声明 声明 文章只是方便各位师傅学习知识&#xff0c;以下网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 ✍&#x1f3fb;作者简介&#xff1a;致力于网络安全领域&#xff0c;目前作为一名学习者&#xff0c;很荣…

go 集成swagger 在线接口文档

安装swaggo go install github.com/swaggo/swag/cmd/swaglatest 编写swag import ("github.com/gin-gonic/gin""goWeb/internal/service""goWeb/model/response" )// UserRouter 路由 func UserRouter(ctx *gin.RouterGroup) {ctx.GET("/…

学习threejs,使用第一视角控制器FirstPersonControls控制相机

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️第一视角控制器FirstPerson…

基于Java Web 的家乡特色菜推荐系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

HMI FUXA测试

Foxa官网 foxa tutorialhttps://github.com/frangoteam/FUXA/wiki/Getting-Started 1 概述 FUXA是基于Web的&#xff0c;过程&#xff08;SCADA、HMI、看板等&#xff09;可视化软件。可创建现代的过程可视化&#xff0c;使用独立的设计器&#xff0c;显示机器和实时数据。 …

数据分析-Excel基础操作

目录 周报讲解 基础概念 理解数据 筛选excel表 数据透视表 插入数据透视表 新建字段 切片器&#xff08;筛选&#xff09; 数据透视图 Excel常用函数 sum&#xff08;求和&#xff09; 1-8月GMV 1月和8月GMV sumif&#xff08;条件求和&#xff09; sumifs 日G…

git创建远程仓库,以gitee码云为例GitHub同理

git远程Remote服务端仓库构建的视频教程在这 Git建立服务端Remote远程仓库&#xff0c;gitee码云例&#xff0c;Github_哔哩哔哩_bilibili 1、登gitee码云/Github 登录 - Gitee.com https://github.com/ &#xff08;没账号的注册一下就行&#xff09; 点击如下图位置的创…

【Android、IOS、Flutter、鸿蒙、ReactNative 】启动页

Android 设置启动页 自定义 splash.xml 通过themes.xml配置启动页背景图 IOS 设置启动页 LaunchScreen.storyboard 设置为启动页 storyboard页面绘制 Assets.xcassets 目录下导入图片 AppLogo Flutter 设置启动页 Flutter Android 设置启动页 自定义 launch_background.xm…

深入理解Flutter生命周期函数之StatefulWidget(一)

目录 前言 1.为什么需要生命周期函数 2.开发过程中常用的生命周期函数 1.initState() 2.didChangeDependencies() 3.build() 4.didUpdateWidget() 5.setState() 6.deactivate() 7.dispose() 3.Flutter生命周期总结 1.调用顺序 2.函数调用时机以及主要作用 4.生…