springboot集成支付宝支付

前置需求

支付宝沙箱配置

需要有支付宝沙箱:提供一个虚拟的支付环境,用于测验调试,

因为是沙箱默认的密钥即可,这其中我们需要如下数据

  • 支付宝的公钥和私钥
  • 支付的网关
  • 支付的APPID
  • 支付宝sdk

内网穿透***

系统启动只是在本地,如何让不同ip都能访问呢?内网穿透

内网穿透是一种技术手段,它允许外部网络的用户访问内部网络中的设备或服务。这项技术通常用于远程访问、建站、调试和游戏联机等场景,使得本地开发或维护的工作可以不受地理位置的限制。

为什么需要内网穿透呢? 因为本质上调用人家的服务,当请求以后,支付宝需要返回给你信息,这就需要你的地址。这样才能找到我们的接口,我们才能根据他返回的信息做出一些业务处理。

NATAPP -

免费即可,第一次需要实名认证

设置免费隧道,

我的隧道进行查询 以下红框中的码需要用到,

下载pc端app,解压出来,只有一个exe文件

这里68cvzs地址就是我本地映射到公网上的地址。

可以启动本地的9090服务,使用公网地址进行测试,试一试能不能访问成功

成功,则内网穿透成功。

具体开发

sdk

<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.9.28.ALL</version>
</dependency>

配置

相当于设置收钱的人是谁

server:
  port: 9090

alipay:
  # appid
  appId: 9021000135634074
  # 应用私钥
  appPrivateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8bdoXTGIiXXkt/FBg0LPoOSvQANWo4wIYKVSjodGa60y+7Dk9d6DavrcWR7xrBYaJcSt9jr35rYGZQsXLuYJya0X1bkuiE4C1B26jrTdvPMu2UXlo9dQM2XdwuAb7ohkgMdp0jkMYag2IbtfjRFv0h+jnlUNfAZjJyQ8l7TePqFnJ8S94L9jVloYIRoeoJG3vj701HJ4IccEEFzpiNoznJnw33elr9Zu5K1gZqvQxp61D2pbMJ0daiof94uh3J0XwqeyB8HNbGlZj3OIyZMibU1AZfIpE2+cYVSGYt7+JawayNybhuTlb540OZGRjFGS7KCysqvEqIH6KbHqZJpwpAgMBAAECggEAVmPLDMmBaJ4Qc+vtciXdhgfINYpcax0BFdDFNGx5bBybSCGHsM3LGt87k+R511tmLleLh/pM0U8iTwEVLG02CH10Sq0x0fI9HUJ2EGbXNpHaGBHMpzml6eo+X5iP4wiTmnYg4TVkP6pH4BhptJHf0wII3zxI8iMKSOF8p3fV9G+Ay8i4zTmJtnzTopMshmH0IjemGBsImVLPjlrGNtt28ibarFNu+y8zq82H8ZKW9Gf2aokU3juml7ztIeVuEJEJGZo9D7hv+IwjCUr4q273pNSVE/RW0Nyi2moHmlaJzvSBY9iGPegmZDZAdG2pTUvfbj7KMUHUpTATSXkOwUg8AQKBgQD7zyspR0V9XAlWIYVNNu9aNgtN6lpYtG0MlQYAREsWF9/HCRgghnSE5/TfvKtDsdk2E2nw6WTp9D9ATPFf5TXTWNCcQp2bhiZttFgr4ii+inFpOf8zNJKovuLEa8SB9ImvPQXF7H1pbvWMnDoFtVTbmtiL7RtfOYK/GaK3I3Z2gQKBgQC/kKcXdieMY0jA6Ho6+5W5LmwBWiuhp/8S+o29494JzLr9cG074q69jaXHBNaEMgLfn3oM9Te2SzaPf8dEIzmfjmOJOrfzz8WOI9vWfW14u9ev6h52p21CjKCVsaxcjle9rXWoO4L8UfPpvjA3gz+/KLFdjyz16ctIeKqLWkvhqQKBgQCxcklBGnduorf1mUOdqRO8p48JxhcKoYKjNjT3ZSjglcxdLxxwdy+PJQJb1FGAL8kYoU+rtF0nvLYB8va5lAV9PP4sz4jPOxbDgi4MJqB9vYO0GmmrROYAwht9PVBKH/ASrFbwJfounUGuZ70/nowBATqOSHVcgmOPlVoj4nqtgQKBgBJdgNdr+XCpGBF+eDFtazY8sBgVF/fAjmC4apxY2zGfUhH4FFDYc54ylUEWQqab0NC1jlLts3Cjl1B8lJMAuYaFdR9z5KzYL31oDKxsi/E2OQBhdpgzhvMJXl/bj8Wz08+YcJ43TmB4TuUK2b6Is7TU5uQsVMgiqN2Cy/7eIkY5AoGANDCwDwrSTsHPzmrdL+8ACgWNv7KIj81mjSRIxGQrYYwuxMUAKWRBzEPcgFj3c6KfbyR/PxH+4jf8weYUIMZqY16pjmTUPs1Ba2UX0C2DSIcZeF7SRTrRX3CT0e5ycRBXu1EoqGPmtmO1YFnObTsGY+ZwMUKIFSS1ip2IkPaDyvA=
  # 支付宝公钥
  alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx8Z31UrIoS1tL3f0VVqia8tlUze7RFnEVi3VbwOzC3RV9tt/crZaR+B21DrWkzjCw36tfrlL2WKYW4dWE/4KKwBY+TB67dZmIUShLf65imxysrB5woofe00uXv5GRYNVEgF+XAyUl3lVO5CHCwr2TGnBdOFOBJzvjmBViwEXHFLqECc4UqqnxvrP0AjMggwExktBEba3Z+i14o1jNznntf52FR2A/RGXiuj9FZHXTOlsc0FHodA3uO5V7WCwMlb7QI0HUm4UvTgPziLv6L3Gee2925Mu5t3NXQuRSVrOVh/W2xUCdwoPVeXd6mNh5v25qrR8TsKbmuO556haJbbCTQIDAQAB
  # 回调接口,支付宝通过什么接口通知你,这里就是填写具体接口地址,我们使用公网的地址以及接口uri /公网地址/uri
  notifyUrl: http://v6tqyw.natappfree.cc/alipay/notify
/**
 * 读取yml中的配置信息,自动填充到对应的属性
 */
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {
    private String appId;
    private String appPrivateKey;
    private String alipayPublicKey;
    private String notifyUrl;

}

具体支付***

步骤
  1. 必须传递如下参数(支付订单号(必须唯一), 订单名称,订单金额)
  2. 创建支付客户端 设定appid 公钥密钥等等信息,用于知道这个订单谁发出的,钱给到谁的账户
  3. 将请求的订单号等等写入支付的请求对象中 并且请求对象设置回调接口以及支付后的接口
  4. 支付客户端对象根据支付请求对象去执行,调用支付宝API
  5. 这个API接口会返回一个表单页面,让用户去输入帐号密码(也就是谁来支付),成功显示金额等等信息,填写支付密码进行转账
  6. 转账以后无论成功与否,支付宝都会调用你的回调接口,传入数据

代码实现

业务参数代码

/**
 * 支付宝支付请求对象 所需要的参数
 */
@Data
public class PayVO {
    private String out_trade_no; // 商户订单号 必填
    private String subject; // 订单名称 必填
    private BigDecimal total_amount; // 付款金额 必填
    private String body; // 商品描述 可空
}

支付宝客户端执行

@RestController
@RequestMapping("/alipay")
@Transactional(rollbackFor = Exception.class)
public class AliPayController {

    @Resource
    AliPayConfig aliPayConfig;

    @Resource
    private ShopOrderDao shopOrderMapper;
    private static final String GATEWAY_URL = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
    private static final String FORMAT = "JSON";
    private static final String CHARSET = "utf-8";
    private static final String SIGN_TYPE = "RSA2";

    /**
     * 支付接口 传入业务参数
     * 支付是一个我向你要钱的过程,设置api参数就是为了,知道收钱的人是谁,
     * 当执行以后支付宝会返回一个登录页面,支付的人输入帐号密码。并且确定金额输入支付密码进行支付
     * @param aliPay
     * @param httpResponse
     * @throws Exception
     */
    // 这里使用Get其实不是很恰当,应该使用post,这里为了调试方便使用Get
    @GetMapping("/pay")
    public void pay(PayVO aliPay, HttpServletResponse httpResponse) throws Exception {
        // 1、根据支付宝的配置生成一个支付客户端 客户端用于去调用支付宝的API
        // 官方写法
        AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);

        
        // 2、创建一个支付请求对象
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        // 设置回调接口
        request.setNotifyUrl(aliPayConfig.getNotifyUrl());
        // 商户订单号,商户网站订单系统中唯一订单号,必填,支付宝不允许有两个相同的订单号
        // 使用uuid生成 避免重复
        aliPay.setOut_trade_no(UUID.randomUUID().toString());
        String out_trade_no = aliPay.getOut_trade_no();
        // 付款金额,必填
        BigDecimal total_amount = aliPay.getTotal_amount();
        // 订单名称,必填
        String subject = aliPay.getSubject();
        // 商品描述,可空
        String body = aliPay.getBody();
        // 设置 业务参数 是一个json对象
        // 这个json对象 支付宝后台回去识别,根据这些参数进行处理,例如 金额,订单名称,商品描述
        request.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
                + "\"total_amount\":\"" + total_amount + "\","
                + "\"subject\":\"" + subject + "\","+ "\"body\":\""+ body +"\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        // 支付完以后跳转的地址
        request.setReturnUrl("http://loaclhost:9090/hello/pay");

        // 3. 客户端执行请求
        // 客户端执行请求,拿到响应的结果,返回给浏览器
        String form = "";
        try {
            // 调用阿里的SDK生成表单
            // 会收到支付宝的响应,响应的是一个页面,一开始是登陆,然后显示金额,让用户输入密码进行付款
            form = alipayClient.pageExecute(request).getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        httpResponse.setContentType("text/html;charset=" + CHARSET);
        // 直接将完整的表单html输出到页面
        httpResponse.getWriter().write(form);
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
    }
效果

输入如下地址

http://localhost:9090/alipay/pay?subject=测试商品&total_amount=1000

支付宝返回页面

输入帐号密码(沙箱有测试的密码)

输入支付密码支付

回调接口

以下是支付宝返回给我们的信息

"gmt_create" -> "2024-03-16 22:40:26"
"charset" -> "utf-8"
"gmt_payment" -> "2024-03-16 22:40:30"
"notify_time" -> "2024-03-16 22:40:31"
"subject" -> "测试商品"
"sign" -> "XfBcgT1lIYpxYm0DzaBtLz7WjzxHxhBK4gUdmDCtD/JTAwhohqu"
"buyer_id" -> "2088722031942622"
"body" -> "null"
"invoice_amount" -> "1000.00"
"version" -> "1.0"
"notify_id" -> "2024031601222224031142620502419"
"fund_bill_list" -> "[{"amount":"1000.00","fundChannel":"ALIPAYACCOUNT"}]"
"notify_type" -> "trade_status_sync"
"out_trade_no" -> "96b14931-b0a7-49bb-aa93-498432247a4"
"total_amount" -> "1000.00"
"trade_status" -> "TRADE_SUCCESS"
"trade_no" -> "20240316220014426205023040"
"auth_app_id" -> "9021000135634074"
"receipt_amount" -> "1000.00"
"point_amount" -> "0.00"
"buyer_pay_amount" -> "1000.00"
"app_id" -> "90210001354"
"sign_type" -> "RSA2"
"seller_id" -> "20887976059"
具体实现
@PostMapping("/notify")  // 注意这里必须是POST接口
    public String payNotify(HttpServletRequest request) throws Exception {
        // 判断返回状态trade_status 支付成功是TRADE_SUCCESS
        if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
            System.out.println("=========支付宝异步回调========");

            Map<String, String> params = new HashMap<>();
            // 返回的所有元素 其中有gmt_create=2024-03-16 22:26:17, charset=utf-8, gmt_payment=2024-03-16 22:26:21, notify_time=2024-03-16 22:26:23, subject=测试商品
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                // servlet写法 通过key获取value
                params.put(name, request.getParameter(name));
            }
            System.out.println(params);
            System.out.println(params.size());
            String tradeNo = params.get("out_trade_no");
            String gmtPayment = params.get("gmt_payment");
            // 支付宝验签
            // 这里必须要初始化不然报错
            if (Factory.Payment.Common().verifyNotify(params)) {
                // 验签通过
                System.out.println("交易名称: " + params.get("subject"));
                System.out.println("交易状态: " + params.get("trade_status"));
                System.out.println("支付宝交易凭证号: " + params.get("trade_no"));
                System.out.println("商户订单号: " + params.get("out_trade_no"));
                System.out.println("交易金额: " + params.get("total_amount"));
                System.out.println("买家在支付宝唯一id: " + params.get("buyer_id"));
                System.out.println("买家付款时间: " + params.get("gmt_payment"));
                System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));
                // 更新订单未已支付
                // 做一些业务上的处理 例如说支付成功以后 更新订单状态 改为已支付等等
                ShopOrder order = new ShopOrder();
                order.setId(tradeNo);
                order.setStatus("1");
                order.setZhhifuTime(gmtPayment);
                shopOrderMapper.save(order);
            }
        }
        return "success";
    }

注意:以上代码会报错,显示空指针异常

报错信息如下

Cannot invoke "com.alipay.easysdk.kernel.Context.getCertEnvironment()" because "this.context" is null

这是因为我们并未初始化sdk基础信息

解决:

/**
 * 读取yml中的配置信息,自动填充到对应的属性
 */
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {
    private String appId;
    private String appPrivateKey;
    private String alipayPublicKey;
    private String notifyUrl;

    /**
     * 初始化sdk 这样回调接口的时候 才能知道sdk的基础信息
     */
    @PostConstruct
    public void init() {
        // 设置参数(全局只需设置一次)
        Config config = new Config();
        config.protocol = "https";
        config.gatewayHost = "openapi-sandbox.dl.alipaydev.com";
        config.signType = "RSA2";
        config.appId = this.appId;
        config.merchantPrivateKey = this.appPrivateKey;
        config.alipayPublicKey = this.alipayPublicKey;
        config.notifyUrl = this.notifyUrl;
        Factory.setOptions(config);
        System.out.println("=======支付宝SDK初始化成功=======");
    }
}

结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值