支付宝API支付使用文档--java《扫一扫支付》--demo但是封装高可用--改一改配置就拿走用!超详细!!有手就行!!!--spring +vue-调用支付宝的当面付的预创建接口

2023-11-16

支付宝API支付使用文档--java《扫一扫支付》--demo但是封装高可用--改一改配置就拿走用!超详细!!有手就行!!!(上)修改官网配置类_一单成的博客-CSDN博客

阿丹:

        上一篇文章具体的描述和讲解了官方提供的配置类,以及如何使用注册开发沙箱,本篇文章重点描述和讲解如何实现代码的编写,以及具体实现的流程,以及数据的流向。重点是将开发的过程记录,并最后封装为工具类方便大家开发。尽量做到拿取代码修改配置即可使用。

阿丹提醒:

        请在阅读本文章之前先详细查看阿丹的效果演示说明以及本文章中需要的技术栈以及核心思想。以及该技术点的特性。

 展示为网站和手机进行支付交互。演示图片中的二维码为一次性扫描的二维码。

解释说明:

        本代码或者说例子是用在当面付的预创建接口

        构建以及使用这个类:AlipayTradePrecreateRequest

开发思路:

在上次开发的时候我们发现有这两个地址没有写,并且会有疑惑我扫描二维码的页面哪里来呢?

不要着急我在后面会进行说明的。

实现思路以及流程图梳理

在文章中我就不在详解加签等操作了(之前已经完成了封装),我们主要讲解一下,专注于业务的流程以及数据流向。

 我将数据流向分为了三条线方便大家,梳理,理解

黑:

        1、用户进入结算付款页面

        2、根据用户订单信息构建PayReq实体类,携带实体类访问请求/createQR接口

        3、与服务/bindingRecord建立websocket连接(用于监听支付信息/状态)

红:

        1、/createQR接口生成支付二维码,通过返回response结果的方式来在vue页面使用弹框展示。

        2、用户使用扫一扫扫码支付

        3、用户手机请求到支付宝应用

绿:

        1、支付宝给我们的系统发送支付状态信息,请求调用了/call接口

        2、call接口向前台发送支付完成信息完成支付!!

我们开始上代码!!

后台代码:

        梳理其实实现很简单。我们需要一个生成二维码的接口、一个websocket接口、一个回调函数(获取支付状态)

相关配置以及使用沙箱说明在这里拿取!!!!!

 用于websocekt连接!!

package com.adn.controller;


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 *  WebSocket服务
 *
 **/
//连接webSocket服务的URL
@ServerEndpoint("/bindingRecord")
@Component
@Slf4j
public class WebSocket {

    private Session session;
    private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();

    /**
     * 新建webSocket配置类
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
    /**
     * 建立连接
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSockets.add(this);
        log.info("【新建连接】,连接总数:{}", webSockets.size());
    }

    /**
     * 断开连接
     */
    @OnClose
    public void onClose(){
        webSockets.remove(this);
        log.info("【断开连接】,连接总数:{}", webSockets.size());
    }

    /**
     * 接收到信息
     * @param message
     */
    @OnMessage
    public void onMessage(String message){
        log.info("【收到】,客户端的信息:{},连接总数:{}", message, webSockets.size());
    }

    /**
     * 发送消息
     * @param message
     */
    public void sendMessage(String message){
        log.info("【广播发送】,信息:{},总连接数:{}", message, webSockets.size());
        for (WebSocket webSocket : webSockets) {
            try {
                webSocket.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.info("【广播发送】,信息异常:{}", e.fillInStackTrace());
            }
        }
    }

}

 代码解释:

这段代码是一个WebSocket服务实现类。它使用了Spring framework提供的注解和依赖项,可以轻松地创建一个WebSocket服务器端点。

主要的注解包括:

  • @ServerEndpoint("/bindingRecord"):指定WebSocket的URL地址,客户端将通过这个地址连接到WebSocket服务器。
  • @Component:将该类声明为Spring组件,可以被Spring容器管理。
  • @OnOpen@OnClose@OnMessage:这些注解用于定义WebSocket的生命周期回调方法。
  • @Bean:这个注解用于表明serverEndpointExporter()方法返回的实例将被Spring容器管理,用于将WebSocket的相关配置发布为服务器的端点。

该类还包括一些实例变量和方法,用于处理WebSocket连接和消息的收发:

  • session:表示当前连接的WebSocket会话。
  • webSockets:一个用于存储所有连接到WebSocket服务器的WebSocket实例的并发集合。
  • onOpen():建立连接时调用的方法,将新的WebSocket实例添加到webSockets集合中。
  • onClose():断开连接时调用的方法,从webSockets集合中移除当前WebSocket实例。
  • onMessage():收到客户端发送的消息时调用的方法,在控制台打印消息和连接总数。
  • sendMessage():用于广播消息给所有连接的客户端,使用getBasicRemote().sendText(message)方法发送消息。

总的来说,这段代码实现了一个简单的WebSocket服务端,可以与客户端建立连接并进行消息的收发。

请求实体类!

package com.adn.common.domain.request;

import lombok.Data;

/**
 * 支付请求实体类
 * */
@Data
public class PayReq {
    //商品订单号
    private String out_trade_no;
    //订单名称
    private String subject;
    //付款金额
    private String total_amount;
    //商品订单描述
    private String body;
}

商品订单号、订单名称、付款金额、商品描述这几个参数只有商品订单描述可以选填,其他的都不能为空!!

后台请求

/createQR、/call接口书写

package com.adn.controller;

import com.adn.common.domain.request.PayReq;
import com.adn.common.domain.response.AliReturnPayBean;
import com.adn.common.result.Result;
import com.adn.common.utils.AppUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import lombok.extern.log4j.Log4j2;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
@Log4j2
public class PayController {
    @Resource
    private AlipayClient alipayClient;
    @Resource
    private AlipayTradePagePayRequest alipayTradePagePayRequest;

    @Autowired
    private AppUtil appUtil;

    @Autowired
    private WebSocket webSocket;


    @RequestMapping("/createQR")
    public Result send(@RequestBody PayReq req) {
        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); //创建API对应的request/0/ 类
//        request.setNotifyUrl("http://cv95x3.natappfree.cc/call");
        System.out.println(req.toString());
        request.setNotifyUrl(appUtil.getNotifyurl());
        //同步回调地址
//        request.setReturnUrl("");
        request.setBizContent("{\"out_trade_no\":\""+ req.getOut_trade_no() +"\","
                + "\"total_amount\":\""+ req.getTotal_amount() +"\","
                + "\"subject\":\""+ req.getSubject() +"\","
                + "\"body\":\""+ req.getBody() +"\","
                + "\"product_code\":\"FACE_TO_FACE_PAYMENT\"}");
        AlipayTradePrecreateResponse response = null;
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
        if (response.isSuccess()) {
            System.out.println("调用成功");
            //获取二维码
            return Result.success(response.getQrCode());
        } else {
            System.out.println("调用失败");
        }
        return Result.error();
    }


    /**
     * 支付宝回调函数
     * @param request
     * @param response
     * @param returnPay
     * @throws IOException
     */
    @RequestMapping("/call")
    public void call(HttpServletRequest request, HttpServletResponse response, AliReturnPayBean returnPay) throws IOException {
        response.setContentType("type=text/html;charset=UTF-8");
        log.info("支付宝的的回调函数被调用");
        if (!appUtil.checkSign(request)) {
            log.info("验签失败");
            response.getWriter().write("failture");
            return;
        }
        if (returnPay == null) {
            log.info("支付宝的returnPay返回为空");
            response.getWriter().write("success");
            return;
        }
        log.info("支付宝的returnPay" + returnPay.toString());
        //表示支付成功状态下的操作
        if (returnPay.getTrade_status().equals("TRADE_SUCCESS")) {
            log.info("支付宝的支付状态为TRADE_SUCCESS");
            //业务逻辑处理 ,webSocket在下面会有介绍配置
            webSocket.sendMessage("true");
        }
        response.getWriter().write("success");
    }
}

代码解释:

        这就是重要的两个的请求的端口路径,那么在这里还有一个重点

这个参数就是面对面的支付的重要参数,在使用前可以去官网上先确定一下相关的状态码。

特别重点!!!

在这里写的路径call其实就是我们之前提到的!配置文件中没有写的!!同步通知地址!!

在我们写过了这个路径的时候要配置中的回调路径地址写上!!!

同时要注意这个地址一定是外网能够访问的!!如果是在本地的开发环境,那就需要去百度一下内网穿透了,要注意这里的配置,在上线(部署服务器之后!!)要将路径的地址改为对应服务器的ip地址。

前台vue页面!!!!

封装的请求:

import request from '@/utils/request'

export function getpay(data) {
  return request({
    url: '/createQR',
    method: 'post',
    data: data
  })
}

 在main.js导入依赖

下载依赖

npm install vue-qr

导入使用依赖 

import vueQr from 'vue-qr'
Vue.component('vueQr', vueQr)

vue完整页面

<template>
  <div>
    <el-form ref="form" :model="payform" label-width="80px">
      <el-form-item label="商户订单号">
        <el-input v-model="payform.out_trade_no"></el-input>
      </el-form-item>
      <el-form-item label="订单名称">
        <el-input v-model="payform.subject"></el-input>
      </el-form-item>
      <el-form-item label="付款金额">
        <el-input v-model="payform.total_amount"></el-input>
      </el-form-item>
      <el-form-item label="商品描述">
        <el-input v-model="payform.body"></el-input>
      </el-form-item>
      <!-- 支付按钮,模拟支付操作 -->
      <van-button type="primary" @click="pay">支付</van-button>

      <el-dialog :title="paySucc?'支付成功':'扫码支付'" :visible.sync="dialogVisible" width="20%" center>
        <!-- 生成二维码图片 -->
        <vueQr :text="text" :size="200" v-if="!paySucc"></vueQr>
        <!-- 使用websocket监控是否扫描,扫描成功显示成功并退出界面 -->
        <span class="iconfont icon-success" style="position: relative;font-size: 100px;color:#42B983;margin-left: 50px;top:-10px;" v-else></span>
      </el-dialog>
    </el-form>
<!--    <el-dialog-->
<!--      title="提示"-->
<!--      :visible.sync="dialogVisible"-->
<!--      width="80%"-->
<!--      hidden="80%"-->
<!--      :before-close="handleClose">-->

<!--      <span slot="footer" class="dialog-footer">-->
<!--    <el-button @click="dialogVisible = false">取 消</el-button>-->
<!--    <el-button type="primary" @click="dialogVisible = false">确 定</el-button>-->
<!--  </span>-->
<!--    </el-dialog>-->
<!--    <div v-html="this.htmlstr"></div>-->
<!--  </div>-->
  </div>
</template>

<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等),
//例如:import 《组件名称》 from '《组件路径》,
  import { getpay } from '@/api/pay'
  import axios  from 'axios'
  import vueQr from 'vue-qr'
export default {
    //import引入的组件需要注入到对象中才能使用"
    components: {

    },
    props: {},
    data() {
      //这里存放数据"

      return {
        text: "",
        paySucc: false,
        dialogVisible:false,
        payform:{
          out_trade_no:'',
          subject:'',
          total_amount:'',
          body:'',
        },
        htmlstr:''
      };
    },
    //计算属性 类似于data概念",
    computed: {},
    //监控data中的数据变化",
    watch: {},
    //方法集合",
    methods: {
      handleClose(done) {
        this.$confirm('确认关闭?')
          .then(_ => {
            done();
          })
          .catch(_ => {
          });
      },
      paymany() {
        getpay(this.pay).then(
          res => {
            console.log(res)
            this.htmlstr = res.data
            this.dialogVisible = true
          }
        )
      },
      pay() {
        let _this = this;
        _this.paySucc = false;
        _this.dialogVisible = true;
        console.log(this.payform)
        getpay(this.payform).then(
          response=>{
            console.log(response)
            _this.text = response.data;
            _this.dialogVisible = true;
            //使用webSocket发送请求,下面会简单介绍websocket使用
            if ("WebSocket" in window) {
              // 打开一个 web socket
              var ws = new WebSocket("ws://服务器的ip:10001/bindingRecord");

              ws.onopen = function() {
                // Web Socket 已连接上,使用 send() 方法发送数据
                // ws.send("data");
                // alert("数据发送中...");
              };

              ws.onmessage = function(evt) {
                var received_msg = evt.data;
                // alert("数据已接收..." + evt.data);
                if (Boolean(evt.data)) {
                  _this.paySucc = true;
                  setTimeout(() => {
                    _this.dialogVisible = false;
                  }, 3 * 1000);
                }
                ws.close();

              };

              ws.onclose = function() {
                // // 关闭 websocket
                console.log("连接已关闭...");
              };
            } else {
              // 浏览器不支持 WebSocket
              alert("您的浏览器不支持 WebSocket!");
            }
          }).catch((err) => {
          console.log(err)
        })
      },
      back(dataUrl, id) {
        console.log(dataUrl, id)
      }
    },
    //生命周期 - 创建完成(可以访问当前this实例)",
    created() {
      alert("预支付效果演示!请注意本页面所有支付均为支付宝开发沙盒,如果支付了,钱可没到阿丹这里!!!")
      this.axios = axios; // 初始化 axios
    },
    //生命周期 - 挂载完成(可以访问DOM元素)",
    mounted() {
    },
    beforeCreate() {
    }, //生命周期 - 创建之前",
    beforeMount() {
    }, //生命周期 - 挂载之前",
    beforeUpdate() {
    }, //生命周期 - 更新之前",
    updated() {
    }, //生命周期 - 更新之后",
    beforeDestroy() {
    }, //生命周期 - 销毁之前",
    destroyed() {
    }, //生命周期 - 销毁完成",
    activated() {
    } //如果页面有keep-alive缓存功能,这个函数会触发",
  };
</script>
<style scoped>

</style>

注意:

        别忘记配置路由!!!并且我使用的是npm来管理依赖的,一定要注意!!

然后就完成了快自己动手尝试一下吧!!!!!

没看错!return_url就是空着了,大家可以写一个空“”来代替,好了大家快自己尝试一下吧!!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

支付宝API支付使用文档--java《扫一扫支付》--demo但是封装高可用--改一改配置就拿走用!超详细!!有手就行!!!--spring +vue-调用支付宝的当面付的预创建接口 的相关文章

随机推荐

  • nginx 反向代理 ssh连接办公室win电脑

    1 生产密钥免密连接服务器 ssh keygen 复制win电脑上的 ssh id rsa pu到服务器 root ssh 目录下 cat id rsa pub gt gt authorized keys sudo chmod 600 au
  • windows11安装wsl2(linux)+VScode+ miniconda+TVM+python环境部署

    在wsl上运行 TVM demo 环境配置 预备加速工具 vpn 清华镜像 第一次安装 失败 1 Windows11 D盘安装wls linux 2 安装vscode并连接到WSL2 3 wsl 安装miniconda 4 wls安装TVM
  • JavaSE基础学习

    JavaSE基础笔记 锦苏的个人笔记 首先声明 这个笔记是根据韩顺平老师 讲的Java课程全程来学习并且做笔记的 如果各位小伙伴们 需要从事Java行业那么从开始决定学习了 就不要放弃 三天打鱼两天晒网这是我的真实写照 我希望不是大家的真实
  • openwrt恢复默认设置

    如果通过无线或者有线口无法连接到router 可以用恢复某些设置重新设置路由器 1 开机 等着一个工作灯亮的时候立即按下rest键2秒 然后就开始拼命闪烁 很好现在进入failsafe模式了 2 设置电脑的ip为静态ip 192 168 1
  • 数据库中的binlog、redolog、undolog的区别

    binlog二进制日志是mysql server层的 主要是做主从复制 时间点恢复使用 redo log重做日志是InnoDB存储引擎层的 用来保证事务安全 undo log回滚日志保存了事务发生之前的数据的一个版本 可以用于回滚 同时可以
  • 线程池之阻塞主线程,等子线程全部跑完之后,执行主线程

    在spark中有异步多线程的需求 需要阻塞主线程 等所有子线程都执行完成后 主线程继续执行 如果是用Thread 不太好实现 用Callable FutrueTask结合线程池 可以快速实现 final AtomicInteger mess
  • 基于Java+SSM项目的计算机毕业设计-毕业生离校管理系统项目实战(附论文+源码)

    大家好 我是职场程序猿 感谢您阅读本文 欢迎一键三连哦 当前专栏 Java毕业设计 精彩专栏推荐 安卓app毕业设计 微信小程序毕业设计 演示视频 ssm147毕业生离校离校管理系统演示 源码下载地址 https download csdn
  • 数论算法:唯一因子分解定理

    这里讲一下算法中常用到的唯一因子分解定理 合数a仅能以一种方式写成如下乘积形式 a p1 e1 p2 e2 pr er 其中pi为素数 p1
  • Vue基本知识1,安装创建以及常用指令

    Vue基本知识1 安装创建以及常用指令 1 Vue的概念 概念 vue是一个渐进式的JavaScript开发框架 基于MVVM实现数据驱动的框架 官网 还可以用来做SPA single page web application 一个网页就是
  • Qt udp数据发送太快,数据丢失

    Qt 在单线程中 如果数据发送太快 应用程序还在处理别的程序 就会触发不了readyRead信号 导致造成数据的丢失 解决方法可以将udp处理类继承于QThread 在多线程入口run函数里通过死循环接收数据 通过信号槽机制通知处理函数进行
  • 微信小程序&PHP 改变小程序码中间logo的方法

    需求 获取小程序码 小程序码中间logo换成用户头像 仔细看了小程序本身的程序 没有发现好的方法 如果有 不吝赐教呀 所以改变方法 把头像传回后台 使用 php gd库在后台操作 然后传回小程序端 初始数据准备 define PATH op
  • Hive的联级(cascade)- 修改分区表的字段类型或者增加新字段

    一 问题描述 踩坑 数仓的分区表 由于需求需要 要把int类型的字段改为bigint 我直接执行的以下语句 alter table table name change column 字段 字段 bigint 出现的问题 之后的分区数据可以正
  • 经济复苏!!! ReportLinker调整2020-2027 IMU市场预测金额至335亿美元

    2022年10月 市场研究机构ReportLinker发布了2020 2027全球IMU市场预测报告 报告预测到2027年全球IMU市场金额将达到236亿美元 2020 2027年期间复合增长率为5 4 新冠过后 经济复苏 ReportLi
  • Python同时显示多张图片在一个画面中(两种方法)

    很多时候需要把很多图片同时显示到一个画面中 现在分享两个方法 这里我恰好拿之前写的爬取网上图片保存到本地的爬虫模型爬一些图片作为素材Python 爬虫批量爬取网页图片保存到本地 得到素材如下所示 现在让这些图片同时显示 方法一 subplo
  • [论文笔记]知识图谱+推荐系统

    仅作个人笔记 2021 3 22 2021 3 29 1 RippleNet Propagating User Preferences on the Knowledge Graph for Recommender Systems 看到一篇翻
  • 深入理解程序设计使用Linux汇编语言

    关于函数 4 1 由系统提供的函数称为原函数 或源语 因为其他一切都是建立在它们之上 在汇编语言中 原语通常就是系统调用 4 3 由于内存的结构 栈是从内存顶部开始向下增长的 当我们提到 栈顶 请记住这是栈内存的底部 movl esp ea
  • C语言 - 制作一个可以容纳一千人的本地通讯录

    本章目录 前言 一 菜单制作 二 创建通讯录 1 创建人员信息结构体 2 创建完整通讯录结构体 3 初始化通讯录 4 存放数据 5 判断空间容量 三 各功能的实现 1 增加人员信息的功能 2 按名字查找的功能 内部使用 3 删除人员信息的功
  • 手把手教你CIFAR数据集可视化

    CIFAR数据集介绍与获取 如同从小到的父母教我们识别每个物体是什么一样 除了看到的画面 父母会在旁边告诉看到的画面是什么 这种学习方式叫做监督学习 与此对应还有无监督学习 计算机也一样 数据集通常应该至少包含两部分内容 一个是图像 一个是
  • C语言:三目运算符 “?”号

    三目运算符的表示一般为 该运算符连接3个对象 是C语言中唯一一个三目运算符 又称条件运算符 它的一般形式如下 表达式a 表达式b 表达式c 其执行步骤如下 1 计算表达式a的值 2 如果表达式a的值为1 则执行表达式b 3 如果表达式b的值
  • 支付宝API支付使用文档--java《扫一扫支付》--demo但是封装高可用--改一改配置就拿走用!超详细!!有手就行!!!--spring +vue-调用支付宝的当面付的预创建接口

    支付宝API支付使用文档 java 扫一扫支付 demo但是封装高可用 改一改配置就拿走用 超详细 有手就行 上 修改官网配置类 一单成的博客 CSDN博客 阿丹 上一篇文章具体的描述和讲解了官方提供的配置类 以及如何使用注册开发沙箱 本篇