SpringBoot集成支付宝网页支付

SpringBoot集成支付宝网页支付

前言

使用支付宝沙箱做模拟测试 体验一下百万富翁的感觉、

使用到的网址或文章

发起支付的业务流程图

  • 图片来自支付宝开放平台。

    AHR0CH~1

    上面的流程图,已经大致描述了发起支付的过程。沙箱环境简介&基础开发配置

沙箱环境简介&基础开发配置

支付宝有一个供开发者测试使用的沙箱环境,会提供一个沙箱版的支付宝app、一个商家账户、一个买家账户。有了这个,可以让我们跳过商家入驻、企业资质审核等过程,开箱即用,降低了学习成本。

使用支付宝沙箱

  • 使用支付宝登录 -- 支付宝开放平台

  • https://open.alipay.com/

  • 进入控制台

  • 进入沙箱服务

    image-20211021112244248

  • 此时就到了沙箱应用的控制台,会为你分配一个APPID:

    image-20211021112643807

配置秘钥

  • 出于安全性的考虑,我们需要生成一份公钥和私钥,公钥提供给支付宝,支付宝对数据进行加密;私钥用于解析支付宝传来的加密数据,由我们自行保管。支付宝提供了公钥和私钥的生成工具,在文档中心点击“开发工具”

  • image-20211021112901170

  • 选择需要版本- 这里是 windows

  • 生成秘钥

  • image-20211021113727561

  • 直接默认的配置生成就好 ----把私钥和公钥保存好。

设置公钥

  • 回到沙箱应用控制台,点击这个地方设置公钥(注意不是公钥证书哦,我们用的不是证书的方式)

  • image-20211021114103915

  • 在弹出的窗口,把刚才生成的公钥复制粘贴进去,点击确定:

  • image-20211021114332929

  • 其中支付宝公钥就是我们上传的公钥,应用公钥是支付宝为我们自动生成的。

  • 在沙箱应用控制台,扫码下载沙箱版的支付宝(目前只有安卓版),提供了一个商家号、一个买家号,其中买家号里面有10万块钱(可惜不能提现呵呵呵),后面支付的时候会用

  • image-20211021114512909

创建Spring Boot项目

  • 只有基本的web、lombok、支付宝的sdk依赖

导入依赖

<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-easysdk -->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-easysdk</artifactId>
            <version>2.0.2</version>
        </dependency>

结构图解

20200908154612840

  • PaymentBO是接收controller层的入参,如商品id、购买数量等等,结合具体的业务而定,我在这里只是使-
  • 项目更规范,所以这个类省略了很多字段;
  • PayController是控制器,负责分发请求,主要是处理用户的支付请求,以及支付宝的回调;
  • ProjectInit是项目初始化时执行的一些操作,适合在整个过程中只需要执行一次的业务;
  • PayService是真正的服务层,主要的业务代码在这里;
  • OrderUtil是一个订单工具类,主要是生成随机订单号,当然这个类只是简单的实现一下;
  • index.html是模拟下单支付的页面;
  • return.html是支付完成之后的友好页面。

编写支付页面和回调页面

  • 在index.html编写如下代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>确认订单</title>
    </head>
    <body>
        <div>
            您的订单信息如下,请确认无误后支付:<br />
            xxxxxxxxxx省略订单信息xxxxxxxxxx
     
            <form>
                <button type="submit">确认支付</button>
            </form>
        </div>
    </body>
    </html>
    
  • 在return.html编写以下代码:

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

搭建内网穿透

回调地址肯定要外网可以访问,要不然支付宝怎么会调用得到呢?但是我们作为开发者可能没有自己到服务器和域名,所以我们使用内网穿透工具natapp获取临时域名

文章连接 --- > natapp - get104 - 博客园 (cnblogs.com)

映射完成之后,启动项目,用临时域名测试一下上面的页面:

  • index.html
  • 20200908162053729
  • return.html
  • 20200908162143418

配置Spring Boot项目&初始化支付宝SDK

在application.yml配置文件,将公钥、私钥、APPID、支付宝的网关、页面回调、接口回调等信息配置进来:

alipay:
  appId: 换成你自己的APPID
  privateKey: 换成你自己的私钥
  publicKey: 换成你自己的公钥
  #支付网关配置,这一项是写死的,正式环境是openapi.alipay.com
  gateway: openapi.alipaydev.com
  #支付成功之后的回调页面,只是一个友好页面。主要换成你自己映射的临时域名
  returnUrl: http://frnqxw.natappfree.cc/return.html
  #支付成功的接口回调,我们还没写,先空着
  notifyUrl: 
  • appid 在支付宝开放平台控制台的沙箱应用里
  • 公钥和秘钥在支付宝开放平台开发助手里
  • 接下来需要读取支付宝的文档了

全局配置Factory类

  • 只需配置一次即可
package com.course.java.alipay.init;
 
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
 
/**
 *  项目初始化
 *
 * @author wangziyang
 * @date 2020-09-08
 * */
@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) throws Exception {
        //初始化支付宝SDK
        Factory.setOptions(getOptions());
        System.out.println("**********支付宝SDK初始化完成**********");
    }
 
    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;
    }
}

再次启动项目:

20200908164250827

编写下单支付接口

  • 既然要下单,肯定少不了传参,所以我们这里简单的声明一个PaymentBO类:
package com.course.java.alipay.bo;
 
import lombok.Data;
 
import java.math.BigDecimal;
 
/**
 *  发起支付时的参数
 * */
@Data
public class PaymentBO {
    //省略其他的业务参数,如商品id、购买数量等
 
    //商品名称
    private String subject;
 
    //总金额
    private BigDecimal total = BigDecimal.ZERO;
}

Alipay Easy SDK API

20200908165329678

20200908165411339

业务实现类

  • 我们只需要Factory.Payment.Page.pay就可以发起一个支付请求,比之前还是简洁了不少,其他操作都封装好了。所以我们在PayService编写以下代码:
package com.course.java.alipay.service;
 
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePayResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.course.java.alipay.bo.PaymentBO;
import com.course.java.alipay.util.OrderUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
 
import java.math.BigDecimal;
 
/**
 *  支付宝支付,业务实现
 *
 * @author wangziyang
 * @date 2020-09-08
 * */
@Service
public class PayService {
 
    //支付成功后要跳转的页面
    @Value("${alipay.returnUrl}")
    private String returnUrl;
 
    /**
     *  下单支付
     * */
    public Object pay (PaymentBO bo) throws Exception {
 
        //从存储介质(如MySQL、Redis)查询商品信息、总金额等敏感信息
 
        //…………省略相关代码,这里直接赋值…………
 
        bo.setSubject("测试商品");
        bo.setTotal(new BigDecimal(10.00));
 
        //调用sdk,发起支付
        AlipayTradePagePayResponse response = Factory.Payment
                //选择网页支付平台
                .Page()
                //调用支付方法,设置订单名称、我们自己系统中的订单号、金额、回调页面
                .pay(bo.getSubject() , OrderUtil.getOrderNo(), bo.getTotal().toString() , returnUrl);
 
        //这里的response.body,就是一个可以直接加载的html片段,
        // 这里为了简单我直接返回这个片段,前端直接
        return response.body;
    }
}

控制器

package com.course.java.alipay.controller;
 
import com.course.java.alipay.bo.PaymentBO;
import com.course.java.alipay.service.PayService;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
 
import java.util.Map;
 
/**
 *  支付宝支付,控制器
 *
 * @author wangziyang
 * */
@RestController
@RequestMapping(value = "/pay")
@AllArgsConstructor
public class PayController {
 
    private PayService payService;
 
    /**
     *  下单支付
     * */
    @GetMapping(value = "/confirm" , produces = {"text/html;charset=UTF-8"})
    public Object pay (@RequestParam(required = false) PaymentBO bo) throws Exception {
        //这个接口其实应该是post方式的,但是我这里图方便,直接以get方式访问,
        //且返回格式是text/html,这样前端页面就能直接显示支付宝返回的html片段
        //真实场景下由post方式请求,返回code、msg、data那种格式的标准结构,让前端拿到data里的        
        //html片段之后自行加载
 
        //由于我这里并没有真正的传参数,所以象征性的new一下,避免空指针
        bo = new PaymentBO();
        return payService.pay(bo);
    }
}

支付测试

  • 在index.html页面做如下修改,点击支付的时候访问我们的/pay/confirm接口:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>确认订单</title>
</head>
<body>
    <div>
        您的订单信息如下,请确认无误后支付:<br />
        xxxxxxxxxx省略订单信息xxxxxxxxxx
 
        <form enctype="multipart/form-data" action="/pay/confirm" method="get">
            <button type="submit">确认支付</button>
        </form>
    </div>
</body>
</html>
  • 运行项目,点击确认支付就看到了以下页面(有时候下面的页面会提示有钓鱼风险,换一个浏览器就好,或者关了浏览器重来,这个无解):

  • 在手机上打开沙箱版支付宝,登录买家帐号,扫码支付:

20200908173607923

2020090817363499

20200908173653286

此时已经完成

  • 上线测试只需修改yml里的配置即可
posted @ 2021-10-21 14:57  get104  阅读(570)  评论(0)    收藏  举报