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

【SpringBoot】✈️本地集成支付宝支付功能

目录

👋前言

🍸一、准备

🍻二、开发

        1、sdk 接入

        2、配置

        3、支付实现

🍹三、项目部署

🍸四、测试

💞️ 五、章末


👋前言

        小伙伴们大家好,前片文章记录了在 mybatis 的mapper 中不建议使用方法重载,文章链接如下:

【踩坑】⭐️MyBatis的Mapper接口中不建议使用重载方法_mybatis的mapper可以重载吗-CSDN博客

        今天来本地集成下支付宝的支付功能,使用的框架是 SpringBoot,本地环境 jdk1.8,服务器使用的是 腾讯云(支付宝的接口回调需要提供可以公网访问的接口)

🍸一、准备

        需要先去支付宝官方提供的沙箱环境,进行必要参数获取,沙箱环境链接如下:

登录 - 支付宝

        点击后使用自己支付宝账号扫码登录即可 ,登录后可以进入如下页面,需要获取到 appId,支付宝的公钥私钥参数

         以上获取到的信息,就足够了,另外就是需要提供一个供支付宝回调用的接口,既然是提供给外部应用回调的,那么必须保证公网可访问,本地启动的项目默认是自己可访问,可以通过以下方式做到公网访问:

        第一种就是使用内网穿透工具,内网穿透就是允许外部网络的用户访问内部网络中的服务,内网穿透工具的选择以及如何使用,可以参考之前的文章,链接如下:

【服务器搭建】✈️用自己电脑搭建一个服务器!_服务器怎么搭建-CSDN博客

        第二种就是将项目部署到服务器上,本次实现就是将项目打包放到服务器上运行测试 ,至于服务器的选择,怎么搭建服务器的环境,怎么将项目部署到服务器上可以参考之前的文章,链接如下:

【服务器项目部署】⭐️将本地项目部署到服务器!_如何把本地项目部署到服务器上-CSDN博客

🍻二、开发

        1、sdk 接入

        本地使用的是 springboot 框架,集成很方便,先在项目的 pom.xml 文件中引入支付的 sdk 即可,版本看个人需求,引入后刷新 maven 会自动下载文件

        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.9.28.ALL</version>
        </dependency>
        2、配置

        这里的配置是指,要使用支付宝支付功能必须要提供的参数,具体信息有以下:

        appId:就是沙箱页面上的 appId,复制到这里即可

        appPrivateKey:应用私钥,在沙箱环境中点击“查看”后可以找到“复制私钥”的 地方,快速粘贴

        alipayPublicKey:支付宝公钥,与私钥一样,在下面可以复制到公钥

        notifyUrl:提供给支付宝回调的接口地址,需要公网可以访问

        3、支付实现

        3.1 配置类代码如下:

        将在 yml 文件中的配置内容映射到配置类中,注册为bean 方便管理和使用

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author HuangBenben 
 */
@Data
@Component
@ConfigurationProperties(prefix="alipay")
public class AliPayConfig {
    private String appId;
    private String appPrivateKey;
    private String alipayPublicKey;
    private String notifyUrl;
}

        3.2 支付宝支付请求对象类代码如下:

import lombok.Data;

import java.math.BigDecimal;

/**
 * @author HuangBenben 
 * 支付宝支付请求对象 所需要的参数
 */
@Data
public class PayVO {
    
    private String out_trade_no; // 商户订单号 必填
    private String subject; // 订单名称 必填
    private BigDecimal total_amount; // 付款金额 必填
    private String body; // 商品描述 可空
}

         3.3 业务代码实现:

        该Controller 中值提供了两个接口,一个发送支付请求,一个用户支付宝回调

        支付接口里会记录该订单的信息到数据库中,订单状态(status)初始化为 0,并且这里传给了一个支付完成后自动跳转的地址(本地传的是之前做的一个简单ai服务的首页)

        支付宝回调接口会打印回调的参数(如果合法性校验通过),并且更改订单状态为200

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 org.example.mapper.OrderDetailMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping("/alipay")
@Transactional(rollbackFor = Exception.class)
public class AliPayController {

    @Resource
    AliPayConfig aliPayConfig;

    @Resource
    private OrderDetailMapper orderDetailMapper;
    private static final String GATEWAY_URL = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
    private static final String FORMAT = "JSON";
    private static final String CHARSET = "utf-8";
    private static final String SIGN_TYPE = "RSA2";

    /**
     * 支付接口 传入业务参数
     * 支付是一个我向你要钱的过程,设置api参数就是为了,知道收钱的人是谁,
     * 当执行以后支付宝会返回一个登录页面,支付的人输入帐号密码。并且确定金额输入支付密码进行支付
     * @param aliPay
     * @param httpResponse
     * @throws Exception
     */
    // 这里使用Get其实不是很恰当,应该使用post,这里为了调试方便使用Get
    @GetMapping("/pay")
    public void pay(PayVO aliPay, HttpServletResponse httpResponse) throws Exception {
        // 1、根据支付宝的配置生成一个支付客户端 客户端用于去调用支付宝的API
        // 官方写法
        AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);

        // 2、创建一个支付请求对象
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        // 设置回调接口
        request.setNotifyUrl(aliPayConfig.getNotifyUrl());
        // 商户订单号,商户网站订单系统中唯一订单号,必填,支付宝不允许有两个相同的订单号
        // 使用uuid生成 避免重复
        aliPay.setOut_trade_no(UUID.randomUUID().toString());
        String out_trade_no = aliPay.getOut_trade_no();
        // 付款金额,必填
        BigDecimal total_amount = aliPay.getTotal_amount();
        // 订单名称,必填
        String subject = aliPay.getSubject();
        // 商品描述,可空
        String body = aliPay.getBody();
        // 设置 业务参数 是一个json对象
        // 这个json对象 支付宝后台回去识别,根据这些参数进行处理,例如 金额,订单名称,商品描述
        request.setBizContent("{" +
                "\"out_trade_no\":\"" + out_trade_no + "\"," +
                "\"total_amount\":\"" + total_amount + "\"," +
                "\"subject\":\"" + subject + "\"," +
                "\"body\":\"" + body + "\"," +
                "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        // 支付完以后跳转的地址
        request.setReturnUrl("http://****自己服务地址****:8890/login.html");

        // 3. 客户端执行请求
        // 客户端执行请求,拿到响应的结果,返回给浏览器
        String form = "";
        try {
            // 调用阿里的SDK生成表单
            // 会收到支付宝的响应,响应的是一个页面,一开始是登陆,然后显示金额,让用户输入密码进行付款
            form = alipayClient.pageExecute(request).getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        OrderDetailEntity order = new OrderDetailEntity();
        BeanUtils.copyProperties(aliPay,order);
        orderDetailMapper.insert(order);

        httpResponse.setContentType("text/html;charset=" + CHARSET);
        // 直接将完整的表单html输出到页面
        httpResponse.getWriter().write(form);
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
    }

    @PostMapping("/notify")  // 注意这里必须是POST接口
    public String returnUrl(HttpServletRequest httpRequest,
                            HttpServletResponse httpResponse) throws Exception {

        System.out.println("支付成功, 进入同步通知接口...");

        // 获取支付宝GET过来反馈信息
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = httpRequest.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = iter.next();
            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] + ",";
            }

            // 乱码解决,这段代码在出现乱码时使用
            // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");

            params.put(name, valueStr);
        }

        // 调用SDK验证签名
        boolean signVerified = AlipaySignature.rsaCheckV1(params,
                aliPayConfig.getAlipayPublicKey(),
                CHARSET,
                SIGN_TYPE
                );


         //验签成功
        if (signVerified) {

            // 同步通知返回的参数(部分说明)
            // out_trade_no :	商户订单号
            // trade_no : 支付宝交易号
            // total_amount : 交易金额
            // auth_app_id/app_id : 商户APPID
            // seller_id :收款支付宝账号对应的支付宝唯一用户号(商户UID )
            System.out.println("****************** 支付宝同步通知成功   ******************");
            System.out.println("同步通知返回参数:" + params.toString());
            System.out.println("****************** 支付宝同步通知成功   ******************");

            String outTradeNo = params.get("out_trade_no");
            OrderDetailEntity detail = orderDetailMapper.getByNo(outTradeNo);
            detail.setStatus(200);
            orderDetailMapper.updateByNo(200,outTradeNo);


        } else {
            System.out.println("支付, 验签失败...");
        }

        // 返回支付操作完成后需要跳转的页面,这里把返回的参数直接传给页面
        return params.toString();
    }

}

🍹三、项目部署

        使用 maven 快速打包,这里已经将支付完成之后自动跳转的地址换成服务器地址了,另外本地项目中有操作数据库表的部分,也已经在服务器上的数据库中进行了同步,只需要将项目部署到服务器即可!

        本地连接到服务器后,将打包好的jar包上传到服务器

         java -jar 命令,项目启动成功!

        由于是服务器上部署的,而大部端口默认是禁止外部 ip 访问的,所以需要将该项目使用的端口号(8890)开放,可以在防火墙设置里面添加规则: 

        以上步骤完成之后,开发+部署就算完成了!

🍸四、测试

        浏览器输入一下地址(访问支付接口,传递了两个参数),会跳出如下页面,可以看到传入的参数在页面上都有显示,订单名称和金额,以及收款账户

http://118.*.*.*:8890/alipay/pay?subject=测试商品&total_amount=666

        可以在沙箱环境控制台获取到商户的信息和测试账户(用来付钱的账户)信息,如下:

         输入账号密码(测试账号的,不是商家的),会自动跳转到付款成功页面,然后跳转到自定义的页面(发送支付请求的时候,传的地址)

         支付成功之后可以看到,商家的余额增加了 666,对应用户的金额减少了 666

        到服务器上的数据库看下数据,如下,订单状态成功更改为200,操作时间是在订单提交后的9秒后,说明从我们发起支付请求后到支付宝处理完成,回调我们提供的接口花费不到十秒钟;在服务器的项目日志中也可以查看详细的回调参数(做了打印)

 

💞️ 五、章末

        文章到这里就结束了~

相关文章:

  • Redhat及其衍生系统安装python
  • 如何使用wolfsll库生成证书?
  • Xcode如何高效的一键重命名某个关键字
  • SOME/IP--协议英文原文讲解12(完结)
  • 【Git版本控制器】第四弹——分支管理,合并冲突,--no-ff,git stash
  • 大语言模型(LLM)提示词(Prompt)高阶撰写指南
  • 《论面向对象的建模及应用》审题技巧 - 系统架构设计师
  • Onvif协议NVR开发方案指南
  • SQL笔记#数据更新
  • [AI] [ComfyUI]理解ComyUI的基本原理及其图像生成技术
  • 深度学习技术全景图:从基础架构到工业落地的超级进化指南
  • flex布局自定义一行几栏,靠左对齐===grid布局
  • 三维空间中直线的多种表示方法
  • Python 基本语法的详细解释
  • 第1章大型互联网公司的基础架构——1.9 LSM Tree
  • 【每日八股】计算机网络篇(一):概述
  • 广东英语十二种应用文模版范文
  • 关于在mac中配置Java系统环境变量
  • MyBatis的CRUD
  • 2025最新版!Fiddler抓包实战:深度解析短视频评论采集技术
  • 盐城有没有做公司网站/网站推广优化的方法
  • 广西网站建设证件查询/百度合伙人官网app
  • 物流商 网站建设方案/企业管理培训免费课程
  • 施工企业市场经营工作思路及措施/游戏优化是什么意思?
  • 防伪码做网站的还能没导入吗/网络市场调研的方法
  • 重庆网站开发/推荐友情链接