Uniapp Android SpringBoot3 对接支付宝支付(最新教程附源码)

news2025/1/15 22:12:31

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.do
  app_id: # 你的 appId
  private_key:  # 你的私钥
  format: json
  sellerId: 2088722047235165
  charset: utf-8
  alipay_public_key: # 你的公钥
  sign_type: RSA2
  notifyUrl: 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 {
    @Autowired
    AlipayConfig alipayConfig;
    @Autowired
    private 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")
    @ResponseBody
    public 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()
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云ECS服务器域名解析

阿里云ECS服务器域名解析&#xff0c;以前添加两条A记录类型&#xff0c;主机记录分别为www和&#xff0c;这2条记录都解析到服务器IP地址。 1.进入阿里云域名控制台&#xff0c;找到域名 ->“解析设置”->“添加记录” 2.添加一条记录类型为A,主机记录为www&#xff0c…

【MySQL】mysql服务器架构

目录 1、背景2、mysql服务器架构解释3、总结 1、背景 简单理解一下mysql的服务器架构。 2、mysql服务器架构解释 mysql的架构图如下&#xff1a; 主要分为三部分&#xff1a;客户端、服务端、存储引擎。接下来我们来解释一下各个部分&#xff1a; 客户端 用来连接mysql服务…

BERT模型的输出格式探究以及提取出BERT 模型的CLS表示,last_hidden_state[:, 0, :]用于提取每个句子的CLS向量表示

说在前面 最近使用自己的数据集对bert-base-uncased进行了二次预训练&#xff0c;只使用了MLM任务&#xff0c;发现在加载训练好的模型进行输出CLS表示用于下游任务时&#xff0c;同一个句子的输出CLS表示都不一样&#xff0c;并且控制台输出以下警告信息。说是没有这些权重。…

基于LSTM的A股股票价格预测系统(torch) :从数据获取到模型训练的完整实现

1. 项目简介 本文介绍了一个使用LSTM&#xff08;长短期记忆网络&#xff09;进行股票价格预测的完整系统。该系统使用Python实现&#xff0c;集成了数据获取、预处理、模型训练和预测等功能。 这个代码使用的是 LSTM (Long Short-Term Memory) 模型&#xff0c;这是一种特殊的…

【ArcGIS微课1000例】0134:ArcGIS Earth实现二维建筑物的三维完美显示

文章目录 一、加载数据二、三维显示三、三维符号化一、加载数据 加载配套实验数据(0134.rar中的建筑物,2d或3d都可以),方法如下:点击添加按钮。 点击【Add Files】,在弹出的Open对话框中,选择建筑物,点击确定,完成添加。 默认二维显示: 二、三维显示 右键建筑物图层…

汽车EEA架构:发展历程

1.发展历程的基本逻辑 汽车电子电气的发展历程中&#xff0c;其使用的基本逻辑是IPO(Input-Processing-Output)模型&#xff0c;如下图1所示&#xff1a; 图 1 那什么是IPO模型了&#xff1f;我们从控制器的原理入手解释IPO模型,控制器的主要用途如下&#xff1a; 根据给定的逻…

python拆分Excel文件

按Sheet拆分Excel 或 按照某一列的不同值拆分Excel。文档样式如下&#xff1a; 结果&#xff1a;红色是按照Sheet名拆出的&#xff0c;蓝色和橙色是某个Sheet按照某列的不同值拆分的。 代码&#xff1a; # -*- coding: utf-8 -*- """ 拆分excel文件——按照…

存内架构IR-DROP问题详解-电容电导补偿

一、总述 电容、电导补偿作为大规模数字电路的关键设计理念&#xff0c;是 CIM 架构优化的核心技术。在 CIM 中&#xff0c;平衡电容或电导并实现计算的精准映射&#xff0c;对能效提升和计算精度保障具有关键作用。本文基于近期文献探讨电容、电导补偿在 CIM 中的具体补偿策…

汽车网络安全 -- IDPS如何帮助OEM保证车辆全生命周期的信息安全

目录 1.强标的另一层解读 2.什么是IDPS 2.1 IDPS技术要点 2.2 车辆IDPS系统示例 3.车辆纵深防御架构 4.小结 1.强标的另一层解读 在最近发布的国家汽车安全强标《GB 44495》,在7.2节明确提出了12条关于通信安全的要求,分别涉及到车辆与车辆制造商云平台通信、车辆与车辆…

【数字化】华为企业数字化转型-认知篇

导读&#xff1a;企业数字化转型的必要性在于&#xff0c;它能够帮助企业适应数字化时代的需求&#xff0c;提升运营效率&#xff0c;创新业务模式&#xff0c;增强客户互动&#xff0c;从而在激烈的市场竞争中保持领先地位并实现可持续发展。通过学习华为企业数字化转型相关理…

用C#开发程序进行ASCII艺术制作

我一直很喜欢 ASCII 艺术&#xff0c;而我对制作 ASCII 艺术的热情促使我探索 .NET 框架中的 GDI。在本文中&#xff0c; 我将向您展示如何通过三个简单的步骤从 JPEG/Bitmap 图像生成 ASCII 艺术。 1、加载并调整图像大小。 2、读取每个像素&#xff0c;获取其颜色并将其转换…

第23周:机器学习及文献阅读

目录 摘要 Abstract 一、理论知识 1、逻辑提升 2、分类任务 3、10倍交叉验证法 二、文献阅读 1、模型方法——MLT &#xff08;1&#xff09;特征选择 &#xff08;2&#xff09;决策树剪枝 2、分类任务——逻辑回归 3、实验部分 数据集的选取 代码实践 模型…

2020年国赛高教杯数学建模E题校园供水系统智能管理解题全过程文档及程序

2020年国赛高教杯数学建模 E题 校园供水系统智能管理 原题再现 校园供水系统是校园公用设施的重要组成部分&#xff0c;学校为了保障校园供水系统的正常运行需要投入大量的人力、物力和财力。随着科学技术的发展&#xff0c;校园内已经普遍使用了智能水表&#xff0c;从而可以…

React开发高级篇 - React Hooks以及自定义Hooks实现思路

Hooks介绍 Hooks是react16.8以后新增的钩子API&#xff1b; 目的&#xff1a;增加代码的可复用性&#xff0c;逻辑性&#xff0c;弥补无状态组件没有生命周期&#xff0c;没有数据管理状态state的缺陷。 为什么要使用Hooks&#xff1f; 开发友好&#xff0c;可扩展性强&#…

摩尔线程 国产显卡 MUSA 并行编程 学习笔记-2024/12/03

Learning Roadmap&#xff1a; Section 1: Intro to Parallel Programming & MUSA Deep Learning Ecosystem&#xff08;摩尔线程 国产显卡 MUSA 并行编程 学习笔记-2024/11/30-CSDN博客&#xff09;UbuntuDriverToolkitcondapytorchtorch_musa环境安装(2024/11/24-Ubunt…

如何使用Docker轻松搭建高颜值无广告音乐播放器SPlayer随时随地听歌

前言 在快节奏的生活环境中&#xff0c;音乐成为了许多人放松和享受的重要方式。本文将介绍如何在Linux Ubuntu系统中使用Docker快速部署一款高颜值无广告的某抑云音乐播放器——SPlayer&#xff0c;并结合Cpolar内网穿透工具实现出门在外也能远程访问本地服务&#xff0c;随时…

C# Decimal

文章目录 前言1. Decimal 的基本特性2. 基本用法示例3. 特殊值与转换4. 数学运算示例5. 精度处理示例6. 比较操作示例7. 货币计算示例8. Decimal 的保留小数位数9. 处理 Decimal 的溢出和下溢10. 避免浮点数计算误差总结 前言 decimal 是 C# 中一种用于表示高精度十进制数的关键…

【理论·专业课】第三次作业

第1题&#xff08;存储管理_内存碎片&#xff09; 请指出内部碎片与外部碎片的区别&#xff61; ANS&#xff1a; 内部碎片是分配给进程但未被进程使用且无法被其他进程利用的内存空间 外部碎片是内存中因进程分配释放内存形成的不连续小块&#xff0c;虽总和够但因不连续无…

最新的springboot 3.x的支持s3协议的2.x方法的minio上传文件方法

拉取镜像 docker pull registry.cn-hangzhou.aliyuncs.com/qiluo-images/minio:latest运行命令 docker run -d \--name minio \-p 10087:9000 \-p 10088:9001 \-e MINIO_ROOT_USERminioadmin \-e MINIO_ROOT_PASSWORDY6HYraaphfZ9k8Lv \-v /data/minio/data:/data \-v /data/…

cocos creator接入字节跳动抖音小游戏JSAPI敏感词检测(进行文字输入,但输入敏感词后没有替换为*号)

今天更新了某个抖音小游戏的版本&#xff0c;增加了部分剧情&#xff0c;半天过后一条短信审核未通过&#xff0c;emmm…抖音总是能给开发者惊喜…打开电脑看看这次又整什么幺蛾子… 首先是一脸懵逼&#xff0c;后端早已接入了官方的内容安全检测能力了&#xff08;https://de…