SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进

目录

  • 引出
  • git代码仓库
  • 准备工作
    • 支付宝沙箱api
    • 内网穿透 [natapp.cn](https://natapp.cn/#download)
  • springboot整合—极简实现版
    • 1.导包+配置文件
    • 2.controller层代码
    • 3.进行支付流程
    • 4.支付成功回调
  • 依赖注入的改进
    • 1.整体结构
    • 2.pom.xml文件依赖
    • 3.配置文件
    • 4.配置类,依赖注入
    • 5.支付商品id,价格,名称
    • 6.controller层进行支付和回调
    • 7.主启动类
  • 总结

引出


1.springboot整合支付宝沙箱支付;
2.准备工作:沙箱api,内网穿透;
3.极简实现理解支付,异步回调等;
4.按照spring依赖注入的思想改造基础demo;

git代码仓库

https://gitee.com/pet365/springboot-alipay

准备工作

支付宝沙箱api

支付宝开放平台 (alipay.com)

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第1张图片

支付参数

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第2张图片

内网穿透 natapp.cn

natapp.cn官网

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第3张图片

启动和配置

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第4张图片

springboot整合—极简实现版

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第5张图片

1.导包+配置文件


        <dependency>
            <groupId>com.alipay.sdkgroupId>
            <artifactId>alipay-sdk-javaartifactId>
            <version>4.34.0.ALLversion>
        dependency>
server:
  port: 9090


# 日志需要配置一下 @Slf4j
logging:
  level:
    com.tianju.alipay: debug

2.controller层代码

订单ID,需要唯一;价格;物品名称(支付主题)

http://localhost:9090/api/ali/pay?orderId=202307302321&price=12.56&subject=iPhone

用户名:[email protected]

密码:111111

AliPayController.java文件

package com.tianju.alipay.controller;


import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 支付宝的支付接口
 * 用户名:[email protected]
 * 密码:111111
 */

// http://localhost:9090/api/ali/pay?orderId=202307302321&price=12.56&subject=iPhone
@RestController
@RequestMapping("/api/ali")
@Slf4j // tips:如果用debug记得在application.yml文件中配置一下日志级别
public class AliPayController {


    String alipayPublicKey = "";

    @GetMapping("/pay")
    public void payOrder(String orderId, String price, String subject, HttpServletResponse httpResponse) throws AlipayApiException, IOException {
        // 1.准备需要的参数,私匙,支付公匙,appid,沙箱支付平台的url
        String privateKey = "";
        String alipayPublicKey = "";
        String appId = "9021000123613164";
        String serverUrl = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";

        // 2.把需要配置的参数set进AlipayConfig类中
        com.alipay.api.AlipayConfig alipayConfig = new com.alipay.api.AlipayConfig();
        alipayConfig.setServerUrl(serverUrl);
        alipayConfig.setAppId(appId);
        alipayConfig.setPrivateKey(privateKey);
        alipayConfig.setFormat("json");
        alipayConfig.setAlipayPublicKey(alipayPublicKey);
        alipayConfig.setCharset("UTF8");
        alipayConfig.setSignType("RSA2");

        // 3.创建支付宝的默认的客户端
        AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);

        // 4.准备支付的参数,包括需要支付的订单的id,价格,物品名称,
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
//        model.setOutTradeNo("2023073022083620");
        model.setOutTradeNo(orderId); // 需要支付的订单id,自定义的订单id,不能重复,唯一,已经支付的无法继续支付
        model.setTotalAmount(price); // 需要支付的钱 model.setTotalAmount("88.88");
        model.setSubject(subject); // 要支付的物品,比如 model.setSubject("Iphone6 16G");
        model.setProductCode("FAST_INSTANT_TRADE_PAY");

        // 5.创建支付的请求,把上面的准备支付的参数进行set
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setBizModel(model);

        /**
         * 设置支付宝异步回调,这个需要用内网穿透
         */
        request.setNotifyUrl("http://uw7ayu.natappfree.cc/api/ali/notify");

        /**
         * 设置支付宝支付成功返回的页面
         * 这里让支付成功直接回到static下的一个静态页面中
         */
        request.setReturnUrl("http://localhost:9090/aliPayReturn.html");

        // 6.调用ali客户端alipayClient,用客户端执行请求,获取响应,获取.getBody(),拿到form表单
        // 执行请求,拿到响应的结果,返回给浏览器
        String form = "";
        try {
            form = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单
            log.debug(">>>>>>getAliPayFrom");
            System.out.println("getAliPayFrom");
            System.out.println(form);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        // 表单数据作为响应返回给前端,显示前端页面
        /**
         * 
* * *
* */
httpResponse.setContentType("text/html;charset=UTF-8"); // 设置头部 httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面 httpResponse.getWriter().flush(); httpResponse.getWriter().close(); } /** * 支付宝异步回调 * request.setNotifyUrl(""); * http://localhost:9090/api/ali/notify * 上面这个网址用内网穿透代理一下 * http://jqdxgm.natappfree.cc/api/ali/notify * 此时支付宝就能回调到这个页面 */ @PostMapping("/notify") public void aliPayNotify(HttpServletRequest request) throws AlipayApiException { log.debug(">>>>>>>支付宝异步回调"); Map<String, String[]> parameterMap = request.getParameterMap(); // [gmt_create, charset, gmt_payment, notify_time, subject, sign, buyer_id, invoice_amount, version, notify_id, // fund_bill_list, notify_type, out_trade_no, total_amount, trade_status, trade_no, auth_app_id, receipt_amount, // point_amount, buyer_pay_amount, app_id, sign_type, seller_id] Set<String> keySet = parameterMap.keySet(); // 获取http请求里面的所有键 /** * paramsMap如下: * {gmt_create=2023-07-31 15:54:11, charset=UTF8, * gmt_payment=2023-07-31 15:54:26, * notify_time=2023-07-31 15:54:27, * sign=fsCABNzc5hax4mwwMULluDiEAT70Kqj77uTMcCgSi82AU6tP5LGbXucEvP7CbvjXrYo5g3hrz5xRQAwddE7qU9Qyrg0v3EnearJBcW4It6N+VNBQ7yfY/W79eKRSKspLBKHRa21RILjyRrmQYG4Cw8R7twP7y0lDCOE8j3rV6ZyGfhiQ7EXp49d6HpgcIT1NjgJjQYSyJFdgyqkzFljKRfbhwPFAtubsmd8IcJCU7XI3YosSKnDhQaCA6ec4dmQiWtvcTbOLNR/r2Sou7rCnI7s1lc8pKeEsuacWTZW2FVR7hdHoLZ/expaRQIt+dNzA86lwQxu3SRCQ9wNTPICv1A==, * buyer_id=2088722005286475, invoice_amount=12.56, version=1.0, notify_id=2023073101222155427086470500776505, * fund_bill_list=[{"amount":"12.56","fundChannel":"ALIPAYACCOUNT"}], * notify_type=trade_status_sync, * subject=iPhone, // 物品名称 * out_trade_no=202307311553, // 进行支付的订单id,唯一,商家自定义,即支付时传入的 String orderId * total_amount=12.56, // 总价格 * trade_status=TRADE_SUCCESS, // 支付状态 * trade_no=2023073122001486470500697216, auth_app_id=9021000123613164, * receipt_amount=12.56, point_amount=0.00, * buyer_pay_amount=12.56, app_id=9021000123613164, sign_type=RSA2, seller_id=2088721005318559} */ Map<String,String> paramsMap = new HashMap<>(); // 专门用来放置请求里面的参数 for(String key : keySet){ paramsMap.put(key, request.getParameter(key)); } System.out.println("*************"); System.out.println(paramsMap); // 验证签名 String sign = paramsMap.get("sign"); String contentV1 = AlipaySignature.getSignCheckContentV1(paramsMap); boolean rsa256CheckSignature = AlipaySignature.rsa256CheckContent(contentV1, sign, alipayPublicKey, "UTF-8"); if (rsa256CheckSignature && "TRADE_SUCCESS".equals(paramsMap.get("trade_status"))){ // 签名验证成功 并且 支付宝回调的状态是成功状态 TRADE_SUCCESS log.info("在{},买家{}进行订单{}的付款,交易名称{},付款金额{}", paramsMap.get("gmt_payment"),paramsMap.get("buyer_id"),paramsMap.get("out_trade_no"),paramsMap.get("subject"),paramsMap.get("total_amount")); // 支付成功,修改数据库中该订单的状态 // 比如:流程,根据订单ID查询出一条数据,修改该条订单的数据,或者只有支付成功,才给数据库里面新增一条数据 } } }

3.进行支付流程

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第6张图片

支付密码输入

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第7张图片

支付成功

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第8张图片

4.支付成功回调

设置异步回调

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第9张图片

启动内网穿透

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第10张图片

设置地址

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第11张图片

异步回调流程解析

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第12张图片

支付宝支付成功跳转

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第13张图片

跳转成功

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第14张图片

依赖注入的改进

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第15张图片

1.整体结构

SpringBoot项目(支付宝整合)——springboot整合支付宝沙箱支付 & 从极简实现到IOC改进_第16张图片

2.pom.xml文件依赖


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <groupId>com.tianju.alipaygroupId>
    <artifactId>springboot-alipayartifactId>
    <version>1.0-SNAPSHOTversion>

    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    properties>

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.5.4version>
        <relativePath/> 
    parent>


    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

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

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>2.0.12version>
        dependency>


        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <scope>testscope>
        dependency>

        
        <dependency>
            <groupId>com.alipay.sdkgroupId>
            <artifactId>alipay-sdk-javaartifactId>
            <version>4.34.0.ALLversion>
        dependency>


    dependencies>
project>

3.配置文件

applicaiton.yml

server:
  port: 9090


# 日志需要配置一下 @Slf4j
logging:
  level:
    com.tianju.alipay: debug

alipay.properties

ali.privateKey = 
ali.alipayPublicKey = 
ali.appId = 9021000123613164
ali.serverUrl = https://openapi-sandbox.dl.alipaydev.com/gateway.do

ali.returnUrl = http://localhost:9090/aliPayReturn.html
ali.notifyUrl = http://uw7ayu.natappfree.cc/api/ali/notify

4.配置类,依赖注入

package com.tianju.alipay.config;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:config/alipay.properties")
@Getter
public class AlipayConfig {
    @Value("${ali.serverUrl}")
    private String serverUrl;
    @Value("${ali.appId}")

    private String appId;
    @Value("${ali.alipayPublicKey}")
    private String alipayPublicKey;

    @Value("${ali.privateKey}")
    private String privateKey;

    @Bean
    public AlipayClient alipayClient() throws AlipayApiException {
        // 2.把需要配置的参数set进AlipayConfig类中
        com.alipay.api.AlipayConfig alipayConfig = new com.alipay.api.AlipayConfig();
        alipayConfig.setServerUrl(serverUrl);
        alipayConfig.setAppId(appId);
        alipayConfig.setPrivateKey(privateKey);
        alipayConfig.setFormat("json");
        alipayConfig.setAlipayPublicKey(alipayPublicKey);
        alipayConfig.setCharset("UTF8");
        alipayConfig.setSignType("RSA2");
        return new DefaultAlipayClient(alipayConfig);
    }
}

5.支付商品id,价格,名称

package com.tianju.alipay.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Alipay {
    private String orderId; // 商家自定义的订单编号,唯一
    private String price; // 商品价格
    private String subject; // 支付主题
}

6.controller层进行支付和回调

package com.tianju.alipay.controller;


import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.tianju.alipay.entity.Alipay;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 支付宝的支付接口
 * 用户名:[email protected]
 * 密码:111111
 */

// http://localhost:9090/api/ali/pay?orderId=202307302321&price=12.56&subject=iPhone
@RestController
@RequestMapping("/api/ali")
@Slf4j
public class AliPayController {

    @Autowired
    private AlipayClient alipayClient;

    @Value("${ali.alipayPublicKey}")
    private String alipayPublicKey;
    @Value("${ali.returnUrl}")
    private String returnUrl; // 支付成功返回的页面
    @Value("${ali.notifyUrl}")
    private String notifyUrl; // 支付宝支付成功异步回调的页面,需要内网穿透

    @GetMapping("/pay")
    public void payOrder(Alipay alipay, HttpServletResponse httpResponse) throws IOException {

        // 4.准备支付的参数,包括需要支付的订单的id,价格,物品名称,
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
        model.setOutTradeNo(alipay.getOrderId()); // 需要支付的订单id,自定义的订单id,不能重复,唯一,已经支付的无法继续支付
        model.setTotalAmount(alipay.getPrice()); // 需要支付的钱 model.setTotalAmount("88.88");
        model.setSubject(alipay.getSubject()); // 要支付的物品,比如 model.setSubject("Iphone6 16G");
        model.setProductCode("FAST_INSTANT_TRADE_PAY");

        // 5.创建支付的请求,把上面的准备支付的参数进行set
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setBizModel(model);

        /**
         * 设置支付宝异步回调,这个需要用内网穿透
         */
        request.setNotifyUrl(notifyUrl);

        /**
         * 设置支付宝支付成功返回的页面
         * 这里让支付成功直接回到static下的一个静态页面中
         */
        request.setReturnUrl(returnUrl);

        // 6.调用ali客户端alipayClient,用客户端执行请求,获取响应,获取.getBody(),拿到form表单
        // 执行请求,拿到响应的结果,返回给浏览器
        String form = "";
        try {
            form = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单
            log.debug(">>>>>>getAliPayFrom");
            System.out.println("getAliPayFrom");
            System.out.println(form);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        // 表单数据作为响应返回给前端,显示前端页面
        httpResponse.setContentType("text/html;charset=UTF-8"); // 设置头部
        httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
    }

    /**
     * 支付宝异步回调
     * request.setNotifyUrl("");
     * http://localhost:9090/api/ali/notify
     * 上面这个网址用内网穿透代理一下
     * http://jqdxgm.natappfree.cc/api/ali/notify
     * 此时支付宝就能回调到这个页面
     */
    @PostMapping("/notify")
    public void aliPayNotify(HttpServletRequest request) throws AlipayApiException {
        log.debug(">>>>>>>支付宝异步回调");
        Map<String, String[]> parameterMap = request.getParameterMap();
        Set<String> keySet = parameterMap.keySet(); // 获取http请求里面的所有键

        /**
         * paramsMap如下:
         * {gmt_create=2023-07-31 15:54:11, charset=UTF8,
         * gmt_payment=2023-07-31 15:54:26,
         * notify_time=2023-07-31 15:54:27,
         * sign=fsCABNzc5hax4mwwMULluDiEAT70Kqj77uTMcCgSi82AU6tP5LGbXucEvP7CbvjXrYo5g3hrz5xRQAwddE7qU9Qyrg0v3EnearJBcW4It6N+VNBQ7yfY/W79eKRSKspLBKHRa21RILjyRrmQYG4Cw8R7twP7y0lDCOE8j3rV6ZyGfhiQ7EXp49d6HpgcIT1NjgJjQYSyJFdgyqkzFljKRfbhwPFAtubsmd8IcJCU7XI3YosSKnDhQaCA6ec4dmQiWtvcTbOLNR/r2Sou7rCnI7s1lc8pKeEsuacWTZW2FVR7hdHoLZ/expaRQIt+dNzA86lwQxu3SRCQ9wNTPICv1A==,
         * buyer_id=2088722005286475, invoice_amount=12.56, version=1.0, notify_id=2023073101222155427086470500776505,
         * fund_bill_list=[{"amount":"12.56","fundChannel":"ALIPAYACCOUNT"}],
         * notify_type=trade_status_sync,
         * subject=iPhone, // 物品名称
         * out_trade_no=202307311553, // 进行支付的订单id,唯一,商家自定义,即支付时传入的 String orderId
         * total_amount=12.56, // 总价格
         * trade_status=TRADE_SUCCESS, // 支付状态
         * trade_no=2023073122001486470500697216, auth_app_id=9021000123613164,
         * receipt_amount=12.56, point_amount=0.00,
         * buyer_pay_amount=12.56, app_id=9021000123613164, sign_type=RSA2, seller_id=2088721005318559}
         */
        Map<String,String> paramsMap = new HashMap<>(); // 专门用来放置请求里面的参数
        for(String key : keySet){
            paramsMap.put(key, request.getParameter(key));
        }
        System.out.println("*************");
        System.out.println(paramsMap);

        // 验证签名
        String sign = paramsMap.get("sign");
        String contentV1 = AlipaySignature.getSignCheckContentV1(paramsMap);
        boolean rsa256CheckSignature = AlipaySignature.rsa256CheckContent(contentV1, sign, alipayPublicKey, "UTF-8");
        if (rsa256CheckSignature && "TRADE_SUCCESS".equals(paramsMap.get("trade_status"))){
            // 签名验证成功 并且 支付宝回调的状态是成功状态 TRADE_SUCCESS
            log.info("在{},买家{}进行订单{}的付款,交易名称{},付款金额{}",
                    paramsMap.get("gmt_payment"),paramsMap.get("buyer_id"),paramsMap.get("out_trade_no"),paramsMap.get("subject"),paramsMap.get("total_amount"));

            // 支付成功,修改数据库中该订单的状态
            // 比如:流程,根据订单ID查询出一条数据,修改该条订单的数据,或者只有支付成功,才给数据库里面新增一条数据
        }
    }
}

7.主启动类

package com.tianju.alipay;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AliPayApp {
    public static void main(String[] args) {
        SpringApplication.run(AliPayApp.class, args);

    }
}

总结

1.springboot整合支付宝沙箱支付;
2.准备工作:沙箱api,内网穿透;
3.极简实现理解支付,异步回调等;
4.按照spring依赖注入的思想改造基础demo;

你可能感兴趣的:(SpringBoot,spring,boot,后端,java)