当前位置: 首页 > news >正文

springboot+vue 支付宝支付(沙箱方式,测试环境使用)

准备工作

1 支付宝沙箱环境的公钥,私钥配置,查询等使用,如果是用自定义的方式,需要下生成公钥,私钥的工具,否则不需要

   登录 - 支付宝

   小程序文档 - 支付宝文档中心




本地测试时,需要内网穿透的工具(如natApp,内网穿透还要配域名,默认的域名不支持外网访问),不然支付宝不能通知本地的程序接口



后端开发
1 pom.xml

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.15</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--支付宝相关--><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.35.79.ALL</version></dependency><!--支付宝SDK的依赖--><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-easysdk</artifactId><version>2.2.0</version></dependency>
</dependencies>

2 yml 配置,特别注意几个公钥,私钥,不要搞叉了

#支付宝配置
alipay:#支付宝应用id,识别应用appId: 2021000148621214#应用私钥,用于对请求进行签名,确保请求的真实性和完整性#appPrivateKey: xxxxx#支付宝的公钥,用于验证支付宝返回的数据的签名,确保数据的真实性和完整性并alipayPublicKey: xxxxxxxx# 支付宝支付结果通知的回调地址,当支付完成后,支付宝会向这个地址发送支付结果通知。(内网穿透配置的地址)notifyUrl: http://lingling.mynatapp.cc/payment/notify# 支付宝支付成功后的页面跳转地址,当支付完成后,支付宝会跳转到这个地址,并携带支付结果参数。returnUrl: http://localhost:8080/payment/index#支付宝沙箱网关地址,开发者在沙箱环境调用 0penAPI 发送 http(s)请求的目标地址,需配置在Alipayclient中并gatewayUrl: http://openapi-sandbox.dl.alipaydev.com/gateway.do#返回数据格式,仅支持jsonformat: json#编码格式,如UTF-8, GBK, GB2312charSet: UTF-8#签名方式signType: RSA2logging:level:root: info

3 yml文件的属性读取配置

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** 支付宝配置类*/
@Component
@ConfigurationProperties(prefix = "alipay")
@Data
public class AlipayConfig {/*** 支付宝应用 ID,用于识别应用*/private String appId;/*** 应用私钥,用于对请求进行签名,确保请求的真实性和完整性*/private String appPrivateKey;/*** 支付宝公钥,用于验证支付宝返回的数据的签名,确保数据的真实性和完整性*/private String alipayPublicKey;/*** 支付宝支付结果通知的回调地址*/private String notifyUrl;/*** 支付宝支付结果的同步回调地址,用户支付成功后,支付宝会向该地址发送一个 GET 请求,开发者需要处理该请求,并根据请求参数中的 out_trade_no 查询订单状态,确认订单是否支付成功。*/private String returnUrl;/*** 支付宝沙箱网关地址,开发者在沙箱环境调用 OpenAPI 发送 http(s) 请求的目标地址*/private String gatewayUrl;/*** 请求和响应的格式,目前只支持 JSON*/private String format;private String charSet;/*** 签名方式*/private String signType;
}

4 订单请求的pojo

import lombok.Data;@Data
public class AliPay {private String traceNo;         // 商户订单号private double totalAmount;    // 支付总金额private String subject;        // 商品名称//private String alipayTraceNo;  // 支付宝流水号
}

5 核心类,支付,回调,查询支付状态的接口类

import cn.hutool.json.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.boot.config.AlipayConfig;
import com.boot.entity.AliPay;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/*** PayController 类用于处理与支付宝支付相关的请求*/
@RestController
@RequestMapping("/payment")
@Slf4j
public class PayController {/*** 注入支付宝配置信息*/@Autowiredprivate AlipayConfig alipayConfig;/*** 模拟 这是 产品页面,个人中心页面,用于支付成功后跳转的页面** @return*/@RequestMapping("/index")public String indexui() {return "产品页面,个人中心页面";}/*** 处理支付请求** @param aliPay       支付信息* @param httpResponse HTTP 响应* @throws IOException 当输出响应出错时抛出*/@RequestMapping("/pay")public void pay(@RequestBody AliPay aliPay, HttpServletResponse httpResponse) throws IOException {System.out.println("alipayConfig = " + alipayConfig);System.out.println("aliPay = " + aliPay);// 1. 创建 Client,通用 SDK 提供的 Client,负责调用支付宝的 APIAlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getGatewayUrl(),alipayConfig.getAppId(),alipayConfig.getAppPrivateKey(),alipayConfig.getFormat(),alipayConfig.getCharSet(),alipayConfig.getAlipayPublicKey(),alipayConfig.getSignType());// 2. 创建 Request 并设置 Request 参数AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); // 发送请求的 Request 类request.setNotifyUrl(alipayConfig.getNotifyUrl());request.setReturnUrl(alipayConfig.getReturnUrl());JSONObject bizContent = new JSONObject();bizContent.put("out_trade_no", aliPay.getTraceNo()); // 我们自己生成的订单编号bizContent.put("total_amount", aliPay.getTotalAmount()); // 订单的总金额bizContent.put("subject", aliPay.getSubject()); // 支付的名称bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 固定配置request.setBizContent(bizContent.toString());// 执行请求,拿到响应的结果,返回给浏览器String form = "";try {form = alipayClient.pageExecute(request).getBody(); // 调用 SDK 生成表单} catch (AlipayApiException e) {e.printStackTrace();}httpResponse.setContentType("text/html;charset=UTF-8");httpResponse.getWriter().write(form); // 直接将完整的表单 HTML 输出到页面httpResponse.getWriter().flush();httpResponse.getWriter().close();}/*** 支付宝异步通知接口,外网要能访问,测试时可以用内网穿透工具的方式* 注意这里必须是POST接口** @param request HTTP 请求* @return 通知处理结果* @throws Exception 当处理通知出错时抛出*/@PostMapping("/notify")public String payNotify(HttpServletRequest request) throws Exception {System.out.println("=========支付宝异步回调========");if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {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 tradeNo = params.get("out_trade_no");String gmtPayment = params.get("gmt_payment");String alipayTradeNo = params.get("trade_no");String sign = params.get("sign");System.out.println("tradeNo = " + tradeNo);System.out.println("gmtPayment = " + gmtPayment);System.out.println("alipayTradeNo = " + alipayTradeNo);System.out.println("sign = " + sign);String content = AlipaySignature.getSignCheckContentV1(params);System.out.println("content = " + content);boolean checksignature = AlipaySignature.rsa256CheckContent(content, sign, alipayConfig.getAlipayPublicKey(), "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"));}// TODO 业务处理 ,如 修改订单状态return "sucess";}return "fail";}/*** 查询支付状态** @param outTradeNo 商户订单号* @return 查询结果*/@GetMapping("/query")public String queryPaymentStatus(@RequestParam("outTradeNo") String outTradeNo) {try {// 1. 创建 AlipayClientAlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getGatewayUrl(),alipayConfig.getAppId(),alipayConfig.getAppPrivateKey(),alipayConfig.getFormat(),alipayConfig.getCharSet(),alipayConfig.getAlipayPublicKey(),alipayConfig.getSignType());// 2. 创建请求对象com.alipay.api.request.AlipayTradeQueryRequest request = new com.alipay.api.request.AlipayTradeQueryRequest();// 3. 设置请求参数JSONObject bizContent = new JSONObject();bizContent.put("out_trade_no", outTradeNo); // 商户订单号request.setBizContent(bizContent.toString());// 4. 执行请求并获取响应com.alipay.api.response.AlipayTradeQueryResponse response = alipayClient.execute(request);// 5. 处理响应结果if (response.isSuccess()) {System.out.println("查询成功");System.out.println("订单号: " + response.getOutTradeNo());System.out.println("交易状态: " + response.getTradeStatus());System.out.println("交易金额: " + response.getTotalAmount());System.out.println("买家支付宝账号: " + response.getBuyerLogonId());System.out.println("交易时间: " + response.getSendPayDate());return "查询成功: 订单号=" + response.getOutTradeNo() +", 状态=" + response.getTradeStatus() +", 金额=" + response.getTotalAmount();} else {System.out.println("查询失败");System.out.println("错误码: " + response.getCode());System.out.println("错误信息: " + response.getMsg());return "查询失败: 错误码=" + response.getCode() + ", 错误信息=" + response.getMsg();}} catch (Exception e) {e.printStackTrace();return "查询异常: " + e.getMessage();}}}

前端随便写的一个测试页面

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>旅游产品页面</title><script src="https://cdn.jsdelivr.net/npm/vue@2"></script><script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script><style>body {font-family: Arial, sans-serif;margin: 20px;}.product {border: 1px solid #ccc;padding: 20px;margin-bottom: 20px;border-radius: 5px;width: 300px;}.product h2 {margin-top: 0;}.buy-button {background-color: #4CAF50;color: white;padding: 10px 15px;border: none;border-radius: 5px;cursor: pointer;}.buy-button:hover {background-color: #45a049;}</style>
</head>
<body>
<div id="app"><h1>旅游产品列表</h1><div v-for="(product, index) in products" :key="index" class="product"><h2>{{ product.name }}</h2><p>价格: ¥{{ product.price }}</p><button class="buy-button" @click="buyProduct(product)">购买</button></div>
</div><script>new Vue({el: '#app',data: {products: [{ name: '泰国普吉岛五日游', price: 250 },{ name: '日本东京六日深度游', price: 356 },{ name: '法国巴黎浪漫七日游', price: 129 },{ name: '意大利文艺复兴之旅八日游', price: 500 },{ name: '希腊圣托里尼岛度假四日游', price: 450 },// 更多产品...{ name: '瑞士阿尔卑斯山滑雪之旅十日游', price: 600 },{ name: '埃及金字塔探秘七日游', price: 780 },{ name: '澳大利亚大堡礁潜水探险九日游', price: 156 },{ name: '巴西里约热内卢狂欢节体验六日游', price: 360 },{ name: '加拿大落基山脉自驾游十二日游', price: 98 },]},methods: {buyProduct(product) {const order = {traceNo: `ORDER_${Math.floor(Math.random() * 1000000)}`, // 随机生成订单号totalAmount: product.price, // 产品价格subject: product.name // 产品名称};// 发起 POST 请求到后端 /payment/pay 接口,并发送 JSON 数据axios.post('http://localhost:8080/payment/pay', order, {headers: {'Content-Type': 'application/json' // 设置请求头为 JSON 格式},responseType: 'text' // 确保返回的内容是 HTML 表单}).then(response => {// 将返回的表单直接插入到页面中并提交const formHtml = response.data;const div = document.createElement('div');div.innerHTML = formHtml;document.body.appendChild(div);document.forms[0].submit();}).catch(error => {console.error('支付请求失败:', error);alert('支付请求失败,请稍后再试!');});}}});
</script>
</body>
</html>

相关文章:

  • VUE Element-ui Message 消息提示组件自定义封装
  • 如何在编译命令中添加灰度标识
  • cnas认证注意事项,cnas认证审核有效期?cnas认证难吗?
  • 思科路由器密码绕过+重置
  • uniapp小程序使用echarts
  • 湖南大学-操作系统实验四
  • python——模块、包、操作文件
  • 如何选择 Flask 和 Spring Boot
  • 【数据结构入门训练DAY-21】信息学奥赛一本通T1334-围圈报数
  • 深入解析C++ STL Stack:后进先出的数据结构
  • 新书推荐——《游·思——看世界 上》孔祥超 著
  • React Ref引用机制解析
  • 指定文件夹随机筛出图像
  • 卷积神经网络常用结构
  • # 构建和训练一个简单的CBOW词嵌入模型
  • 密码学(1)LWE,RLWE,MLWE的区别和联系
  • 语法长难句
  • 星火燎原:Spark技术如何重塑大数据处理格局
  • 设计模式--工厂模式详解
  • ubuntu系统下部署使用git教程
  • 准85后青海海北州副州长、州公安局局长李贤荣赴山东临沂挂职
  • 王文涛会见德国汽车工业协会主席穆勒
  • 银川市长信箱被指乱回复:问诗词大会、答工程欠款,官方称工作失误
  • 当代视角全新演绎,《风雪夜归人》重归首都剧场
  • 特朗普说克里米亚将留在俄罗斯,泽连斯基:绝不承认
  • 广州多条BRT相关线路将停运,全市BRT客运量较高峰时大幅下降