SpingBoot的项目实战--模拟电商【5.沙箱支付】

🥳🥳Welcome Huihui's Code World ! !🥳🥳

接下来看看由辉辉所写的关于SpringBoot电商项目的相关操作吧 

目录

🥳🥳Welcome Huihui's Code World ! !🥳🥳

一. 沙箱支付是什么

二.SpringBoot项目集成沙箱支付 

 1.进入支付宝开放平台

2.进入控制台

(1)找到沙箱

(2)设置公钥

(3)生成密钥

①下载密钥生成工具

②使用密钥生成工具生成密钥

3.Java集成沙箱支付

(1)进入文档中心

(2)下载依赖

(3)Alipay Easy SDK API 总览

(4)配置类AplipayConfig 

(5)编写controller 

(6)支付测试 


因为我们写的这个项目是用来练手的,用来熟练技术点的,所以自然也不能接入支付宝的接口【没有营业执照这些证件】。但是支付宝也提供了可以供我们开发使用的接口--沙箱支付

一. 沙箱支付是什么

        沙箱支付(Sandbox payment)是指在软件开发过程中用于模拟真实支付环境的测试工具。它提供了一个安全的环境,允许开发人员在不涉及真实货币交易的情况下进行支付系统的测试和调试。

        通常,沙箱支付系统会模拟真实支付系统的功能和流程,但使用虚拟货币或测试帐户来完成支付过程。这样,开发人员可以在没有真实金钱交易的情况下验证他们的支付代码是否正常工作,并进行必要的调试和优化。

        沙箱支付对于开发支付相关应用程序和网站非常有用,因为它们可以在不影响真实用户和真实支付的情况下进行测试和调试。通过使用沙箱支付,开发人员可以确保应用程序在正式上线之前能够正确处理支付事务,减少潜在的错误和风险。

需要注意的是,沙箱支付只用于开发和测试目的,不是真实的支付系统,不能进行真实的交易操作。

二.SpringBoot项目集成沙箱支付 

 1.进入支付宝开放平台

支付宝开放平台icon-default.png?t=N7T8https://openhome.alipay.com/

如果说没有支付宝账号的话,需要进行注册

有的话直接登录就好了

2.进入控制台

(1)找到沙箱

往下滑找到沙箱

(2)设置公钥

因为我们再写的是一个模拟的电商项目,那么我们就也模拟一下商户和用户之间的交易--那么我们就需要用到自定义密钥

系统密钥与自定义密钥区别:

  1. 系统密钥(System Key):系统密钥是支付宝沙箱环境中的全局密钥,由支付宝提供。系统密钥用于对数据进行加密和签名,以确保数据的安全性和完整性。开发者在使用支付宝沙箱支付时,需要使用正确的系统密钥来进行数据加密和验证。

  2. 自定义密钥(Custom Key):自定义密钥是开发者在支付宝沙箱环境中创建和管理的密钥,用于模拟真实商户的身份和权限。开发者可以在支付宝开放平台上创建自定义密钥,并将其用于沙箱环境中的支付请求和验证。自定义密钥用于模拟真实商户的身份认证和数据签名。

(3)生成密钥

①下载密钥生成工具

②使用密钥生成工具生成密钥

将生成的密钥中的公钥填入即可

然后我们就可以查看到支付宝公钥了

点击查看也是一样可以查看到的

3.Java集成沙箱支付

(1)进入文档中心

支付宝文档中心icon-default.png?t=N7T8https://opendocs.alipay.com/common/找到对应的SDK【软件开发工具包(Software Development Kit)】,这里我选择的是easy版,因为这个项目不是上线的项目,没有那么庞大的数据和复杂的场景

Java通用版与Java Easy版的区别:

  1. Java通用版(General Version):Java通用版是基于支付宝开放平台提供的Java SDK开发的,适用于更为复杂的场景和高度自定义需求。使用Java通用版可以更灵活地进行开发,但相应地也需要更多的配置和代码编写。开发者需要根据支付宝提供的API文档,手动构建请求参数、进行签名、发送请求并处理响应等操作。

  2. Java Easy版:Java Easy版是基于支付宝提供的简化SDK开发的,旨在降低开发门槛,简化开发过程。Java Easy版封装了一些常用的功能和操作,提供了更简单、易用的接口,并自动处理一些繁琐的步骤,如参数构建、签名等。使用Java Easy版可以快速上手并进行基本的沙箱支付测试,减少了开发者的工作量。


(2)下载依赖

<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-easysdk</artifactId>
    <version>2.2.0</version>
</dependency>

(3)Alipay Easy SDK API 总览

这里是其中提供的aip接口,大家也可以通过网站链接访问

Alipay Easy SDK API 总览icon-default.png?t=N7T8https://opendocs.alipay.com/common/02n6z6?pathHash=f5e2a056

能力类别

场景类别

接口方法名称

Base(基础能力)

OAuth(用户授权)

getToken(获取授权访问令牌和用户 user_id)

Base(基础能力)

OAuth(用户授权)

refreshToken(刷新授权访问令牌)

Base(基础能力)

Qrcode(小程序二维码)

create(创建小程序二维码)

Base(基础能力)

Image(图片)

upload(上传门店照片)

Base(基础能力)

Video(视频)

upload(上传门店视频)

Member(会员能力)

Identification(支付宝身份认证)

init(身份认证初始化)

Member(会员能力)

Identification(支付宝身份认证)

certify(生成认证链接)

Member(会员能力)

Identification(支付宝身份认证)

query(身份认证记录查询)

Payment(支付能力)

Common(通用)

create(创建交易)

Payment(支付能力)

Common(通用)

query(查询交易)

Payment(支付能力)

Common(通用)

refund(交易退款)

Payment(支付能力)

Common(通用)

close(关闭交易)

Payment(支付能力)

Common(通用)

cancel(撤销交易)

Payment(支付能力)

Common(通用)

queryRefund(交易退款查询)

Payment(支付能力)

Common(通用)

downloadBill(查询对账单下载地址)

Payment(支付能力)

Common(通用)

verifyNotify(异步通知验签)

Payment(支付能力)

Huabei(花呗分期)

create(创建花呗分期交易)

Payment(支付能力)

FaceToFace(当面付)

pay(扫用户出示的付款码,完成付款)

Payment(支付能力)

FaceToFace(当面付)

precreate(生成交易付款码,待用户扫码付款)

Payment(支付能力)

App(手机APP)

pay(生成订单串,再使用客户端 SDK 凭此串唤起支付宝收银台)

Payment(支付能力)

Page(电脑网站)

pay(生成交易表单,渲染后自动跳转支付宝网站引导用户完成支付)

Payment(支付能力)

Wap(手机网站)

pay(生成交易表单,渲染后自动跳转支付宝网站引导用户完成支付)

Security(安全能力)

TextRisk(文本内容安全)

detect(检测内容风险)

Marketing(营销能力)

Pass(支付宝卡包)

createTemplate(卡券模板创建)

Marketing(营销能力)

Pass(支付宝卡包)

updateTemplate(卡券模板更新)

Marketing(营销能力)

Pass(支付宝卡包)

addInstance(卡券实例发放)

Marketing(营销能力)

Pass(支付宝卡包)

updateInstance(卡券实例更新)

Marketing(营销能力)

TemplateMessage(小程序模板消息)

send(发送模板消息)

Marketing(营销能力)

OpenLife(生活号)

createImageTextContent(创建图文消息内容)

Marketing(营销能力)

OpenLife(生活号)

modifyImageTextContent(更新图文消息内容)

Marketing(营销能力)

OpenLife(生活号)

sendText(群发本文消息)

Marketing(营销能力)

OpenLife(生活号)

sendImageText(群发图文消息)

Marketing(营销能力)

OpenLife(生活号)

sendSingleMessage(单发模板消息)

Marketing(营销能力)

OpenLife(生活号)

recallMessage(生活号消息撤回)

Marketing(营销能力)

OpenLife(生活号)

setIndustry(模板消息行业设置)

Marketing(营销能力)

OpenLife(生活号)

getIndustry(生活号查询行业设置)

Util(辅助工具)

AES(加解密)

decrypt(解密,常用于会员手机号解密)

Util(辅助工具)

AES(加解密)

encrypt(加密)

Util(辅助工具)

Generic(通用接口)

execute(自行拼接参数,执行OpenAPI调用)

Util(辅助工具)

Generic(通用接口)

sdkExecute(自行拼接参数,生成加签串,功能等同于通用版 SDK 中的 sdkExecute 方法)

(4)配置类AplipayConfig 

其中的appid

支付宝公钥

应用私钥

应用私钥是在我们的密钥生成工具中

package com.wh.easyshop.util;

import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.zking.starshop.pojo.Order;
import org.springframework.stereotype.Component;

@Component
public class AlipayConfig {

    private Config aliconfig() {
        Config config = new Config();
        //沙箱支付宝地址
        config.gatewayHost = "openapi-sandbox.dl.alipaydev.com";
        //协议https
        config.protocol = "https";
        //应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
        config.appId = "appId ";
        //支付宝公钥
        config.alipayPublicKey = "支付宝公钥";
        //签名方式
        config.signType = "RSA2";
        //商户私钥(应用私钥),您的PKCS8格式RSA2私钥
        config.merchantPrivateKey ="应用私钥";
        return config;
    }

   

}

需要修改沙箱支付的支付方式,改成网页支付:

public String goAlipay(Order order) {
        try {
            // 1. 设置参数(全局只需设置一次)
            Factory.setOptions(aliconfig());
            // 2. 发起API调用(subject商品标题、outTradeNo订单编号、totalAmount总金额、returnUrl异步通知地址)
            AlipayTradePagePayResponse response = Factory.Payment.Page()
                    .pay("SpringBoot练手项目收款",
                            order.getOid().toString(),//订单编号
                            order.getTotal().toString(),//总金额
                            //支付成功之后的异步通知(跳出到自己系统的哪个位置)
                            "http://localhost:8080/order/payDone");
            return response.body;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

(5)编写controller 

然后再前端接收到这个订单编号,并且把这个id带到指定的路径中

 $.post('/order/addOrder',order,resp=>{
        if(resp.code===200){
          let f=confirm("确认支付吗");
          if(f){
        location.href="/order/pay?oid="+resp.data;
          }else{
        location.href="/";
          }
        }
    },'json')

指定的路径:此方法用查询订单,然后把订单的信息带到支付页面中

这个是在配置类中缩写的支付成功之后的异步通知

package com.wh.easyshop.controller;

import com.alipay.easysdk.factory.Factory;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.yitter.idgen.YitIdHelper;
import com.wh.easyshop.exception.BusinessException;
import com.wh.easyshop.model.Goods;
import com.wh.easyshop.model.Order;
import com.wh.easyshop.model.OrderItem;
import com.wh.easyshop.model.User;
import com.wh.easyshop.resp.JsonResponseBody;
import com.wh.easyshop.resp.JsonResponseStatus;
import com.wh.easyshop.service.IGoodsService;
import com.wh.easyshop.service.IOrderItemService;
import com.wh.easyshop.service.IOrderService;
import com.wh.easyshop.service.IRedisService;
import com.wh.easyshop.util.AlipayConfig;
import com.wh.easyshop.vo.CartVo;
import com.wh.easyshop.vo.OrderVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单信息表 前端控制器
 * </p>
 *
 * @author wh
 * @since 2023-12-27
 */
@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    IRedisService redisService;
    @Autowired
    IGoodsService goodsService;
    @Autowired
    IOrderItemService iOrderItemService;
    @Autowired
    IOrderService iOrderService;

    @RequestMapping("/addOrder")
    public JsonResponseBody<?> order (User user, OrderVo orderVo) {
        //拿到其中的商品id
        String ids = orderVo.getIds();
        //拿到购物车中指定的商品【数量和id】
        List<CartVo> cartVos = redisService.loadCart(user, ids);
        List<Long> ds = cartVos.stream().map(CartVo::getGid).collect(Collectors.toList());
        //拿到数据库中的内容【名字,价格】
        List<Goods> goods = goodsService.listByIds(ds);
        //遍历集合 赋值给对应的对象
        for (Goods g : goods) {
            //找到对应id相同的元素
            CartVo vo = cartVos.stream()
                    .filter(v -> Objects.equals(v.getGid(), g.getGid()))
                    .findFirst()
                    .get();
            //将商品g的属性赋值给vo【这样vo中的属性就有数据了】
            BeanUtils.copyProperties(g,vo);
        }
        //订单中的总价
        BigDecimal total =new BigDecimal(0);
        //生成订单详情
        //需要订单id【雪花id生成】
        long oid = YitIdHelper.nextId();
        //使用集合将需要加进去的订单详情装起来【使用批处理一次性处理掉】
        List<OrderItem> orderItemList=new ArrayList<>();
        for (CartVo cartVo : cartVos) {
        //订单详情
            OrderItem orderItem = new OrderItem();
            //将vo类的属性赋值给orderitem【共有的属性赋值】
            BeanUtils.copyProperties(cartVo,orderItem);
            //还有一个数量的值没有赋成功【因为两个实体中的这个属性名字不一样】
            orderItem.setQuantity(cartVo.getNum());
            //设置订单详情的id
            orderItem.setOoid(YitIdHelper.nextId());
            //设置订单id
            orderItem.setOid(oid);
            //将订单详情加入到订单详情的集合中
            orderItemList.add(orderItem);
            //计算总价
            total=total.add(cartVo.xj());
        }
        //将生成的额订单详情加入到数据库中
        iOrderItemService.saveBatch(orderItemList,5);
        //生成订单
        Order order = new Order();
        //将其中的ordervo的属性赋值给order
        BeanUtils.copyProperties(orderVo,order);
        //订单id
        order.setOid(oid);
        //总价
        order.setTotal(total);
        //用户id
        order.setUserId(user.getId());
        //订单生成时间
        order.setCreateDate(new Date());
        //订单状态【0 未支付】
        order.setStatus(0);
        //将生成的订单加入到数据库
        iOrderService.save(order);
        //订单生成成功之后需要将购物车中的信息删除
        String ids1 = orderVo.getIds();
        List<String> delids = Arrays.asList(ids1.split(","));
        redisService.delCart(delids,user);
        return JsonResponseBody.success(oid);
    }



    @RequestMapping("/pay")
    public String order (User user, String oid) {
        //根据传递过来的订单编号拿到订单对象
        Order order = iOrderService.getById(oid);
        AlipayConfig alipayConfig = new AlipayConfig();
        String body= alipayConfig.goAlipay(order);//表单内容
        return body;
    }

    /**
     * 支付成功之后的回调路径
     * @param ms
     * @return
     * @throws Exception
     */
    @RequestMapping("/payDone")
    @ResponseBody
    //@RequestParam没有这个注解就无法正确的接受参数
    public String payDone(@RequestParam Map<String, String> ms) throws Exception {
        //验签【不然的话就可以在地址栏上手动传递参数】
        Boolean f = Factory.Payment.Common().verifyNotify(ms);
        //如果验证不通过,那就抛出一个异常
        if(!f){
            throw  new BusinessException(JsonResponseStatus.UN_KNOWN);
        }
        String oid = ms.get("out_trade_no");//拿到请求中的订单编号
        iOrderService.update(new UpdateWrapper<Order>()
                //根据订单编号,修改订单状态【未支付=>已支付】
                .eq("oid", oid)
                .set("status", 1)
                .set("pay_date", new Date())//设置支付的时间
        );
        return "<script>\n" +
                "    alert('支付成功')\n" +
                "    location.href = '/'\n" +
                "</script>";
    }


}

(6)支付测试 

如果支付成功就会跳到主页面中

好啦,今天的分享就到这了,希望能够帮到你呢!😊😊 

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

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

相关文章

el-table魔改后出现的坑,未解决供大家讨论

目前有一个坑。当我el-table表格字体设置大之后&#xff1a;show-overflow-tooltip会一直显示。 目前产品必须按照Ui图字体大小走&#xff0c;所以导致上面情况

(04)刻蚀——选择刻蚀材料创建所需图形

01、光“堆叠”可不行 前期我们了解了如何制作“饼干模具”。本期,我们就来讲讲如何采用这个“饼干模具”印出我们想要的“饼干”。这一步骤的重点,在于如何移除不需要的材料,即“刻蚀(Etching)工艺”。 ▲ 图1: 移除饼干中间部分,再倒入巧克力糖浆 让我们再来回想一下…

Lumerical------关闭 drawing grid 去更好地显示 mesh grid

Lumerical------关闭 drawing grid 去更好地显示 mesh grid 引言正文 引言 在 Lumerical 结构设置的时候&#xff0c;有时候我们想要查看 mesh 结构的 grid&#xff0c;但是本身默认的 dtawing grid 黑框会阻碍我们的观察&#xff0c;这时&#xff0c;我们便可以通过设置关闭这…

C#中List<T>底层原理剖析

C#中List底层原理剖析 1. 基础用法2. List的Capacity与Count&#xff1a;3.List的底层原理3.1. 构造3.2 Add()接口3.3 Remove()接口3.4 Inster()接口3.5 Clear()接口3.6 Contains()接口3.7 ToArray()接口3.8 Find()接口3.8 Sort()接口 4. 总结5. 参考 1. 基础用法 list.Max() …

简单又好玩的数据库就是有点烦

1 数据库 1.1 数据库类型 关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记录。 SQL语句&#xff08;标准数据查询语句&#xff09;就是一种基于关系型数据库的语言&#xff…

基于Java SSM框架实现实现机房预约系统项目【项目源码+论文说明】

基于java的SSM框架实现机房预约系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#…

2024年度 ROTS - 实时操作系统 Top 15

RTOS&#xff08;实时操作系统&#xff09;。 这里说的 RTOS 并非新星球大战电影中的机器人&#xff0c;而是物联网设备、航空系统、空中交通管制等背后的无声协调者&#xff0c;就在地球上。 RTOS&#xff0c;或称实时操作系统&#xff0c;设计它们是为了更好的管理资源&…

Transformer-MM-Explainability

two modalities are separated by the [SEP] token&#xff0c;the numbers in each attention module represent the Eq. number. E h _h h​ is the mean&#xff0c; ∇ \nabla ∇A : ∂ y t ∂ A {∂y_t}\over∂A ∂A∂yt​​for y t y_t yt​ which is the model’s out…

分布式锁3: zk实现分布式锁2 使用临时节点(需要自旋)

一 使用临时节点实现分布式锁 1.1 代码截图 1.2 代码如下 由于zookeeper获取链接是一个耗时过程&#xff0c;这里可以在项目启动时&#xff0c;初始化链接&#xff0c;并且只初始化一次。借助于spring特性&#xff0c;代码实现如下&#xff1a; package com.atguigu.distri…

营业执照代办网站源码 工商注册代账公司模板源码 公司注册和企业资质业务办理网站

这款大气蓝色的网站响应式模板专门为工商记账、公司注册和企业资质业务办理公司设计,包含了16个页面,非常适合正在寻找一个高质量网站模板的企业使用。 该模板使用响应式设计,使其能够在各种设备上呈现良好的用户体验,包括PC、平板和手机等。同时,页面设计非常美观,允许…

JAVA基础语句1

目录 前言 一.JAVA特性 简单 面向对象 分布式 多线程 二.关键字 三.对象和类 对象 类 构造方法 创建对象 访问实例变量和方法 源文件声明规则 Java 包 import 语句 总结 前言 这里参考了&#xff1a;Java 教程 | 菜鸟教程 (runoob.com) 第一个必须是&#xff1a; hello world&a…

【langchain】入门初探实战笔记(Chain, Retrieve, Memory, Agent)

1. 简介 1.1 大语言模型技术栈 大语言模型技术栈由四个主要部分组成&#xff1a; 数据预处理流程&#xff08;data preprocessing pipeline&#xff09;嵌入端点&#xff08;embeddings endpoint &#xff09;向量存储&#xff08;vector store&#xff09;LLM 终端&#xff…

看图识熊(三)

使用Windows Machine Learning加载ONNX模型并推理 环境要求 Windows Machine Learning支持在Windows应用程序中加载并使用训练好的机器学习模型。Windows 10从10.0.17763.0版本开始提供这套推理引擎&#xff0c;所以需要安装17763版本的Windows 10 SDK进行开发&#xff0c;并…

XML技术分析01

一、什么是XML eXtensible Markup Language ——可扩展标记语言 1、标记&#xff08; markup &#xff09;及标记语言&#xff1a; markup的含义是指插入到文档(document)中的标记 标记&#xff1a;是以标志的格式附加在文档中的。标志赋予文档的某一部分一个标记的标识…

HTML5+CSS3小实例:人物介绍卡片2.0

实例:人物介绍卡片2.0 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><…

PDF控件Spire.PDF for .NET【安全】演示:获取并验证 PDF 中的数字签名

在 PDF 中创建数字签名广泛用于保护 PDF 文件。因此&#xff0c;当您查看一些带有数字签名的PDF文件时&#xff0c;需要获取并验证数字签名。本文向您展示了一种通过使用Spire.PDF和 C# 代码来获取和验证 PDF 中的数字签名的解决方案。 Spire.PDF for .NET 是一款独立 PDF 控件…

搭建一个教育小程序的必要步骤

随着科技的飞速发展&#xff0c;小程序已经深入到我们生活的方方面面。对于教育行业来说&#xff0c;小程序的出现不仅为教育机构提供了新的宣传和互动平台&#xff0c;更为学生和家长带来了更为便捷的学习体验。那么&#xff0c;如何开发一款适合教育机构的小程序呢&#xff1…

基于springboot的sql防注入过滤器

目录 何为SQL注入基于springboot的sql防注入过滤器 回到顶部 何为SQL注入 SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严&#xff0c;攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句&#xff0c;在管理员不知情的情况下实现…

牛客网BC12-字符圣诞树

字符圣诞树 解题思路&#xff1a; 确定行数&#xff0c;一共5行&#xff0c;循环5次确定每行答应的内容&#xff0c;分成两部分&#xff0c;空格和字符 打印空格的个数依次递减打印字符的个数依次递增 找出打印空格和字符的个数与行数之间的关系 int main() {char ch 0;scanf(…

C# StringBuilder对比string的优点和15大案例

文章目录 StringBuilder和String 对比1. **循环内字符串连接**2. **构建大型日志消息**3. **格式化长字符串**4. **SQL 查询构造**5. **从文件读取并合并行**6. **拼接数组元素**7. **格式化电子邮件模板**8. **处理用户输入流**9. **JSON 或 XML 格式的序列化与构建**10. **动…