【SpringBoot开发】之商城项目案例(沙箱支付)

发布时间:2024年01月04日

?🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚

🌟推荐给大家我的博客专栏《SpringBoot开发之商城项目系列》。🎯🎯

🎁如果感觉还不错的话请给我关注加三连吧!🎁🎁


前言

? ? ? ? 在上一期的商城项目分享中我们实现了订单项及订单的生成,首先在购物车中选择我们所需要结算的商品,点击结算。然后我们就会获取相关的参数传递都后端进行一系列处理,接着我们调用方法进行新增,其次就是将选中的商品从缓存中去除掉指定的商品数据即可。本期博客基于上期的博客文章,本次实现的是购买支付,请仔细阅读,干货满满。

一、什么是沙箱支付

1. 概述

????????支付宝沙箱支付(Alipay Sandbox Payment)是支付宝提供的一个模拟支付环境,用于开发和测试支付宝支付功能的开发者工具。在真实的支付宝环境中进行支付开发和测试可能涉及真实资金和真实用户账户,而沙箱环境则提供了一个安全、隔离的环境,使开发者能够模拟支付过程,测试支付功能,而不会使用真实资金。

????????使用支付宝沙箱支付环境,开发者可以模拟各种支付场景,包括交易创建、支付请求、支付回调等,以验证支付功能的正确性和稳定性。沙箱环境中的所有交易和数据都是虚拟的,不会产生真实的交易或资金流动。

????????支付宝沙箱支付提供了开发者工具和接口,使开发者能够在模拟环境下进行支付流程的调试和测试。开发者可以在沙箱环境中创建测试账户、配置模拟的交易金额和状态,使用沙箱环境中的接口进行支付操作,并模拟支付回调接口接收支付结果。

????????通过使用支付宝沙箱支付,开发者可以更安全、更有效地进行支付功能的开发和测试,避免了对真实环境的影响和风险。一旦支付功能在沙箱环境中验证通过,开发者可以将其部署到真实的支付宝生产环境中,与真实用户进行交互和支付。

2. 沙箱支付的应用场景

????????沙箱支付是一个用于模拟真实支付环境的测试工具,其主要作用是在开发和测试阶段模拟支付流程,以便开发者能够验证其支付系统的正确性、稳定性和安全性。以下是沙箱支付的主要作用和一些应用场景:

沙箱支付的应用场景
应用场景说明
测试支付集成开发者可以使用沙箱支付来测试其应用与支付网关、第三方支付服务商等支付系统的集成。这有助于确保支付流程的正确性和顺畅性。
模拟不同支付状态沙箱支付通常允许开发者模拟不同的支付状态,如支付成功、支付失败、支付取消等,以确保系统能够正确地处理各种支付结果。
调试和错误排查在沙箱环境中,开发者可以更容易地调试和排查支付相关的问题,而不必担心真实支付交易对用户产生影响。
安全性测试沙箱支付环境还可以用于测试支付系统的安全性,包括对支付数据的加密、身份验证和防止欺诈的功能。
开发人员培训沙箱支付是培训新的开发人员、商户或支付系统操作员的理想环境,因为它提供了一个模拟支付流程的平台,而无需使用真实的支付账户和资金。
API 接口测试对于开发使用支付API的应用,沙箱支付提供了一个测试接口的环境,开发者可以验证其API调用的正确性。
新功能测试当支付系统添加新功能时,沙箱环境可以用于测试这些新功能,确保其与现有系统的兼容性。
合规性测试对于需要遵循一定支付行业标准和法规的应用,沙箱支付可以用于测试支付系统的合规性,确保符合相关的法规和标准。

二、配置沙箱支付

1.接入支付宝开放平台

? ? ? ? 首先我们进入到支付宝开放平台官网进行登陆,网址:支付宝开放平台

?????????登录成功之后,点击控制台跳转到控制台主页面,将浏览器进度条滚动到最下面,选择沙箱,最后点击沙箱选项即可,如下:

? ? ? ? ?在进入之前我们还需要进行一个操作,补全信息

?????????将浏览器进度条滚动到最下面,选择沙箱,最后点击沙箱选项即可

?2.?下载

? ? ? ? 我们使用沙箱支付需要下载安装支付宝开放平台开发助手。

网址:小程序文档 - 支付宝文档中心

注意事项 :请不要安装在含有空格的目录路径下,否则会导致公私钥乱码的问题

? ? ? ? 我们下载的版本如下所示?

? ? ? ? ?我们下载好安装包点击进行安装,根据指示进行安装,安装之后我们的桌面会生成一个图标,打开之后的页面如下

3. 配置秘钥

????????打开支付宝开放平台开发助手,选择密钥方式,选择`RSA2`方式,最后点击生成密钥即可生成得到私钥和公钥。

? ? ? ? ?我们会生成对应的应用私钥和应用公钥。

4.? 生成支付宝公钥

?????????根据支付宝开放平台开发助手生成的应用公钥,生成支付宝公钥。找到支付宝开放平台的沙箱应用一栏,选择“开发信息”接口加签方式中的自定义密钥方式,点击设置并查看按钮:

?

? ? ? ? 将我们之前生成的应用公钥的内容复制在所选的框内?

?5. 配置沙箱账号(买家)

? ? ? ? 我们使用沙箱支付前还需配置沙箱账号进行相应的付款支付。

网址:登录 - 支付宝

?6. 下载沙箱支付宝(手机下载)

????????下载沙箱支付宝(只支持安卓),利用实现功能

网址:登录 - 支付宝

? ? ? ? 下载安装之后,进行相应的注册或者登陆?

?

三、SpringBoot项目中集成使用

1. 导入pom依赖

? ? ? ? 导入我们沙箱支付所需要的pom依赖,版本不同可能会影响到我们的使用

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

2. 配置沙箱支付配置类

? ? ? ? 我们可以参考我们的开发文档中根据自己的需要及信息进行修改

? ? ? ? 下面是我自己修改后的配置类,配置类中需要修改以下信息

?

?AlipayConfig.java

package com.yx.yxshop.config;

import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.yx.yxshop.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 = "9021000133666836";
        //支付宝公钥
        config.alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr5pP6Hf4txMzWmDIjkUUK5wju2gaZwV3MdNa/zvBOImeHTNcSdXTrtNtweZ8/gXxoJJV9nJR1h9SDyBZqDx54wd2Mu3XeHuQRZuqW1Sr4uCpcbsFGgULzv4Y8AgmJZpIJUkFFUIFY3m3NdK7bHHjBqVw9T+AkiHSAvzKgAOmoLX/WHEFIEKTEGrkILvs6HtDsVSn7MWJ6WM5ndk9tZBJZDRhb2p2H0EgeVPrZ8OrqJxzWs/Tl08WYrd0yblPuLg3c7IUM6G7gXx0Y2cPc0XyjpXVhXcryASH5Pl5u7nChI9ra6xvwNshdZpXlZ9iygFsTrHW7tAgHpKwjlIxe4pM2QIDAQAB";
        //签名方式
        config.signType = "RSA2";
        //商户私钥(应用私钥),您的PKCS8格式RSA2私钥
        config.merchantPrivateKey = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCKk86IjtT1YR6Q00PY4OcPyd4p2L6PpYkphY2vBXoH8Umy2IJbpaERH5y+NeD06SLrCkAbSuEZ8neGR27HKX6VRlMhyFSbiSufLn4FjGrfuiLhFQ2EjtJOXN2f/aw4hpigjBEbPppt+wXTEel3ggwY8t3xqYmyuTnKLbd1R7wKTJz3iBrt02eGmcBgnkhL10oImvivbcoacCYZjbv4TvbPHYQPpyFNRGHGHFH8p4EZzmoRyZHyHCCfiUnYKvP1VESRSWWihDS1be05OIVRwFl6RORLhouDrqukRnvFnu9euE8mXsZSc1sL2fF/9uRtVY7q+s00gFYFZTseDSltcVbRAgMBAAECggEAfkEN8XKYaXtzeqVQcj7tpX/Yzi1v6LX7kn3gSS5nMOdPqwcBNXhgl5ZCmzXBX8EslBHBuFvvXFGBPjDEp+WRM3Vf9i5rj01ZFe0o2etFz9HpR0KED1qEFusa7FIU32cZlWQnjbfqwPrsIpJ2L/CnDu/u7+bz0oZZNW+TbuHNW19tPAkpS+kkSZRYOdMjqXJ8faDUUVm0ywFepOYjRgsUZhBhELC7W9Snd3ZKJt0QdtgN/OD9t3xqsrOcIoCe5V+EgYz69ntFly2xZssf9wUDr5jJz4L+ZsOolfKfiP+S9aEfq55kJxYkTseD4XmoGk6uUQMYwShi/RwBIbeFt0PJ5QKBgQD9n+ahZPXuWP4EAPiVDpG6A4HfJBTkfQ3EPIPd8/43kXuRto9VkB4XrKA90qF2+Ubg+iZTbfOZ5r9dQvODbgWA7CP8pMUV1hagIx+MrgYSkt4rZAFAZzPmDZEoPz8BrpSeGUysmii2OM6Oi/E5TOEsY9QR12AD5YdSHtt6DxaEcwKBgQCL4BCMbvZzhRh2k8tJRsoMt0d9hvK+R1AVu++dSLQEWqAYDCAVU5EGRpTMMbXH+wMNvyUCnk17vtQXJfdxdpbOlQXwHfukX5p94Tw05jI8sndbGzcmiBuB2lD+SrONIRebtqXpYqKrAvQNJN3r1VJ0nR9tU5ydGODYlNP0eqQqqwKBgHFmqKFrWgcbZWB26q8DF6d7X/tcz7amL5yZjkCUkwtXkk/Bt+8DBGGDfxaFckqXBNkdIDvXgr9CjDfv8p/GdtGBREn3hmPQGMe7TKUDPpXZc1slWOXp/yuSz1+Wf96Jp8vU9hKKzz9CwSC9c7syI9BMRos/qJ+1Zj8SqwG1c1T/AoGAXzX7zbvSYjvctQpRO+XFrvYq4ZU0MrVUHBc7OXK9pqERjIzkYd/qPb1Zl8zWkfOY1oif5rJex7bTo5YiYsd4S9JAonumSPMStFDWrKNs2sgYWpmh5saLAs7aht3ObmhyK3oeYUjUtVdjTHcl4FvqrpotRDu/xWej1Yko5ad9i9UCgYB1XGJcnclrlMlVjJvwdK/JXoBgLu+N4OLIaJQ3E5G/CSYRSFQzy1m312ogd1CeCJFL9gZzkCZE5HL5/ARpAP/5CbvcEyxN6KN+1nV9bqv9d5IqYH/CDnzVeInx3zReWSMAmpUIUX6jj+4YIfJ2Gkm4++Dei7lHJv98Gwf1xyDx5w==";
        return config;
    }

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

}

3.?修改订单生成代码

?OrderController.java
package com.yx.yxshop.controller;

import com.alipay.easysdk.factory.Factory;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.yitter.idgen.YitIdHelper;
import com.yx.yxshop.config.AlipayConfig;
import com.yx.yxshop.exp.BusinessException;
import com.yx.yxshop.pojo.Goods;
import com.yx.yxshop.pojo.Order;
import com.yx.yxshop.pojo.OrderItem;
import com.yx.yxshop.pojo.User;
import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.resp.JsonResponseStatus;
import com.yx.yxshop.service.IGoodsService;
import com.yx.yxshop.service.IOrderItemService;
import com.yx.yxshop.service.IOrderService;
import com.yx.yxshop.service.IRedisService;
import com.yx.yxshop.vo.CartVo;
import com.yx.yxshop.vo.OrderVo;
import org.checkerframework.checker.units.qual.A;
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.RestController;

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

/**
 * <p>
 * 订单信息表 前端控制器
 * </p>
 *
 * @author yangxin
 * @since 2023-12-27
 */
@RestController
@RequestMapping("/order")
public class OrderController {

//    引入对应接口
    @Autowired
    private IRedisService redisService;
    @Autowired
    private IGoodsService goodsService;
    @Autowired
    private IOrderItemService orderItemService;
    @Autowired
    private IOrderService orderService;

    /**
     * 跳转订单的请求方法
     * @return
     */
    @RequestMapping("/add")
    public JsonResponseBody<?> toOrder(User user, OrderVo orderVo){
//        获取购物车商品id字符串
        String ids = orderVo.getIds();
//        从缓存中获取购物车商品信息   此时只有id和数量
        List<CartVo> cartitems = redisService.loadCart(user, ids);
        //        从数据库中获取商品信息
        //        获取到所选中商品的Id集合
        List<Long> gds = cartitems.stream().map(CartVo::getGid).collect(Collectors.toList());
//      根据集合查询对应的商品信息
        List<Goods> goods = goodsService.listByIds(gds);
//      遍历结合赋值给对应的选项
        for (Goods g : goods) {
//            根据商品Id查询对应的购物车选项
            CartVo cartVo = cartitems.stream().filter(v -> Objects.equals(v.getGid(), g.getGid()))
                    .findFirst()
                    .get();
//            商品g的属性赋值给vo
            BeanUtils.copyProperties(g,cartVo);
        }
//          使用雪花id生成订单id
        long oid = YitIdHelper.nextId();//订单id
//        定义一个变量计算总价
        BigDecimal total = new BigDecimal("0");
//        增加订单项
//        实例化一个集合
        List<OrderItem> orderItems = new ArrayList<>();//用户后续插入数据
//        遍历商品信息集合
        for (CartVo item : cartitems) {
//            生成订单项
//            实例化一个订单项
            OrderItem orderItem = new OrderItem();
//              赋值属性
            BeanUtils.copyProperties(item,orderItem);
//            订单项的订单商品数量
            orderItem.setQuantity(item.getNum());
            orderItem.setOoid(YitIdHelper.nextId());//订单项id
            orderItem.setOid(oid);//订单id
//            订单对象存放到订单集合中
//              减少请求生成
            orderItems.add(orderItem);
//            计算总价
            total = total.add(item.sumprice());

        }
//        向数据库中批量插入数据
        orderItemService.saveBatch(orderItems,5);//分批插入防止数据量过多
//        增加订单
//        实例化订单对象
        Order order = new Order();
//        设置订单属性
        BeanUtils.copyProperties(orderVo,order);
//        设置订单id
        order.setOid(oid);
//      设置总价
        order.setTotal(total);
//        设置用户id
        order.setUserId(user.getId());
//        设置订单状态  默认0
        order.setStatus(0);
//      设置创建时间
        order.setCreateDate(new Date());
//      将订单插入数据库
        orderService.save(order);
//        删除缓存中购物车对应的商品
        redisService.removeCart(user,orderVo.getIds());
        return JsonResponseBody.success(oid);
    }

    /**
     * 跳转支付的请求方法
     * @return
     */
    @RequestMapping("/pay")
    public String toOrder(User user, String oid){
//        根据订单ID查询订单
        Order order = orderService.getById(oid);
//        调用支付宝的支付功能
        String body = new AlipayConfig().goAlipay(order);//返回的是表单
        return body;
    }

    /**
     * 支付成功之后的请求方法,修改其订单状态
     * @return
     */
    @RequestMapping("/payDone")
    public String toOrder(@RequestParam Map<String,String> ms) throws Exception {
//        验签  检验是否支付
        Boolean b = Factory.Payment.Common().verifyNotify(ms);
        if (!b){//没有支付
           throw new BusinessException(JsonResponseStatus.UN_KNOWN);//抛出未知异常
        }
//       获取订单id
        String oid = ms.get("out_trade_no");
//        修改订单状态
        orderService.update(new UpdateWrapper<Order>().set("status",1).set("pay_date",new Date()).eq("oid",oid));
        return "<script>\n" +
                "\talert(\"支付成功\");\n" +
                "\tlocation.href=\"/\";//跳转首页\n" +
                "</script>";
    }

}

? ? ? ? 前端代码修改主要是js的代码

?

四、演示


?

?🎉🎉本期的博客分享到此结束🎉🎉

📚📚各位老铁慢慢消化📚📚

🎯🎯下期博客博主会带来新货🎯🎯

🎁三连加关注,阅读不迷路?!🎁

文章来源:https://blog.csdn.net/weixin_74352229/article/details/135383408
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。