在当今的在线支付市场中,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 {
@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添加订阅产品和优惠券
@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;
@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 Boot 向 Stripe 创建服务产品后,登录Stripe Disabled就可以查看到我们所创建的服务产品了
3.3.2.通过API创建优惠券
@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
@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 就可以完成这个支付功能啦!
支付实参
@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 中进行查看我们具体的交易信息