SpringBoot—支付—支付宝

一、流程 

二、沙箱操作

1.用支付宝账号登录【开放控制平台】创建应用获取 appid 
2.选择沙箱模拟环境 
3.沙箱应用-》获取appid(一个appid绑定一个收款支付宝账户) 
4.利用开发助手工具生成RSA2密钥 
公钥:传给支付宝平台 
私钥:配置代码中,签名用 
5.生成密钥后,进行配置 
返回平台 --> 开发信息 --> 自定义密钥 --> 设置并启用(加签)--> 应用公钥 
保存后生成支付宝公钥(需要配置到项目中) 
6.支付宝网关(配置代码中) 
https://openapi.alipaydev.com/gateway.do 

2.1、用 nat app 内网穿透工具进行异步/同步回调 

NATAPP-内网穿透 基于ngrok的国内高速内网映射工具

2.2、依赖与配置 

2.2.1、引入依赖 

<dependency>
   <groupId>com.alipay.sdk</groupId>
   <artifactId>alipay-sdk-java</artifactId>
</dependency>

 2.2.2、配置文件 

spring:
  application:
    name: alipay-demo

server:
  port: 9999

alipay:
  appId: 收款账号对应的应用id
  privateKey: 应用私钥
  publicKey: 支付宝公钥
  returnUrl: 127.0.0.1:9999/order/return
  notifyUrl: 127.0.0.1:9999/order/notify-url(异步回调地址,http/https开头必须外网能访问)
  refundNotifyUrl: https://blog.csdn.net/qq_37630282(同步回调地址,需要外网能够访问)
  gatewayUrl: https://openapi.alipaydev.com/gateway.do(沙箱官网与正式网关不同,此处为沙箱网关)
  charset: utf-8
  signType: RSA2

2.2.3、支付相关的实体类 

支付的配置类
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
    private String appId;
    private String privateKey;
    private String publicKey;
    private String returnUrl;
    private String notifyUrl;
    private String refundNotifyUrl;
    private String gatewayUrl;
    private String charset;
    private String signType;
}

支付的实体类
@Data
public class AliPay {
    private String traceNo;   // 订单编号
    private double totalAmount; // 总金额
    private String subject;  // 商品名称
    private String alipayTraceNo;
}

2.3、支付、回调、退款接口 

支付流程
1.在Controller中配置gateway_url(调用支付宝url的一个网关地址)、format(JSON形式)、charset(UTF-8)、sign_type(签名方式-rsa2)
2.编写Get请求,(参数是AliPay的配置类里生成的订单号、总金额、支付的名称、支付宝交易凭证号、HttpServletResponse)
3.创建Client(由通用SDK提供的Client,负责调支付宝的API,设置参数包含网关地址、appid、密钥、公钥、format、charset、签名方式) 
4.创建 AlipayTradePagePayRequest,配置notifyUrl并设置Request参数(参数包含订单号、总金额、支付的名称)(格式:JSON格式)
5.通过AlipayClient执行request调用SDK生成表单,用HttpServletResponse(浏览器响应的一个流)写表单的内容,创建一个html的网页)
6.回调接口(Post)先验证交易状态是否成功,获取request里面的信息。支付宝验签(使用的是AlipaySignature(通用SDK提供的类)获取一个String字符串将其与sign签名验证),用 OrderMapper 更新到数据库)

退款流程
1.创建Client(由通用SDK提供的Client,负责调用支付宝的API)(参数包含网关地址、appid、密钥、公钥、format、charset、签名方式)
2.创建 AlipayTradePagePayRequest,设置Request参数(参数包含支付宝回调的订单流水号、总金额、我的订单编号)(格式:JSON格式)
3.通过 AlipayClient 执行 request 获取 response,通过isSuccess判断是否成功,成功后更新数据库状态。

@RestController
@RequestMapping("/alipay")
public class AliPayController {
	
    private static final String GATEWAY_URL = "https://openapi.alipaydev.com/gateway.do";
	
    private static final String FORMAT = "JSON";
	
    private static final String CHARSET = "UTF-8";
	
    //签名方式
    private static final String SIGN_TYPE = "RSA2";
	
    @Resource
    private AlipayConfig aliPayConfig;
	
	/**
	 * 支付接口
	 */
	@GetMapping("/pay")
	public void pay(AliPay aliPay, HttpServletResponse httpResponse) throws Exception {
		// 1. 创建Client,通用SDK提供的Client,负责调用支付宝的API
		AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
		aliPayConfig.getPrivateKey(), FORMAT, CHARSET, 
		aliPayConfig.getPublicKey(), SIGN_TYPE);
		// 2. 创建 Request并设置Request参数
		AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();  // 发送请求的 Request类
		request.setNotifyUrl(aliPayConfig.getNotifyUrl());
		JSONObject bizContent = new JSONObject();
		bizContent.set("out_trade_no", aliPay.getTraceNo());  // 我们自己生成的订单编号
		bizContent.set("total_amount", aliPay.getTotalAmount()); // 订单的总金额
		bizContent.set("subject", aliPay.getSubject());   // 支付的名称
		bizContent.set("product_code", "FAST_INSTANT_TRADE_PAY");  // 固定配置
		request.setBizContent(bizContent.toString());
		// 执行请求,拿到响应的结果,返回给浏览器
		AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
		if(response.isSuccess()){
			//System.out.println("调用成功");
			log.info("调用成功");
			String form = response.getBody();
			httpResponse.setContentType("text/html;charset=" + CHARSET);
			httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面
			httpResponse.getWriter().flush();
			httpResponse.getWriter().close();
			return form;
		} else {
			//System.out.println("调用失败");
			log.info("调用失败,返回码=====" + response.getCode() + "返回描述=====" + response.getMsg());
			throw new RuntimeException("调用失败");
		}
	}
	
	/**
	 * 回调接口
	 */
    @PostMapping("/notify")  // 必须是POST接口
    public String payNotify(HttpServletRequest request) throws Exception {
        if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
            System.out.println("=========支付宝异步回调========");
            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                params.put(name, request.getParameter(name));
                // System.out.println(name + " = " + request.getParameter(name));
            }
            String outTradeNo = params.get("out_trade_no");
            String gmtPayment = params.get("gmt_payment");
            String alipayTradeNo = params.get("trade_no");
            String sign = params.get("sign");
            String content = AlipaySignature.getSignCheckContentV1(params);
            boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, aliPayConfig.getPublicKey(), "UTF-8"); // 验证签名 
            // 支付宝验签 
            if (checkSignature) {
                // 验签通过 
                System.out.println("交易名称: " + params.get("subject"));
                System.out.println("交易状态: " + params.get("trade_status"));
                System.out.println("支付宝交易凭证号: " + params.get("trade_no"));
                System.out.println("商户订单号: " + params.get("out_trade_no"));
                System.out.println("交易金额: " + params.get("total_amount"));
                System.out.println("买家在支付宝唯一id: " + params.get("buyer_id"));
                System.out.println("买家付款时间: " + params.get("gmt_payment"));
                System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));
            }
        }
        return "success";
    }
	
	/**
	 * 退款接口
	 */
	@GetMapping("/return")
	public Result returnPay(AliPay aliPay) throws AlipayApiException {
			// 7天无理由退款
			String now = DateUtil.now();
			Orders orders = ordersMapper.getByNo(aliPay.getTraceNo());
			if (orders != null) {
				// hutool工具类,判断时间间隔
				long between = DateUtil.between(DateUtil.parseDateTime(orders.getPaymentTime()), DateUtil.parseDateTime(now), DateUnit.DAY);
				if (between > 7) {
					return Result.error("-1", "该订单已超过7天,不支持退款");
				}
			}
		// 1. 创建Client,通用SDK提供的Client,负责调用支付宝的API
		AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL,
				aliPayConfig.getAppId(), aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET,
				aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);
		// 2. 创建 Request,设置参数
		AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
		JSONObject bizContent = new JSONObject();
		bizContent.set("trade_no", aliPay.getAlipayTraceNo());  // 支付宝回调的订单流水号
		bizContent.set("refund_amount", aliPay.getTotalAmount());  // 订单的总金额
		bizContent.set("out_request_no", aliPay.getTraceNo());   //  我的订单编号

		// 返回参数选项,按需传入
		//JSONArray queryOptions = new JSONArray();
		//queryOptions.add("refund_detail_item_list");
		//bizContent.put("query_options", queryOptions);
		request.setBizContent(bizContent.toString());
		// 3. 执行请求
		AlipayTradeRefundResponse response = alipayClient.execute(request);
		if (response.isSuccess()) {  // 退款成功,isSuccess 为true
			System.out.println("调用成功");
			// 4. 更新数据库状态
			ordersMapper.updatePayState(aliPay.getTraceNo(), "已退款", now);
			return Result.success();
		} else {   // 退款失败,isSuccess 为false
			System.out.println(response.getBody());
			return Result.error(response.getCode(), response.getBody());
		}
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

json解析本地数据,使用JSONObject和JsonUtility两种方法。

json解析丨网址、数据、其他信息 文章目录 json解析丨网址、数据、其他信息介绍一、文中使用了两种方法作为配置二、第一种准备2.代码块 二、第二种总结 介绍 本文可直接解析本地json信息的功能示例&#xff0c;使用JSONObject和JsonUtility两种方法。 一、文中使用了两种方法…

[GKCTF 2020]ez三剑客-eztypecho

[GKCTF 2020]ez三剑客-eztypecho 考点&#xff1a;Typecho反序列化漏洞 打开题目&#xff0c;发现是typecho的CMS 尝试跟着创建数据库发现不行&#xff0c;那么就搜搜此版本的相关信息发现存在反序列化漏洞 参考文章 跟着该文章分析来&#xff0c;首先找到install.php&#xf…

Arduino开发实例-AD8232心率监测传感器驱动

AD8232心率监测传感器驱动 文章目录 AD8232心率监测传感器驱动1、AD8232介绍2、硬件准备及接线3、驱动实现1、AD8232介绍 AD8232 传感器可为您提供心电图或 ECG 信号监测。 分析这些信号可以提供有关心脏功能的有用信息,例如心跳率、心律和其他有关心脏状况的信息。 该模块可…

word 常用功能记录

word手册 多行文字对齐标题调整文字间距打钩方框插入三线表插入参考文献自动生成目录 多行文字对齐 标题调整文字间距 打钩方框 插入三线表 插入一个最基本的表格把整个表格设置为无框线设置上框线【实线1.5磅】设置下框线【实线1.5磅】选中第一行&#xff0c;设置下框线【实线…

Mysql的基本用法(上)非常详细、快速上手

上篇结束了java基础&#xff0c;本篇主要对Mysql中的一些常用的方法进行了总结&#xff0c;主要对查询方法进行了讲解&#xff0c;包括重要的多表查询用到的内连接和外连接等&#xff0c;以下代码可以直接复制到可视化软件中&#xff0c;方便阅读以及练习&#xff1b; SELECT *…

Springer Latex正文参考文献样式改为数字

用过爱斯唯尔的latex&#xff0c;正文参考文献都是数字&#xff0c;第一次用Springer Latex的参考文献竟然是authoryear&#xff0c;如下&#xff1a; 将这种样式变回序号样式&#xff1a; &#xff08;1&#xff09;使用这个documentclass&#xff08;此为双栏&#xff09; …

Mnist手写体数字数据集介绍与在Pytorch中使用

1.介绍 MNIST&#xff08;Modified National Institute of Standards and Technology&#xff09;数据集是一个广泛用于机器学习和计算机视觉研究的常用数据集之一。它由手写数字图像组成&#xff0c;包括0到9的数字&#xff0c;每张图像都是28x28像素的灰度图像&#xff0c;图…

Unity游戏资源更新(AB包)

目录 前言&#xff1a; 一、什么是AssetBundle 二、AssetBudle的基本使用 1.AssetBundle打包 2.BuildAssetBundle BuildAssetBundleOptions BuildTarget 示例 3.AssetBundle的加载 LoadFromFile LoadFromMemory LoadFromMemoryAsync UnityWebRequestAsssetBundle 前…

设计模式:单例模式

文章目录 1、概念2、实现方式1、懒汉式2、饿汉式3、双检锁/双重校验锁4、登记式/静态内部类5、枚举6、容器实现单例 1、概念 单例模式&#xff08;Singleton Pattern&#xff09;是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创…

Linux引导过程与服务控制

目录 一、操作系统引导过程 1. 过程图示 2. 步骤解析 2.1 bios 2.2 mbr 2.3 grup 2.4 加载内核文件 3. 过程总结 4. centos6和centos7启动区别 5. 小结 二、服务控制及切换运行级别 1. systemd核心概念 2. 运行级别 3. Systemd单元类型 4. 运行级别所对应的Syst…

YOLOv8改进 | 检测头篇 | DynamicHead原论文一比一复现 (不同于网上版本,全网首发)

一、本文介绍 本文给大家带来的改进机制是DynamicHead(Dyhead),这个检测头由微软提出的一种名为“动态头”的新型检测头,用于统一尺度感知、空间感知和任务感知。网络上关于该检测头我查了一些有一些魔改的版本,但是我觉得其已经改变了该检测头的本质,因为往往一些细节上才…

【安卓的签名和权限】

Android 编译使用哪个key签名&#xff1f; 一看Android.mk 在我们内置某个apk的时候都会带有Android.mk&#xff0c;这里面就写明了该APK使用的是什么签名&#xff0c;如&#xff1a; LOCAL_CERTIFICATE : platform表明使用的是platform签名 LOCAL_CERTIFICATE : PRESIGNED…

MPI并行程序设计 —— C 和 fortran 环境搭建 openmpi 示例程序

1.安装环境 wget https://download.open-mpi.org/release/open-mpi/v4.1/openmpi-4.1.6.tar.g tar zxf openmpi-4.1.6.tar.gz cd openmpi-4.1.6/ 其中 configure 选项 --prefix/.../ 需要使用绝对路径&#xff0c;例如&#xff1a; ./configure --prefix/home/hipper/ex_open…

使用UDP和JSON在C#中高效发送结构体数据

使用UDP和JSON在C#中高效发送结构体数据 引言 在许多网络编程场景中&#xff0c;我们经常需要在不同的应用程序或服务之间发送和接收数据。UDP&#xff08;用户数据报协议&#xff09;因其低延迟和少开销的特点&#xff0c;在需要快速数据传输的场景中非常有用。本文介绍了如何…

VS 2022 控制台程序运行时不显示控制台

Visual Studio 2022&#xff0c;C#控制台程序运行时不显示控制台。此外&#xff0c;C#程序修改运行时的程序名。 文章目录 不显示控制台修改运行时的程序名打包成.exe 文件 不显示控制台 1 选中需要项目&#xff0c;右击属性&#xff0c;选中常规。 2 将输出类型从控制台改为…

介绍两本书《助推》与《耐力》

冠历最后一年已经养成了没有冲突的情况下每天跑步、读书的习惯&#xff0c;今天突发奇想&#xff1a;是否重新挑战下每日写作。 ​ 今天介绍两本书。第一本是《助推》&#xff0c;这本书是由于真友 吾真本 的介绍开始读的。 一句话介绍这本书&#xff0c;那就是&#xff1a;如果…

MySQL将多条数据合并成一条的完整示例

数据库中存的是多条数据&#xff0c;展示的时候需要合并成一条 数据表存储形式如下图 以type分组&#xff0c;type相同的算一条&#xff0c;且保留image和link的所有数据&#xff0c;用groupBy只保留一条数据 解决方案&#xff1a;用GROUP_CONCAT 完整语法如下 group_concat…

Go后端开发 -- main函数 变量 常量 函数

Go后端开发 – main函数 & 变量 & 常量 & 函数 文章目录 Go后端开发 -- main函数 & 变量 & 常量 & 函数一、第一个main函数1.创建工程2.main函数解析 二、变量声明1.单变量声明2.多变量声明 三、常量1.常量的定义2.优雅的常量 iota 四、函数1.函数返回…

【FPGA/verilog -入门学习15】vivado FPGA 数码管显示

1&#xff0c;需求&#xff1a;使用xc720 开发板的8个数码管显示12345678 2&#xff0c;需求分析&#xff1a; 75hc595 1&#xff0c;74hc595驱动&#xff0c;将串行数据转换成并行输出。对应研究手册 2&#xff0c;发送之前将要发的数据&#xff0c;合并成高8位:SEG,低8位&…

二蛋赠书十三期:《一书读懂物联网》

前言 大家好&#xff01;我是二蛋&#xff0c;一个热爱技术、乐于分享的工程师。在过去的几年里&#xff0c;我一直通过各种渠道与大家分享技术知识和经验。我深知&#xff0c;每一位技术人员都对自己的技能提升和职业发展有着热切的期待。因此&#xff0c;我非常感激大家一直…