支付宝沙箱支付(包括直接扫码支付)SpringBoot

准备工作

1、登录支付宝开放平台:https://open.alipay.com/platform/developerIndex.htm

2、点击研发服务:
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第1张图片
3、设置密匙
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第2张图片
由于我设置过了,里面有密匙,你们要点击那个支付宝密匙生成器,快捷链接:https://miniu.alipay.com/keytool/create
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第3张图片
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第4张图片
点确定之后,就和我这个一样了
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第5张图片
好了,准备工作搞完了,页面先别关

2、网页支付

2.1 项目结构

为了通俗易懂,我就省略了业务层和持久层,直接伪造数据,防止大家对有些方法疑惑。

支付宝沙箱支付(包括直接扫码支付)SpringBoot_第6张图片

2.2 pom.xml


<dependency>
    <groupId>com.alipay.sdkgroupId>
    <artifactId>alipay-easysdkartifactId>
    <version>2.2.0version>
dependency>

<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
dependency>

2.3 application.yml

alipay:
  appId: 
  privateKey: 
  publicKey: 
  gateway: openapi.alipaydev.com  # 不用修改
  returnUrl: http://127.0.0.1:8080/return.html # 支付成功后跳转的页面,可以为空,也可以根据回调接口的判断来返回信息
  notifyUrl: http://域名/pay/notify  # 当支付成功后,支付宝的回调接口,是你本地的接口,如果没有域名的话要内网穿透

内网穿透可以参考:https://blog.csdn.net/wulei2921625957/article/details/113776112?spm=1001.2014.3001.5501

解释:

  • appId
    支付宝沙箱支付(包括直接扫码支付)SpringBoot_第7张图片
  • privateKey
    支付宝沙箱支付(包括直接扫码支付)SpringBoot_第8张图片
  • publicKey
    支付宝沙箱支付(包括直接扫码支付)SpringBoot_第9张图片

2.4 ProjectInit.java

/**
 * 项目初始化
 */
@Component
public class ProjectInit implements ApplicationRunner {
     

    //应用id
    @Value("${alipay.appId}")
    private String appId;

    //私钥
    @Value("${alipay.privateKey}")
    private String privateKey;

    //公钥
    @Value("${alipay.publicKey}")
    private String publicKey;

    //支付宝网关
    @Value("${alipay.gateway}")
    private String gateway;

    //支付成功后的接口回调地址,不是回调的友好页面,不要弄混了
    @Value("${alipay.notifyUrl}")
    private String notifyUrl;

    /**
     * 项目初始化事件
     */
    @Override
    public void run(ApplicationArguments args) {
     
        //初始化支付宝SDK
        Factory.setOptions(getOptions());
    }

    private Config getOptions() {
     
        //这里省略了一些不必要的配置,可参考文档的说明
        Config config = new Config();
        config.protocol = "https";
        config.gatewayHost = this.gateway;
        config.signType = "RSA2";
        config.appId = this.appId;

        // 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = this.privateKey;

        //注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = this.publicKey;

        //可设置异步通知接收服务地址(可选)
        config.notifyUrl = notifyUrl;

        return config;
    }
}

2.5 PayController.java

/**
 * @program: alipay
 * @create: 2021-06-25 14:44
 */
@RestController
public class PayController {
     
    //支付成功后要跳转的页面
    @Value("${alipay.returnUrl}")
    private String returnUrl;

    /**
     * 下单支付
     * 当用户点击支付的时候,应该是要传参的,我这里就直接省略了,免得修改麻烦,大家应该可以自己实现
     */
    @SneakyThrows
    @RequestMapping(value = "/pay", method = RequestMethod.POST)
    public String pay() {
     
        AlipayTradePagePayResponse response = Factory.Payment
                //选择网页支付平台
                .Page()
                //调用支付方法:订单名称、订单号、金额、回调页面
                .pay("测试商品", UUID.randomUUID().toString(), "5", returnUrl);
        //这里可以加入业务代码:比如生成订单等等。。
        return response.body;
    }

    /**
     * 支付后异步回调的接口
 	 * 这个接口需要内网穿透,因为要别人访问你的本地网站
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/notify", method = RequestMethod.POST)
    public String notifyAsync(HttpServletRequest request) {
     
        Map<String, String> map = new HashMap();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
     
            String key = parameterNames.nextElement();
            String value = request.getParameter(key);
            map.put(key, value);
        }
        //验签
        try {
     
            if (Factory.Payment.Common().verifyNotify(map)) {
     
                //验证用户的支付结果
                String trade_status = request.getParameter("trade_status");
                if ("TRADE_SUCCESS".equals(trade_status)) {
     
                    //这里可以更新订单的状态等等。。

                }
            } else {
     
                return "fail";
            }
        } catch (Exception e) {
     
            return "fail";
        }
        return "success";
    }
}

2.6 前端页面

index.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>确认订单title>
head>
<body>
<div>
    您的订单信息如下,请确认无误后支付:<br/>
    xxxxxxxxxx省略订单信息xxxxxxxxxx

    <form action="/pay" method="post">
        <button type="submit">确认支付button>
    form>
div>
body>
html>

return.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>支付成功title>
head>
<body>
<div>
    您已成功购买此商品,感谢您的支持
div>
body>
html>

至此,最简单的支付就搭建完成了,可以启动项目访问首页(劝大家换个浏览器访问,或者关闭当前浏览器所有页面,重新打开)点击支付后回调到下面的页面
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第10张图片
那怎么支付呢?直接用账号或者扫码(不是我们日常的支付宝,是沙箱版支付宝),那账号呢?
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第11张图片
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第12张图片
支付成功后会跳到事先写好的return页面。

那要怎么体现前后端的交互呢?当前端发起请求的时候,我们后端应该要生成一条订单插入数据库,设置一个状态,然后用户支付成功后,根据回调函数更新数据库订单的状态即可,这个时候,前端就要定时监听了,监听我们后端写的获取订单的状态接口。如果支付成功,则结束监听,可以利用这一点替换掉yml文件里面设置的返回页面,根据这个来跳转即可。

那如果用户多次获取二维码,那我们会生成许多无用的订单,这个时候我觉得用Redis存储订单Id并设置有效时间,配合后端可以写一个定时任务来清理无用的订单即可。

3、扫码支付

支付宝沙箱支付(包括直接扫码支付)SpringBoot_第13张图片
我这里举例类似于上面的打赏功能,实现起来还是比较简单的。前端代码我就不展示了,等会说下思路即可。

重新搭建一个项目吧

pom.xml

和上面一样的

application.yml

和上面一样的

ProjectInit.java

和上面一样的

AlipayServiceImpl.java

@Service
public class AlipayServiceImpl implements AlipayService {
     
	
    //这个服务是订单服务,我就不展示了,主要的功能就是插入订单,更新订单状态。可以自己写
    @Autowired
    private OrderService orderService; 

    /**
     * 生成二维码,当面付
     *
     * @param order  订单类,大家应该可以想到里面应该有哪些字段
     */
    @SneakyThrows
    @Override
    public Map<String, Object> FaceTofACEPay(Order order) {
     
        /**
         * 订单的公共操作
         */
        MyUtil myUtil = new MyUtil();
        //获取二维码的同时要生成订单号
        String orderId = myUtil.getUUID();
        order.setOrderId(orderId);
        //获取当前日期
        String orderDate = myUtil.getCurrentDetailedTime();
        order.setOrderDate(orderDate);
        //设置订单为未支付状态
        order.setOrderStatus(0);

        Integer integer;
        HashMap<String, Object> map = new HashMap<>();
        AlipayTradePrecreateResponse response = Factory.Payment.FaceToFace().preCreate("树洞赞赏", orderId, "" + order.getOrderAmount());
        if (ResponseChecker.success(response)) {
     
            String qrCode = response.getQrCode();
            map.put("qrCode", qrCode);  //生成的二维码链接,不可直接打开,后面会说到
            map.put("orderId", orderId); //生成的订单号应该要返回给前端
            integer = orderService.insertOrder(order); //插入订单数据库
        } else {
     
            integer = 0;
            map.put("qrCode", "");
            map.put("orderId", "");
        }
        if (integer <= 0) {
     
            //我自定义的异常类
            throw new BusinessException(EnumBusinessError.UNKNOWN_ERROR, "生成二维码失败");
        }
        return map;
    }

    /**
     * 支付异步回调
     * 代码跟上面的案例大同小异
     * @param mapParams
     * @return
     */
    @SneakyThrows
    @Override
    public String notifyUrl(HttpServletRequest request) {
     
        Map<String, String> map = new HashMap();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
     
            String key = parameterNames.nextElement();
            String value = request.getParameter(key);
            map.put(key, value);
        }
        //验签
        try {
     
            if (Factory.Payment.Common().verifyNotify(map)) {
     
                //验证用户的支付结果
                String trade_status = request.getParameter("trade_status");
                if ("TRADE_SUCCESS".equals(trade_status)) {
     
                    //这里可以根据业务更新订单支付状态.....
                    Order order = new Order();
                    String orderId = map.get("out_trade_no");
                    order.setOrderId(orderId);
                    order.setOrderStatus(1);
                    orderService.updateOrder(order);
                    return "success";
                }
        } else {
     
            return "fail";
        }
        return "fail";
    }
}

PayController.java

@RestController
public class PayController {
     

    @Autowired
    private OrderService orderService;

    @Autowired
    private AlipayService alipayService;

    /**
     * 前端定时检查订单的支付状态
     *
     * @param order  订单类作为接收参数  用到了里面的  orderId
     * CommonReturnType  这个就是统一返回前端的结果  code  msg  data
     * @return
     */
    @RequestMapping(value = "/checkOrderStatus", method = RequestMethod.POST)
    public CommonReturnType checkOrderStatus(@RequestBody Order order) {
     
        boolean b = orderService.checkOrderStatus(order);
        return CommonReturnType.create(b);
    }


    /**
     * 获取支付二维码
     *
     * @param order  订单类作为接收参数  用到了里面的  order金额 字段
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/getQrCode", method = {
     RequestMethod.POST})
    public CommonReturnType FacetoFacePay(@RequestBody Order order) throws Exception {
     
        Map<String, Object> map = alipayService.FaceTofACEPay(order);
        return CommonReturnType.create(map);
    }


    /**
     * 异步回调接口
     * 
     */
    @RequestMapping(value = "/notifyUrl", method = RequestMethod.POST)
    public String notifyInterface(HttpServletRequest request) throws Exception {
     
        return alipayService.notifyUrl(request);
    }
}

现在后端搭建完毕,前端就不写了,直接调用接口即可:

获取二维码接口: 在线的二维码转换网站:http://qrcode.cnaidc.com/
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第14张图片
监听支付状态接口:监听的时候,前端要定时监听,一般为1s到2s的样子发一次请求,如果刷新了二维码,那就要重新带上新的订单ID重新监听。
支付宝沙箱支付(包括直接扫码支付)SpringBoot_第15张图片

你可能感兴趣的:(SpringBoot,java)