Java 实现支付宝支付、退款、订单查询

news2024/12/27 0:28:32

最在开发一款APP,需要实现支付宝支付,记录一下实现过程

流程整体交互图如下所示
在这里插入图片描述

一、引入pom依赖

            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>4.0.3</version>
            </dependency>

二、初始化请求对象

@SneakyThrows(value = AlipayApiException.class)
    public AlipayClient initAlipay() {
        //构造client
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        //设置网关地址
        certAlipayRequest.setServerUrl(AliPayUtil.SERVER_URL);
        //设置应用Id
        certAlipayRequest.setAppId(AliPayUtil.APP_ID);
        //设置应用私钥
        certAlipayRequest.setPrivateKey(AliPayUtil.PRIVATE_KEY);
        //设置请求格式,固定值json
        certAlipayRequest.setFormat(AliPayUtil.FORMAT);
        //设置字符集
        certAlipayRequest.setCharset(AliPayUtil.CHARSET);
        //设置签名类型
        certAlipayRequest.setSignType(AliPayUtil.SIGN_TYPE);
        //设置应用公钥证书路径
        certAlipayRequest.setCertPath(AliPayUtil.CERT_PATH);
        //设置支付宝公钥证书路径
        certAlipayRequest.setAlipayPublicCertPath(AliPayUtil.ALIPAY_PUBLIC_CER_PATH);
        //设置支付宝根证书路径
        certAlipayRequest.setRootCertPath(AliPayUtil.ROOT_CER_PATH);
        //构造client
        return new DefaultAlipayClient(certAlipayRequest);
    }

三、生成支付宝订单

注意,这里返回orderStr需要返回前端、用于唤起支付宝APP

@SneakyThrows(value = AlipayApiException.class)
    public String alipayRecharge(AliPayModel aliPayModel) {

        //初始化配置信息
        AlipayClient alipayClient = initAlipay();

        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

        //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        String orderNumber = aliPayModel.getOrderNumber();

        model.setBody(aliPayModel.getBody());
        model.setSubject(aliPayModel.getSubject());
        model.setOutTradeNo(orderNumber);
        model.setTimeoutExpress(AliPayUtil.TIME_OUT_EXPRESS);
        model.setTotalAmount(aliPayModel.getTotalAmount());
        request.setBizModel(model);

        //异步通知地址
        request.setNotifyUrl(AliPayUtil.NOTIFY_URL);
        try  {
            //业务处理
            //dosomething
            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

            if (!response.isSuccess()) {
                log.error("接口调用错误:{}",response.getBody());
                throw new AlipayApiException("接口调用错误");
            }

            AliPayUtil.NOT_CLOSE_ORDER.put(orderNumber,aliPayModel);

            return response.getBody(); //就是orderString 可以直接给客户端请求,无需再做处理。
        }catch(AlipayApiException e) {
            log.error("生成支付订单错误:",e);
            return null;
        }
    }

三、回调方法

用户在支付宝支付成功之后会回调此方法,接口在步骤二设置。

public String aliPayNotify(HttpServletRequest request) {

        // 将异步通知中收到的待验证所有参数都存放到map中
        Map<String, String> params = AliPayUtil.convertRequestParamsToMapWith(request);
        String paramsJson = JSON.toJSONString(params);
        log.info("支付宝回调,{}", paramsJson);
        try {
            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCertCheckV1(params, AliPayUtil.ALIPAY_PUBLIC_CER_PATH,
                    AliPayUtil.CHARSET, AliPayUtil.SIGN_TYPE);
            if (signVerified) {
                // 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
                AliPayUtil.check(params);
                String tradeStatus = params.get("trade_status");
                String outTradeNo = params.get("out_trade_no");
                String tradeNo = params.get("trade_no");
                //通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功
                if(tradeStatus.equals(AliPayUtil.TRADE_SUCCESS) || tradeStatus.equals(AliPayUtil.TRADE_FINISHED)){
                    //业务逻辑处理
                    //do something

                }
                return "success";
            } else {
                log.info("支付宝回调签名认证失败,signVerified=false, paramsJson:{}", paramsJson);
                return "failure";
            }
        } catch (AlipayApiException e) {
            log.error("支付宝回调签名认证失败,paramsJson:{},errorMsg:{}", paramsJson, e.getMessage());
            return "failure";
        }
    }

注意:此处需返回给支付宝状态 success 或 failure

四、查询订单

   @SneakyThrows(value = AlipayApiException.class)
    public String getAlipayOutTradeNo(Long outTradeNo, Long tradeNo) {

        //初始化支付宝配置
        AlipayClient alipayClient = initAlipay();
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel model = new AlipayTradeQueryModel();
        model.setOutTradeNo(outTradeNo.toString());
        if(tradeNo != null){
            model.setTradeNo(tradeNo.toString());
        }
        request.setBizModel(model);
        AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);
        return response.getBody();
    }

注意:查询订单时可用商品订单号(自己生成的订单号)或支付订单号(支付宝返回的订单号)进行查询,两个都有的情况下优先使用支付宝订单号~

五、退款

    @SneakyThrows(value = AlipayApiException.class)
    public Boolean refund(String outTradeNo,String refundAmount,String refundReason) {

        AlipayClient alipayClient = initAlipay();

        AlipayTradeRefundRequest alipayTradeCloseRequest =new AlipayTradeRefundRequest();
        //请求参数集合对象,除了公共参数之外,所有参数都可通过此对象传递
        AlipayTradeRefundModel alipayTradeRefundModel =new AlipayTradeRefundModel();
        //退款的订单号,传入生成支付订单时的订单号即可
        alipayTradeRefundModel.setOutTradeNo(outTradeNo);
        //退款金额
        alipayTradeRefundModel.setRefundAmount(refundAmount);
        //退款的原因
        alipayTradeRefundModel.setRefundReason(refundReason);
        alipayTradeCloseRequest.setBizModel(alipayTradeRefundModel);

        //退款的执行流程与支付不太一样,支付时成功之后,需要通知回调接口,而退款则不需要,只需判断响应			参数 refundResponse.getFundChange().equals("Y") 判断是否发生了资金变化, equals("Y")表示资金发生了变化,退款成功
        AlipayTradeRefundResponse response = alipayClient.execute(alipayTradeCloseRequest);
        return response.isSuccess() && response.getFundChange().equals("Y");
    }

注意:退款需要传三个参数,商品订单号(自己生成的订单号)、退款原因和退款金额

六、使用到的工具类(仅供参考)

工具类包含支付宝初始化参数订单号生成方法解析支付宝消息方法订单金额验证方法和签名验证方法

@Slf4j
public class AliPayUtil {

    public static Map<String, AliPayModel> NOT_CLOSE_ORDER = new HashMap<>();

    public static String TIME_OUT_EXPRESS = "30m";
    public static String APP_ID = "";
    public static String SERVER_URL = "";
    public static String PRIVATE_KEY = "";
    public static String NOTIFY_URL = "";
    public static String CERT_PATH = "";
    public static String ALIPAY_PUBLIC_CER_PATH = "";
    public static String ROOT_CER_PATH = "";

    public static String CHARSET = "utf-8";

    public static String FORMAT = "json";

    public static String SIGN_TYPE = "RSA2";
    public static String TRADE_SUCCESS = "TRADE_SUCCESS";
    public static String TRADE_FINISHED = "TRADE_FINISHED";


    private static final Random random = new Random();

    public static String generateOrderNumber(int userId, int productId) {
        // 获取当前时间戳
        long timestamp = System.currentTimeMillis();

        // 生成6位随机数
        int randomNum = random.nextInt(900000) + 100000;

        // 组装订单号
        return String.format("%d%d%d%d", timestamp, randomNum, userId, productId);
    }


    /**
     * request 转 map
     *
     * @param request
     * @return
     */
    public static Map<String, String> convertRequestParamsToMapWith(HttpServletRequest request) {
        Map<String, String> params = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        for (Iterator 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] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        return params;
    }

    /**
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
     * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
     * 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
     *
     * @param params 支付宝回调参数
     */

    @SneakyThrows(value = AlipayApiException.class)
    public static void check(Map<String, String> params) {
        //订单号
        String outTradeNo = params.get("out_trade_no");
        //商户订单实际金额
        String totalAmount = params.get("total_amount");
        //APPId
        String appId = params.get("app_id");

        //验证订单号
        AliPayModel aliPayModel = NOT_CLOSE_ORDER.get(outTradeNo);

        if (aliPayModel == null) {
            throw new AlipayApiException("out_trade_no错误");
        }
        //验证订单实际金额
        if (!totalAmount.equals(aliPayModel.getTotalAmount())) {
            throw new AlipayApiException("支付金额不一致");
        }

        //验证app_id是否为商户本身
        if (!appId.equals(AliPayUtil.APP_ID)) {
            throw new AlipayApiException("app_id不一致");
        }
    }


    /**
     * 客户端同步验签
     *
     * @param request
     * @return true:验证成功,false:验证失败
     * @throws AlipayApiException
     */
    public Boolean getVerifySignResult(HttpServletRequest request) throws AlipayApiException {
        Map<String, String> params = AliPayUtil.convertRequestParamsToMapWith(request);
        String paramsJson = JSON.toJSONString(params);
        log.info("支付宝回调,{}", paramsJson);
        return AlipaySignature.rsaCertCheckV1(params, AliPayUtil.ALIPAY_PUBLIC_CER_PATH,
                AliPayUtil.CHARSET, AliPayUtil.SIGN_TYPE);
    }

大功告成,文章中若有不对请指出,望见谅~

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

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

相关文章

【Java可执行命令】(八)JWS应用程序启动工具 javaws:深入解析Java Web Start应用程序的启动工具javaws ~

Java可执行命令之javaws 1️⃣ 概念&#x1f50d;JNLP (Java Network Launch Protocol) &#xff1f; 2️⃣ 优势3️⃣ 使用3.1 语法3.1.1 运行选项&#xff1a;-Xnosplash3.1.2 运行选项&#xff1a;-wait3.1.3 控制选项&#xff1a;-import [导入选项] < jnlp-file> 4️…

IDEA中删除某个模块后在创建同名模块显示已存在 的解决方案

IDEA中删除某个模块后在创建同名模块显示已存在 的解决方案 出现的问题复现解决方案成功添加后可能会出现的问题 出现的问题复现 前提是你认为已经删干净了&#xff0c;因为删除模块得删除两次才能从本地的文件夹中删除。 解决方案 右击项目名 找到 这个玩意儿&#xff0c;点…

MySQL不常用但非常实用的函数在项目中的应用

MySQL内置了不少函数&#xff0c;利用这些函数可以很好地在进行数据查询时候&#xff0c;进行数据处理&#xff0c;如果要查看MySQL所有的内置函数&#xff0c;可以在官网的文档中&#xff1a;Built-In Function and Operator Reference 有很详细的表格&#xff0c;列举了所有…

代码随想录二刷day39 |动态规划 之 62.不同路径 63. 不同路径

day39 62.不同路径确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组的初始化确定遍历顺序举例推导dp数组 63. 不同路径 II确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 62.不…

模仿QQ之右键菜单

参考&#xff1a;QT多级菜单 - 知乎 (zhihu.com) 运行效果图&#xff1a; 关键代码&#xff1a; void personMenu::contextMenuEvent(QContextMenuEvent *event) {//我完全可以写出一个代码生成器来把这些代码生成出来。parentnew QMenu(this);parent->addAction(QIcon(…

C++ 多态详解附图与代码

一、多态 1.1 什么是多态 多态是面向对象编程中的一个重要概念&#xff0c;它允许在不同类型的对象上执行相同的操作&#xff0c;并根据对象的实际类型来决定具体执行哪个操作。通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象…

2019年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题——纯享题目版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&#xff0c;题目常常看&a…

【排序算法】堆排序

堆与一维数组 建立堆与一维数组的联系 堆排序并不是直接对堆节点Node类型排序&#xff0c;而是通过建立索引之间的关系&#xff0c;对一维数组排序。 称之为堆排序&#xff0c;是因为节点索引值之间的关系与完全二叉树的非常类似&#xff0c;而树又称堆。 设根节点为i&#xff…

【C#】委托、匿名方法、Lambda表达式和事件

【C#】委托、匿名方法、Lambda表达式和事件 委托 什么是委托&#xff1f; 委托和类一样&#xff0c;是用户自定义类型&#xff0c;是方法&#xff08;函数&#xff09;的抽象。通俗讲&#xff0c;委托就是 自定义类型的方法&#xff08;函数&#xff09;的代表。 声明委托 …

HTML+CSS+JavaScript华为主页

样式&#xff1a; HTMLCSSJavaScript仿华为首页 HTML: <!DOCTYPE html> <html><head><meta charset"utf-8"><link rel"stylesheet" type"text/css" href"Homepage.css"/><script type"text/ja…

NextJs下浅尝Prisma+Sqlite+逆向生成数据模型

1.安装prisma npm install prisma/client 2.创建schema.prisma npx prisma init 执行完命令后创建文件目录如下&#xff1a; 3.配置数据库连接 generator client {provider "prisma-client-js" }datasource db {provider "sqlite" //数据库类型 这…

libevent实践07:监听服务器并管理客户端

简介 函数bufferevent_new struct bufferevent * bufferevent_new(evutil_socket_t fd,bufferevent_data_cb readcb, bufferevent_data_cb writecb,bufferevent_event_cb eventcb, void *cbarg) 参数说明&#xff1a; fd:新客户端的文件描述符 readcb&#xff1a;一个函数指…

【Redis的优化】

目录 一、Redis 高可用二、 Redis 持久化2.1、Redis 提供两种方式进行持久化2.2、RDB 持久化1. 触发条件&#xff08;1&#xff09;手动触发&#xff08;2&#xff09;自动触发 2. 执行流程3. 启动时加载 2.3、AOF 持久化1. 开启AOF2. 执行流程(1&#xff09;命令追加(append)(…

深入理解 Linux 物理内存分配全链路实现

目录 内核物理内存分配接口 物理内存分配内核源码实现 内存分配的心脏 __alloc_pages prepare_alloc_pages 内存慢速分配入口 alloc_pages_slowpath 总结 内核物理内存分配接口 在物理内存分配成功的情况下&#xff0c; alloc_pages&#xff0c;alloc_page 函数返回的都是指…

2022最常用密码公布,你的账户安全吗?

密码管理工具 NordPass 公布了 2022 年最常用密码列表&#xff0c;以及破解密码所需的时间。该研究基于对来自 30 个不同国家 / 地区的 3TB 数据库的分析。研究人员将数据分为不同的垂直领域&#xff0c;使得其能够根据国家和性别进行统计分析。今年的研究主要聚焦于文化如何影…

工业软件对于现代制造业的生产效率和质量有何影响?

工业软件在提高现代制造业的生产力和质量方面发挥着至关重要的作用。比如&#xff1a; 流程自动化&#xff1a;工业软件可以实现各种制造流程的自动化&#xff0c;消除手动任务并减少人为错误。自动化通过简化操作、缩短周期时间和提高整体效率来提高生产力。它还可以最大限度地…

vue3和element plus踩坑

1.有说vue版本有两个&#xff0c;但检查之后发现只有一个&#xff0c;且为vue3的版本 2.也有说是因为命名的问题&#xff0c;组件名和页面名一致 最后发现是因为 在main.js里面引入element plus 使用这种use方式会报错&#xff0c;虽然也不知道为什么 import { createApp } …

《计算机系统与网络安全》第十一章 入侵检测与防御技术

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Dell-Precision5520 电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件配置 硬件型号驱动情况 主板Dell-Precision5520 处理器Intel Core i7-7820HQ已驱动 内存Micron 2400MHz DDR4 16GB x2已驱动 硬盘Samsung 970EVO 512GB已驱动 显…

Java中volatile的作用和原理

用法 volatile 是 Java 中的关键字&#xff0c;直接修饰成员变量&#xff0c;不能和 final 关键字同时使用。 private volatile boolean flag false;作用 当一个变量被声明为volatile时&#xff0c;它可以确保以下两点&#xff1a; 保证可见性&#xff1a;当一个线程修改了…