Spring Boot整合Stripe订阅支付指南

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

1.Stripe简介

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

2.准备工作

2.1.准备工作

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

2.2.添加Maven依赖

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

3.代码编写

3.1.配置Stripe密钥

        在 Spring Boot 的配置文件中(如 application.properties 或 application.yml,添加 Stripe 的 私钥 

stripe.keys.secret=你的Stripe私钥

3.2.编辑Service层代码

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

@Service
public class StripeService {@PostConstructpublic void init() {// 初始化 Stripe APIStripe.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添加订阅产品和优惠券

@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创建订阅服务产品

@RestController
@RequestMapping("/api/auth/order/commodity")
@Tag(name = "商品管理")
public class CommodityController {private final CommodityService commodityService;private final StripeService stripeService;@Autowiredpublic 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 Boot 向 Stripe 创建服务产品后,登录Stripe Disabled就可以查看到我们所创建的服务产品了

3.3.2.通过API创建优惠券

@RestController
@RequestMapping("/api/auth/order/discount")
@Tag(name = "折扣码管理")
public class DiscountController {private final DiscountService discountService;@Autowiredpublic 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

@RestController
@RequestMapping("/api/auth/pay/stripe")
@Tag(name = "Stripe-Pay")
public class StripeController {private final StripeService stripeService;private final OrderCommodityService orderCommodityService;@Autowiredpublic 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 就可以完成这个支付功能啦!

支付实参

@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 中进行查看我们具体的交易信息 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/883269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全桥PFC电路及MATLAB仿真

一、PFC电路原理概述 PFC全称“Power Factor Correction”&#xff08;功率因数校正&#xff09;&#xff0c;PFC电路即能对功率因数进行校正&#xff0c;或者说是能提高功率因数的电路。是开关电源中很常见的电路。功率因数是用来描述电力系统中有功功率&#xff08;实际使用…

【GESP】C++一级练习BCQM3145,奇数求和

一级知识点for循环分和支语句if的应用的练习题。难度不大&#xff0c;综合性略微提升&#xff0c;感觉接近但略低于一级真题水平。 题目题解详见&#xff1a;https://www.coderli.com/gesp-1-bcqm3145/ https://www.coderli.com/gesp-1-bcqm3145/https://www.coderli.com/ges…

springboot073车辆管理系统设计与实现(论文+源码)_kaic.zip

车辆管理系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了车辆管理系统的开发全过程。通过分析车辆管理系统管理的不足&#xff0c;创建了一个计算机管理车辆管理系统的方案。文章介绍了车辆管理系统的系统…

HTML标签汇总详解

一、前言 HTML 标签是用于定义网页内容结构和表现形式的标记。每个标签都有特定的含义和用途&#xff0c;通过不同的标签组合&#xff0c;可以构建出丰富多彩的网页。 二、标签的表现形式 2.1 单标签与双标签 根据标签的写法不同&#xff0c;可以将标签分为单标签和双标签。…

大数据-190 Elasticsearch - ELK 日志分析实战 - 配置启动 Filebeat Logstash

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

为微信小程序换皮肤之配置vant

微信小程序自带的控件虽然具有很好的通用性和简洁性&#xff0c;但在面对一些复杂的交互场景和个性化的设计需求时&#xff0c;可能会显得力不从心。其功能的相对基础使得开发者在实现诸如多步骤复杂表单提交、实时数据交互与可视化展示、高度定制化的界面布局等方面&#xff0…

vue3 选中对话框时,对话框右侧出一个箭头

先看下做出的效果&#xff1a; html代码&#xff0c;其中listPlan.records是后台拿到的数据进行遍历 <template><ul class"list"><li style"height: 180px;width: 95%":key"index"v-for"(item, index) in listPlan.record…

任务看板是什么?如何选择合适的任务看板工具?

一、任务看板是什么&#xff1f; 任务看板是一种可视化的项目管理工具&#xff0c;它通常以板状的形式呈现&#xff0c;将任务以卡片的形式展示在不同的列中&#xff0c;每一列代表任务的不同状态。例如&#xff0c;待办事项、进行中、已完成等。任务看板能够帮助团队成员清晰…

Android--简易计算器实现

以下实验是利用逍遥模拟器搭建的简易计算器页面 对现有功能说明&#xff1a;可实现双目运算和开方单目运算&#xff1b; 待改进&#xff1a;需要实现表达式的计算&#xff1b;以及负数参与运算&#xff1b; //XML代码<?xml version"1.0" encoding"utf-8&q…

排序(一)插入排序,希尔排序,选择排序,堆排序,冒泡排序

目录 一.排序 1.插入排序 2.希尔排序 3.选择排序 4.堆排序 5.冒泡排序 二.整体代码 1.Sort.h 2.Sort.c 3.test.c 一.排序 1.插入排序 插入排序基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为 止…

ubuntu20.04上使用 Verdaccio 搭建 npm 私有仓库

安装nvm 首先安装必要的工具&#xff1a; apt update apt install curl下载并执行nvm安装脚本&#xff1a; curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash添加环境变量&#xff08;如果安装脚本没有自动添加&#xff09;。编辑 ~/.bash…

mysql建表

作业要求&#xff1a; 根据以下需求完成图书管理系统数据库及表设计&#xff0c;并建库建表&#xff0c;并截图创建表的详细信息(desc 表名),不用添加数据 1. 用户表: 字段: 姓名&#xff0c;用户名&#xff0c;密码&#xff0c;电话&#xff0c;住址&#xff0c;专业及年级…

命名空间std, using namespace std

命名空间std&#xff0c;using namespace std 在标准C以前&#xff0c;都是用#include<iostream.h>这样的写法的&#xff0c;因为要包含进来的头文件名就是iostream.h。标准C引入了名字空间的概念&#xff0c;并把iostream等标准库中的东东封装到了std名字空间中&#x…

系统设计-通用用户权限管理系统

通用用户权限管理系统 一、系统安全二、登录授权三、系统内部安全1. 相关实体1.1 实体关系(ER)2. 菜单权限3. 接口权限3.1 权限获取3.2 接口调用鉴权4. 数据权限四、其他一个没有权限控制的系统,是非常不安全的。 在日常业务运营的系统中台,基本都会存在用户的菜单权限控制,…

【华为路由】OSPF多区域配置

网络拓扑 设备接口地址 设备 端口 IP地址 RTA Loopback 0 1.1.1.1/32 G0/0/0 10.1.1.1/24 RTB Loopback 0 2.2.2.2/32 G0/0/0 10.1.1.2/24 G0/0/1 10.1.2.1/24 RTC Loopback 0 3.3.3.3/32 G0/0/0 10.1.2.2/24 G0/0/1 10.1.3.1/24 RTD Loopback 0 4.4.4…

与ai聊我的代码架构

以包目录结构模块&#xff0c;以*.py脚本收纳模块。 (笔记模板由python脚本于2024年10月25日 18:39:10创建&#xff0c;本篇笔记适合编程基础的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《…

在线教育(培训+考试)/企业培训-企业培训平台-企业培训平台系统-企业内部培训系统-在线教育-Java语言开发

介绍 企业培训平台支持企业培训考试全流程&#xff0c;在线学习、在线考试&#xff0c;成熟的企业培训考试解决方案&#xff0c;充分满足企业培训需求。 独立部署&#xff0c;仅内部员工登录使用&#xff0c;稳定、安全、高效&#xff0c;满足企业、政府、教育行业的各种在线学…

STM32-Modbus协议(一文通)

Modbus协议原理 RT-Thread官网开源modbus RT-Thread官方提供 FreeModbus开源。 野火有移植的例程。 QT经常用 libModbus库。 Modbus是什么&#xff1f; Modbus协议&#xff0c;从字面理解它包括Mod和Bus两部分&#xff0c;首先它是一种bus&#xff0c;即总线协议&#xff0c;和…

Maya---骨骼绑定

调节骨骼大小 回车键确认骨骼 FK子集跟父集走 IK子集不跟父集走 前视图中按shift键添加骨骼 清零、删除历史记录&#xff0c;创建新的物体

多元线性回归【正规方程/sklearn】

多元线性回归【正规方程/sklearn】 1. 基本概念1.1 线性回归1.2 一元简单线性回归1.3 最优解1.4 多元线性回归 2. 正规方程求最优解2.1 线性回归的损失函数&#xff08;最小二乘法&#xff09;2.2 推导正规方程2.3 正规方程练习2.4 使用sklearn计算多元线性方程2.5 凸函数 3. 线…