Spring Boot整合Stripe订阅支付指南

在当今的在线支付市场中,Stripe 作为一款一体化的全球支付平台,因其易用性和广泛的支付方式支持,得到了许多企业的青睐。本文将详细介绍如何在 Spring Boot 项目中整合 Stripe 实现订阅支付功能。

1.Stripe简介

Stripe 是一家为个人或公司提供网上接受付款服务的科技公司,无需开设商家账户即可在线接受付款。它支持多种支付方式,覆盖全球 195 个以上的国家和地区,具备高度的安全性和稳定性。通过内置的优化功能和一键结账,Stripe 能够显著提高转化率,为商家提供无缝的客户体验。

2.准备工作

2.1.准备工作

前往 Stripe 官网 注册一个账号,邮箱地址可以是国内的。注册完成后,获取到测试用的秘钥,用于后续的开发和测试。

2.2.添加Maven依赖

XML 复制代码
<dependency>  
    <groupId>com.stripe</groupId>  
    <artifactId>stripe-java</artifactId>  
    <version>最新版本号</version>  <!--  博主这里用的是27.0.0版本  -->
</dependency>

3.代码编写

3.1.配置Stripe密钥

Spring Boot 的配置文件中**(如 application.propertiesapplication.yml)** ,添加Stripe私钥

bash 复制代码
stripe.keys.secret=你的Stripe私钥

3.2.编辑Service层代码

3.2.1.在 StripeService 中使用 Stripe API创建订阅计划:

java 复制代码
@Service
public class StripeService {

    @PostConstruct
    public void init() {
        // 初始化 Stripe API
        Stripe.apiKey = StripeKey.getTestKey();
    }

    /**
     * 创建 Stripe 客户
     */
    public Customer createCustomer(String email, String paymentMethodId) throws StripeException {
        CustomerCreateParams customerParams = CustomerCreateParams.builder()
                .setEmail(email)
                .setPaymentMethod(paymentMethodId)
                .build();

        return Customer.create(customerParams);
    }

    /**
     * 创建 Stripe 订阅
     * @param customerId Stripe 客户 ID
     * @param paymentMethodId 支付方式的 ID
     * @param priceId 订阅计划的价格 ID
     * @return 创建的订阅对象
     */
    public Subscription createSubscription(String customerId, String paymentMethodId, String priceId, String couponId) throws StripeException {
        SubscriptionCreateParams subscriptionParams = SubscriptionCreateParams.builder()
                .setCustomer(customerId)
                .addItem(
                        SubscriptionCreateParams.Item.builder()
                                .setPrice(priceId)
                                .build()
                )
                .setDefaultPaymentMethod(paymentMethodId)
                // 添加优惠券
                .addDiscount(
                        SubscriptionCreateParams.Discount.builder()
                                .setCoupon(couponId) // 关联创建好的 coupon
                                .build()
                )
                .build();

        return Subscription.create(subscriptionParams);
    }

}

通常情况下,我们的连续包月服务首月的价格跟之后每月的价格是不一样的。

  • 这里博主将设置首月价格为**3.99** ,之后每月的价格为**9.99**
  • 博主通过 一次性优惠券的方式完成这个首月折扣的功能
  • 如果需要设置首月价格为$9.99,之后每月价格为$3.99,可以考虑使用**永久优惠券方案**

3.2.2.通过API向Stripe添加订阅产品和优惠券

java 复制代码
@Service
public class StripeService {

    /**
     * 创建 Stripe 订阅产品
     */
    public Product createSubscriptionProduct(String productName, String description) throws Exception {
        ProductCreateParams params = ProductCreateParams.builder()
                .setName(productName)
                .setDescription(description)
                .setType(ProductCreateParams.Type.SERVICE) // 订阅产品通常是服务型产品
                .build();

        // 创建产品
        return Product.create(params);
    }

    /**
     * 创建 Stripe 价格计划
     */
    public Price createMonthlyPrice(String productId, Long unitAmount) throws Exception {
        PriceCreateParams params = PriceCreateParams.builder()
                .setProduct(productId) // 使用创建的产品ID
                .setCurrency("USD") // 设置货币类型,例如USD
                .setRecurring(PriceCreateParams.Recurring.builder()
                        .setInterval(PriceCreateParams.Recurring.Interval.MONTH) // 按月计费
                        .build())
                .setUnitAmount(unitAmount) // 设置金额(以分为单位,10美元即为1000)
                .build();

        // 创建价格计划
        return Price.create(params);
    }

    /**
     * 创建 Stripe 折扣
     */
    public String createdStripeDiscount(DiscountModel model) {

        Map<String, Object> couponParams = new HashMap<>();

        try {
            // 设置折扣方式
            if (Objects.equals(model.getDiscountMethod(), "percent_off")) {
                // 按百分比折扣
                couponParams.put(model.getDiscountMethod(), model.getProportion());
            }

            // 按具体金额折扣
            long price = Math.round(Float.parseFloat(model.getDiscountPrice())) * 100L;
            couponParams.put(model.getDiscountMethod(), price);

            // 设置货币类型
            couponParams.put("currency", "USD");

            if (model.getDiscountType().equals("repeating")) {
                // 有效期: 月份整数
                couponParams.put("duration_in_months", model.getDurationInMonths());
            }
            // 设置折扣券类型
            couponParams.put("duration", model.getDiscountType());

            return Coupon.create(couponParams).getId();

        } catch (Exception e) {

            return e.getMessage();
        }
    }
}

3.3.编辑Controller层代码

3.3.1.通过API创建订阅服务产品

java 复制代码
@RestController
@RequestMapping("/api/auth/order/commodity")
@Tag(name = "商品管理")
public class CommodityController {

    private final CommodityService commodityService;

    private final StripeService stripeService;

    @Autowired
    public CommodityController(CommodityService commodityService, StripeService stripeService) {

        this.commodityService = commodityService;

        this.stripeService = stripeService;
    }

    @PostMapping("/created")
    @Operation(summary = "新增商品")
    public Result<Object> created(@RequestHeader("Authorization")String token, @RequestBody CommodityModel model) {

        String jwt = token.substring(11);

        try {
            // Step 1: 创建订阅产品
            Product product = stripeService.createSubscriptionProduct(model.getCommodityName(), model.getDescription());

            // Step 2: 为产品创建价格计划

            // 将 double 价格转换为以分为单位的 long 类型
            Long unitAmountInCents = Math.round(model.getUnitPrice()) * 100;

            Price price = stripeService.createMonthlyPrice(product.getId(), unitAmountInCents);

            // 将 Stripe 产品 ID 与 Stripe 产品价格 ID 存储到商品实体中
            model.setStripeId(product.getId());
            model.setStripePriceId(price.getId());

            // 更新数据库
            int result = commodityService.createdCommodity(jwt, model);

            return result >= 1 ? Result.SUCCESS("Created Success !") : Result.FAILURE("Created Fail !");

        } catch (Exception e) {
            // 错误处理
            return Result.FAILURE(e.getMessage());
        }
        
    }

}

当我们通过 Spring BootStripe 创建服务产品后,登录Stripe Disabled就可以查看到我们所创建的服务产品了

3.3.2.通过API创建优惠券

java 复制代码
@RestController
@RequestMapping("/api/auth/order/discount")
@Tag(name = "折扣码管理")
public class DiscountController {

    private final DiscountService discountService;

    @Autowired
    public DiscountController(DiscountService discountService) {
        this.discountService = discountService;
    }

    @PostMapping("/created")
    @Operation(summary = "新增[折扣码]", parameters = {
            @Parameter(
                    name = "Authorization",
                    description = "TOKEN",
                    in = ParameterIn.HEADER,
                    required = true,
                    schema = @Schema(type = "string")
            )
    })
    public Result<Void> createDiscount(@RequestBody DiscountModel model) {

        int result = discountService.createdDiscount(model);

        return result >= 1 ? Result.SUCCESS() : Result.FAILURE();
    }

}

同样的,这里我们创建优惠券[这里博主以一次性优惠券为例]

Stripe 中通过这个优惠券ID自动进行费用折扣计算,Stripe支持按比例折扣和按具体金额折扣方式进行优惠折算

3.3.3.编写Stripe支付API

java 复制代码
@RestController
@RequestMapping("/api/auth/pay/stripe")
@Tag(name = "Stripe-Pay")
public class StripeController {

    private final StripeService stripeService;

    private final OrderCommodityService orderCommodityService;

    @Autowired
    public StripeController(StripeService stripeService, OrderCommodityService orderCommodityService) {

        this.stripeService = stripeService;

        this.orderCommodityService = orderCommodityService;
    }

@PostMapping("/create")
    @Operation(summary = "Stripe_Pay", parameters = {
            @Parameter(
                    name = "Authorization",
                    description = "TOKEN",
                    in = ParameterIn.HEADER,
                    required = true,
                    schema = @Schema(type = "string")
            )
    })
    public Result<Object> createSubscription(@RequestBody PayModel model) {

        try {
            // Step 1: 创建客户
            Customer customer = stripeService.createCustomer(model.getEmail(), model.getPaymentMethodId());

            // Step 2: 创建订阅
            Subscription subscription = stripeService.createSubscription(customer.getId(), model.getPaymentMethodId(), model.getPriceId(), model.getDiscountId());

            // Step 3: 检查支付状态
            Invoice invoice = Invoice.retrieve(subscription.getLatestInvoice());
            PaymentIntent paymentIntent = PaymentIntent.retrieve(invoice.getPaymentIntent());

            if ("succeeded".equals(paymentIntent.getStatus())) {
                // 支付成功,修改订单状态
                int i = orderCommodityService.getBaseMapper().updateOrderById(1, model.getOrderId());

                return i >= 1 ? Result.SUCCESS("Payment succeeded !") : Result.FAILURE("Payment failed !");

            } else {
                // 支付失败或处理中
                return Result.FAILURE("Payment status: " + paymentIntent.getStatus());
            }

        } catch (StripeException e) {

            return Result.FAILURE(e.getMessage());
        }
    }

}

编写完接口之后,我们就可以在前端生成 paymentMethodId ,进行支付的时候将下列的参数上传到 Spring Boot就可以完成这个支付功能啦!

支付实参

java 复制代码
@Data
public class PayModel {

    @Schema(description = "用户邮箱")
    private String email;

    @Schema(description = "订单ID")
    private String orderId;

    @Schema(description = "Stripe 支付方式ID")
    private String paymentMethodId;

    @Schema(description = "Stripe 价格ID")
    private String priceId;

    @Schema(description = "Stripe 客户ID")
    private String discountId;

}

最终支付完成后,可以在 Stripe Dashboard中进行查看我们具体的交易信息

相关推荐
皮皮林5511 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友1 小时前
什么是OpenSSL
后端·安全·程序员
bobz9651 小时前
mcp 直接操作浏览器
后端
前端小张同学4 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook4 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康4 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在5 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net
文心快码BaiduComate5 小时前
文心快码入选2025服贸会“数智影响力”先锋案例
前端·后端·程序员
neoooo5 小时前
🌐 Cloudflare Tunnel vs ZeroTier:两个世界的内网穿透哲学
后端
卡尔特斯5 小时前
Android Kotlin 项目代理配置【详细步骤(可选)】
android·java·kotlin