Uniapp Android SpringBoot3 对接支付宝支付(最新教程附源码)
Uniapp Android SpringBoot3 对接支付宝支付(最新教程附源码)
- 1、效果展示
- 2、后端实现
- 2.1 引入支付宝SDK依赖 pom.xml
- 2.2 配置 application.yml
- 2.3 支付宝相关代码
- 2.3.1 AlipayConfig.java
- 2.3.2 ZfbPayConfig.java
- 2.3.3 支付接口
- 2.3.4 支付回调处理接口(用于支付成功后,支付宝通知我们支付信息)
- 2.3.4 退款接口
- 3 前端代码
- 3.1 支付接口调用
- 3.2 支付方法
- 3.2 退款接口调用
1、效果展示
效果展示
2、后端实现
2.1 引入支付宝SDK依赖 pom.xml
<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.40.0.ALL</version>
</dependency>
2.2 配置 application.yml


真实环境需要开通 APP 支付, 如果是沙箱环境的话不支持退款。
# 支付宝支付
alipay:server_url: https://openapi.alipay.com/gateway.doapp_id: # 你的 appIdprivate_key: # 你的私钥format: jsonsellerId: 2088722047235165charset: utf-8alipay_public_key: # 你的公钥sign_type: RSA2notifyUrl: http://wtw867.natappfree.cc/app/shop/order/zfb-pay/notify # 支付成功通知地址
2.3 支付宝相关代码
2.3.1 AlipayConfig.java
package com.zhong.config;import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Getter
@Setter
@ToString
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig extends com.alipay.api.AlipayConfig {private String serverUrl;private String appId;private String privateKey;private String format;private String charset;private String alipayPublicKey;private String signType;private String notifyUrl;
}
2.3.2 ZfbPayConfig.java
package com.zhong.config;import com.alipay.api.AlipayApiException;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.zhong.model.entity.goods.GoodsOrderInfo;
import com.zhong.service.GoodsOrderInfoService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;@Configuration
@Component
@Data
@Slf4j
public class ZfbPayConfig {@AutowiredAlipayConfig alipayConfig;@Autowiredprivate GoodsOrderInfoService goodsOrderInfoService;private DefaultAlipayClient client() throws AlipayApiException {return new DefaultAlipayClient(alipayConfig);}public String pay(GoodsOrderInfo order) {String source = "";try {DefaultAlipayClient client = client();AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();model.setSubject("云尚社区");model.setOutTradeNo(order.getOrderNo());model.setTotalAmount(String.valueOf(0.01));// alipay 封装的接口调用AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();request.setBizModel(model);request.setNotifyUrl(alipayConfig.getNotifyUrl());AlipayTradeAppPayResponse response = client.sdkExecute(request);source = response.getBody();} catch (AlipayApiException e) {goodsOrderInfoService.noPay(order.getOrderNo()); // 支付失败更新订单状态,可以根据您的具体业务做出调整log.error("支付出现问题,详情:{}", e.getErrMsg());e.printStackTrace();}log.info(source);return source;}/*** 退款** @param tradeNo* @param totalAmount* @return*/public AlipayTradeRefundResponse refund(String tradeNo, String totalAmount) {try {DefaultAlipayClient client = client();AlipayTradeRefundModel alipayTradeRefundModel = new AlipayTradeRefundModel();alipayTradeRefundModel.setTradeNo(tradeNo);alipayTradeRefundModel.setRefundAmount(totalAmount);AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();request.setBizModel(alipayTradeRefundModel);AlipayTradeRefundResponse response = client.execute(request);return response;} catch (AlipayApiException e) {log.error("退款出现问题,详情:{}", e.getErrMsg());e.printStackTrace();}return null;}}
2.3.3 支付接口
全局统一返回结果类
package com.zhong.result;import lombok.Data;/*** 全局统一返回结果类*/
@Data
public class Result<T> {//返回码private Integer code;//返回消息private String message;//返回数据private T data;public Result() {}private static <T> Result<T> build(T data) {Result<T> result = new Result<>();if (data != null)result.setData(data);return result;}public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {Result<T> result = build(body);result.setCode(resultCodeEnum.getCode());result.setMessage(resultCodeEnum.getMessage());return result;}public static <T> Result<T> fail(Integer code, String message) {Result<T> result = build(null);result.setCode(code);result.setMessage(message);return result;}public static <T> Result<T> ok(T data) {return build(data, ResultCodeEnum.SUCCESS);}public static <T> Result<T> ok() {return Result.ok(null);}public static <T> Result<T> fail() {return build(null, ResultCodeEnum.FAIL);}
}
支付接口
我这里是多订单的,如果是但订单的话这里 payUrl = zfbPayConfig.pay(goodsOrderInfoArrayList.get(0)); 可以传入一个 orderInfo 方便zfbPayConfig.pay() 获取 tradeNo 和 totalAmount。
@Operation(summary = "支付商品订单")@GetMapping(value = "/pay/zfb")@ResponseBodypublic Result pay(@RequestParam String id) throws AlipayApiException {List<String> split = List.of(id.split(","));String payUrl = "";// 单个商品的情况if (split.size() == 1) {GoodsOrderInfo orderInfo = service.getById(id);orderInfo.setTotalPrice((orderInfo.getTotalPrice()));payUrl = zfbPayConfig.pay(orderInfo);orderInfo.setCodeUrl(payUrl);orderInfo.setOrderIds(id);service.saveOrUpdate(orderInfo);}// 多个商品else {ArrayList<GoodsOrderInfo> goodsOrderInfoArrayList = new ArrayList<>();BigDecimal price = new BigDecimal("0.00");for (String shopInfoId : split) {GoodsOrderInfo orderInfo = service.getById(shopInfoId);goodsOrderInfoArrayList.add(orderInfo);price = price.add(orderInfo.getPrice().multiply(BigDecimal.valueOf(orderInfo.getGoodNum())));}System.out.println(price);goodsOrderInfoArrayList.get(0).setTotalPrice(price);payUrl = zfbPayConfig.pay(goodsOrderInfoArrayList.get(0));goodsOrderInfoArrayList.get(0).setCodeUrl(payUrl);goodsOrderInfoArrayList.get(0).setOrderIds(id);service.saveOrUpdate(goodsOrderInfoArrayList.get(0));}return Result.ok(payUrl);}
2.3.4 支付回调处理接口(用于支付成功后,支付宝通知我们支付信息)
@PostMapping("/zfb-pay/notify") // 注意这里必须是POST接口public String payNotify(HttpServletRequest request) {Map<String, String> params = new HashMap<>();Map<String, String[]> requestParams = request.getParameterMap();System.out.println("=========支付宝异步回调========");System.out.println(JSON.toJSONString(requestParams));for (String name : requestParams.keySet()) {String[] values = 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);}try {boolean flag = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType());if (flag) {System.out.println("支付回调信息:" + params);// TODO 验签成功System.out.println("支付成功,异步验签成功!");// 验证订单是否为当前订单String orderNumber = params.get("out_trade_no");LambdaQueryWrapper<GoodsOrderInfo> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(GoodsOrderInfo::getOrderNo, orderNumber);
// .eq(ShopOrderInfo::getIsDeleted, 0)GoodsOrderInfo orderInfo = service.getOne(queryWrapper);if (orderInfo == null) {throw new LeaseException(ResultCodeEnum.SHOP_ORDER_NOT_FIND_ERROR);}// 验证订单金额是否正确 TODO 测试环境不验证金额都是 0.01
// BigDecimal totalPrice = new BigDecimal(params.get("total_amount"));
// BigDecimal totalPriceParam = orderInfo.getTotalPrice();
// if (!Objects.equals(totalPriceParam, totalPrice)) {
// throw new LeaseException(ResultCodeEnum.SHOP_ORDER_TOTAL_PRICE_ERROR);
// }// 验证商家ID是否一致 防止付给别人了String sellerId = config.getProperty("alipay.sellerId");String sellerIdParams = params.get("seller_id");if (!Objects.equals(sellerId, sellerIdParams)) {throw new LeaseException(ResultCodeEnum.SHOP_ORDER_BUSINESS_PID_ERROR);}// 验证APPID是否一致String appId = config.getProperty("alipay.app_id");String appIdParams = params.get("app_id");if (!Objects.equals(appIdParams, appId)) {throw new LeaseException(ResultCodeEnum.SHOP_ORDER_BUSINESS_APPID_ERROR);}// 检查交易状态String tradeStatus = params.get("trade_status");if (!"TRADE_SUCCESS".equals(tradeStatus) && !"TRADE_CLOSED".equals(tradeStatus)) {throw new LeaseException(ResultCodeEnum.SHOP_ORDER_PAY_ERROR);}// 支付成功if ("TRADE_SUCCESS".equals(tradeStatus)) {// TODO 处理订单业务 修改订单状态 记录支付日志service.processOrder(params);}// 退款操作if ("TRADE_CLOSED".equals(tradeStatus)) {String orderNo = params.get("out_biz_no");service.refund(orderNo);}return "success";} else {// TODO 支付失败标记为未付款String orderNumber = params.get("out_trade_no");service.noPay(orderNumber);return "error";}} catch (AlipayApiException e) {System.out.println("支付宝错误回调:" + e.getErrMsg());e.printStackTrace();return "error";}}
2.3.4 退款接口
ApplyRefund 退款参数
@Data
public class ApplyRefund {private String id;private String refundInfo;private String remarks;
}
退款接口
@Operation(summary = "申请退款")@PostMapping("apply/refund")public Result applyRefund(@RequestBody ApplyRefund applyRefund) {service.applyRefund(applyRefund);return Result.ok();}
3 前端代码
3.1 支付接口调用
// 支付订单
export const payShopOrderApi = (id) => {return http.get(`/app/shop/order/pay/zfb?id=${id}`)
}
3.2 支付方法
const toPayFun = async () => {// 调起支付let res = await payShopOrderApi(ids.value);console.log(res.data);uni.requestPayment({//服务提供商 通过uni.getProvider获取provider: 'alipay',//后台返回的订单数据orderInfo: res.data,//调用成功的回调success(res) {uni.showToast({title: "支付成功"})setTimeout(() => {uni.redirectTo({url: "/pages/src/user/user-order/user-order"})}, 500)},//调用失败的回调fail(err) {uni.showToast({title: "支付取消",icon: "none"})setTimeout(() => {uni.redirectTo({url: "/pages/src/user/user-order/user-order"})}, 500)}})}
3.2 退款接口调用
这里我的退款接口放在商家平台,app只负责申请退款,需要商家审核,使用的是TS代码。
// 退款
export interface PostInterfacesRefundRes {orderIds: number,tradeNo: number
}
const openDialog = async (rowData: PostInterfacesRefundRes = {} as PostInterfacesRefundRes,
) => {console.log(rowData);const params = {orderIds: rowData.orderIds,tradeNo: rowData.tradeNo}let res = await postAgreementRefundApi(params);ElMessage.success({ message: `操作成功!` })proTable.value?.getTableList()
}
相关文章:
Uniapp Android SpringBoot3 对接支付宝支付(最新教程附源码)
Uniapp Android SpringBoot3 对接支付宝支付(最新教程附源码) 1、效果展示2、后端实现2.1 引入支付宝SDK依赖 pom.xml2.2 配置 application.yml2.3 支付宝相关代码2.3.1 AlipayConfig.java2.3.2 ZfbPayConfig.java2.3.3 支付接口2.3.4 支付回调处理接口&…...
SQL DML 语句
CREATE TABLE classes (ClassID varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 班级ID,ClassName varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 班级名称,TeacherID varchar(20) CHARACTER SET utf8mb4…...
饲料颗粒机全套设备有哪些机器组成
颗粒饲料机主要用于将各种饲料原料(如玉米、豆粕、麦麸、鱼粉等)进行混合、压制,制成颗粒状的饲料。这种饲料不仅方便储存和运输,还能提高动物的采食效率和饲料利用率。同时,颗粒饲料在加工过程中能灭部分微生物和寄生…...
MySQL事务与锁
定义一个事务向d_eams数据库的student表中插入3条记录,并检验若插入相同的学号,则回滚事务,既插入无效,否则成功提交 delimiter $$ create procedure tr_proc() begindeclare continue handler for sqlstate 23000beginrollback;…...
汽车免拆案例 | 2007款宝马650i车发动机偶尔无法起动
故障现象 一辆2007款宝马650i车,搭载N62B48B发动机,累计行驶里程约为26万km。车主反映,发动机偶尔无法起动,故障频率较低,十几天出现1 次,且故障出现时起动机不工作。 故障诊断 接车后试车,…...
PostgreSQL和Oracle的sql差异
PostgreSQL和Oracle的sql差异 1.rownum (1)Oracle分页查询使用rownum,PostgreSQL使用limit offset ORACLEPOSTGRESQLselect * from (select rownum r,e.* from emp e where rownum <5) t where r>0;select * from emp limit 5 offset…...
SpringMvc完整知识点二(完结)
SpringMVC获取请求参数 环境准备工作等均省略,可详见快速入门,此处只写非共有部分代码 该部分示例项目SpringMvcThree已上传至Gitee,可自行下载 客户端请求参数的格式为:namevalue&passwordvalue... ... 服务端想要获取请求…...
29 - Java Serializable 序列化
Java的Serializable接口是Java序列化机制的核心,它允许一个对象的状态被转换为字节流,从而可以方便地进行存储或传输。 序列化后的对象可以被写到数据库、存储到文件系统,或者通过网络传输。 要在 Java 中使一个类可序列化,你需要…...
59 基于STM32的烟雾、红外、温湿度检测
所有仿真详情导航: PROTEUS专栏说明-CSDN博客 目录 一、主要功能 二、硬件资源 三、主程序编程 四、资源下载 一、主要功能 基于SMT32F103C8T6单片机,采用DHT11检测温湿度,采用光敏电阻检测光照,采用滑动变阻器分别模拟红外、烟雾,通过OLED显示屏显示,如果湿度过低…...
使用Excel 对S型曲线加减速算法进行仿真
项目场景: 项目场景:代码中写了S型加减速算法,相查看生成的加减速数组,直观的展示出来,USB通信一次64字节,对于我几个个32位的频率值不太方便,于是采用Excel进行仿真。 代码中如何生成S加减速曲…...
flink-connector-mysql-cdc:01 mysql-cdc础配置代码演示
flink-connector-mysql-cdc: 01 mysql-cdc基础配置代码演示02 mysql-cdc高级扩展03 mysql-cdc常见问题汇总04 mysql-cdc-kafka生产级代码分享05 flink-kafka-doris生产级代码分享06 flink-kafka-hudi生产级代码分享 flink-cdc版本:3.2.0 flink版本&…...
java计算机毕设课设—进销存管理系统(附源码、文章、相关截图、部署视频)
这是什么系统? 资源获取方式再最下方 java计算机毕设课设—进销存管理系统(附源码、文章、相关截图、部署视频) 一、项目简介 项目名称: 基于Java的进销存管理系统 开发背景: 在现代企业管理中,库存管理是核心环节之一&#…...
鸿蒙UI开发——渐变色效果
1、概 述 ArkTs可以通过颜色渐变接口,设置组件的背景颜色渐变效果,实现在两个或多个指定的颜色之间进行平稳的过渡。 目前提供三种渐变类型:线性渐变、角度渐变、径向渐变。 我们在鸿蒙UI布局实战 —— 个人中心页面开发中,默认…...
嵌入式硬件设计 — 智能设备背后的隐形架构大师
目录 引言 一、嵌入式硬件设计概述 (一)需求分析 (二)硬件选型 (三)电路设计 (四)PCB 制作与焊接 (五)硬件调试与测试 (六)软…...
QNX的系统资源访问机制
资料参考: QNX官网文档 在QNX中,一些系统的资源默认是无法访问的,或者可访问的范围过大,导致产生不可控的危险,此时便需要对系统资源进行访问限制 接口如下 #include <sys/rsrcdbmgr.h> #include <sys/rsrcdbmsg.h>int rsrcdbmgr_create(...
高校数字化运营平台解决方案:构建统一的服务大厅、业务平台、办公平台,助力打造智慧校园
教育数字化是建设教育强国的重要基础,利用技术和数据助推高校管理转型,从而更好地支撑教学业务开展。 近年来,国家多次发布政策,驱动教育行业的数字化转型。《“十四五”国家信息化规划》,推进信息技术、智能技术与教育…...
多模态大型语言模型MM-1.5采用数据驱动的方法,通过不断优化数据组合提高模型性能
多模态大型语言模型MM-1.5采用数据驱动的方法,通过不断优化数据组合提高模型性能 MM-1.5模型的设计核心在于其数据驱动的方法,这意味着模型的性能在很大程度上取决于所使用的数据类型和组合。这种方法的实施细节可以从以下几个方面来展开: …...
16 设计模式之适配器模式(充电器转换案例)
一、适配器模式的定义 适配器模式(Adapter Pattern)是一种结构型设计模式,常用于解决接口不兼容的问题。适配器模式通过引入一个“适配器”类,将一个接口转化为客户端期望的另一种接口,使得原本因接口不兼容而无法交互…...
基于Java Springboot在线招聘APP且微信小程序
一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 微信…...
多组学数据如何发表高分SCI论文,以RNA-Seq数据为例
随着高通量测序以及生物信息学的发展,R语言在生物大数据分析以及数据挖掘中发挥着越来越重要的作用。想要成为一名优秀的生物数据分析者与科研团队不可或缺的人才,除了掌握对生物大数据挖掘与分析技能之外,还要具备一定的统计分析能力与SCI论…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
