Stripe Web 购买集成
图片被吞了可以来这里看:https://juejin.cn/post/7346388511338381364
1. 准备事项
- Stripe 账号
- 域名以及配套的网站
- Stripe 账号付款信息
- 公钥和私钥
2. 配置产品以及价格
可以通过 API 或者 Stripe 管理后台来进行配置
产品:就是商品,只需要配置一个名称和一个类型(用于计算税额)

价格:价格有定期和一次性两种收费方式,定期其实就是订阅。价格实体非常灵活,适合多种场景,一般就使用固定费率的一次性付款和定期付款。

3. 设计一下流程

4. 代码集成
4.1 依赖导入
stripe/stripe-java: Java library for the Stripe API. (github.com)
<dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>23.3.0</version>
</dependency>
4.2 配置
# 公钥
stripe.key=pk_test_51Nxxxx
# 私钥
stripe.secret=sk_test_51xxxx
# webhook 密钥签名
stripe.endpoint_secret=whsec_Tcxxxx
@Data
@Configuration
@ConfigurationProperties(prefix = "stripe")
public class StripeConfig { private String key;private String secret;private String endpointSecret;@Bean public StripeClient stripeClient() {return new StripeClient(secret);}
}
4.3 创建收银
Stripe 中有两种方式能进行收款,Stripe-hosted page 和 Embedded form。
Stripe-hosted page:指的是收费的时候跳转到 Stripe 提供的一个收银台页面进行付款。
Embedded form:则是需要高度自定义页面的产品使用,或者是客户端。
文档:Stripe Checkout | Stripe 文档
Demo: docs.stripe.com/checkout/quickstart
Web 端一般使用 Stripe-hosted page 来简化开发,像 ChatGPT 也是使用这种方式。

后端创建收银台
public CheckoutCreateResult create(CheckoutCreateRequest request) {// 查询或者创建客户String customerId = queryOrCreateCustomer();// 查询价格idString priceId = queryPrice();// 构建成功URL和取消URLUriComponents successUrl = UriComponentsBuilder.fromHttpUrl(request.getSuccessUrl()).queryParam("checkout_id", checkoutId).queryParam("receipt", "{CHECKOUT_SESSION_ID}") // 模板变量 https://stripe.com/docs/payments/checkout/custom-success-page#modify-success-url.build();UriComponents cancelUrl = UriComponentsBuilder.fromHttpUrl(request.getCancelUrl()).queryParam("checkout_id", checkoutId).build();// 创建checkout 收银台SessionCreateParams.Builder builder = SessionCreateParams.builder().setSuccessUrl(successUrl.toUriString()).setCancelUrl(cancelUrl.toUriString())// 指定付款用户.setCustomer(customerId)// 自动扣税.setAutomaticTax(SessionCreateParams.AutomaticTax.builder().setEnabled(false).build())// 购买项目:和订单明细类似.addLineItem(SessionCreateParams.LineItem.builder()// 数量.setQuantity(request.getCount().longValue())// 价格.setPrice(priceId).build())// 元数据:额外附加的数据。 webhook 通知的时候可以取出来.putAllMetadata(ImmutableMap.of(MetaDataKey.CHECKOUT_ID, checkoutId,MetaDataKey.APP_ID, request.getAppId()))// 是否允许优惠码.setAllowPromotionCodes(Boolean.TRUE);if (productPrice.getPriceType() == PriceTypeEnum.RECURRING) {// 定期价格,最后会创建订阅对象。可以为付款成功后生成的订阅对象设置一些数据builder.setMode(SessionCreateParams.Mode.SUBSCRIPTION).setSubscriptionData(// 试用期SessionCreateParams.SubscriptionData.builder().putMetadata(MetaDataKey.APP_ID, request.getAppId()).build());} else {// 一次性价格,最后会创建付款对象。可以为付款成功后生成的付款对象设置一些数据builder.setMode(SessionCreateParams.Mode.PAYMENT).setPaymentIntentData(SessionCreateParams.PaymentIntentData.builder().putMetadata(MetaDataKey.APP_ID, request.getAppId()).build());}SessionCreateParams params = builder.build();/*.addDiscount( // 优惠券SessionCreateParams.Discount.builder().setCoupon("bBfCjIMt").build())*/Session session = null;try {session = stripeClient.checkout().sessions().create(params);} catch (StripeException e) {log.error("failed to create checkout session. {}, {}, {}", request, customerId, priceId, e);throw new RuntimeException("failed to create checkout session: "+ e.getMessage());}return new CheckoutCreateThirdResult()// checkout session 的 id.setId(session.getId())// 可供用户进行付款的页面链接,前端直接打开即可跳转到Stripe.setTokenThird(session.getUrl());
}
4.4 完成收银
4.4.1 前端提交
用户付款完成后,Stripe 会将页面重定向到创建 Checkout Session 时设置的 success_url。
页面可以从URL中获取到订单id和sessionId来进一步调用后端接口完成收银。
4.4.2 接收 Webhook
用户付款完成后,Stripe 的后台还会将对应的事件通过 WebHook 的方式 POST 我们预先提供的接口。
第一步,先提供一个 Webhook 回调接口
本地测试的方式不是很友好,可以使用内网穿透工具将请求转到本地来进行调试
@RestController
@Slf4j
public class WebhookController {@Resourceprivate StripeConfig stripeConfig;@Resourceprivate List<WebhookHandler> webhookHandlers;@PostMapping("/webhook")public Object handle(@RequestHeader("Stripe-Signature") String sigHeader, @RequestBody String payload) {log.info("stripe webhook payload: {}", payload);return webhook(payload, sigHeader, stripeConfig.endpointSecret());}private Object webhook(String payload, String sigHeader, String endpointSecret) {Event event;try {event = Webhook.constructEvent(payload, sigHeader, endpointSecret);StripeEventType stripeEventType = StripeEventType.convert(event.getType());webhookHandlers.stream().filter(webhookHandler -> webhookHandler.supports(stripeEventType)).findFirst().get().handle(event);} catch (Exception e) {log.error("failed to handle webhook event. {}, {}", sigHeader, payload, e);return ResponseEntity.status(500).body(e.getMessage());}return ResponseEntity.ok().body("OK");}
}
Stripe 事件枚举
public enum StripeEventType implements EnumBase { // 收银完成 CHECKOUT_SESSION_COMPLETED("checkout.session.completed"), // 退款 CHARGE_REFUNDED("charge.refunded"), IGNORED(""); private final String message; StripeEventType(String message) { this.message = message; } public static StripeEventType convert(String message) { for (StripeEventType value : StripeEventType.values()) { if (StringUtils.equals(value.message(), message)) { return value; } } return IGNORED; } @Override public String message() { return this.message; } @Override public Number value() { return null; }
}
Webhook 处理器
public interface WebhookHandler { boolean supports(StripeEventType stripeEventType); void handle(Event event) throws EventDataObjectDeserializationException;
}public abstract class WebhookHandlerBase<T> implements WebhookHandler { @SuppressWarnings("unchecked") @Override public void handle(Event event) throws EventDataObjectDeserializationException { EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer(); StripeObject stripeObject = dataObjectDeserializer.deserializeUnsafe(); handle((T) stripeObject); } public abstract void handle(T stripeObject);
}@Component
@Slf4j
public class WebhookHandlerDefaultImpl implements WebhookHandler { @Override public boolean supports(StripeEventType stripeEventType) { return stripeEventType.equals(StripeEventType.IGNORED); } @Override public void handle(Event event) { log.info("ignored event: {} {}", event.getType(), event.toJson()); }
}@Component
@Slf4j
public class WebhookHandlerCheckoutSessionCompletedImpl extends WebhookHandlerBase<Session> {@Overridepublic boolean supports(StripeEventType stripeEventType) {return stripeEventType.equals(StripeEventType.CHECKOUT_SESSION_COMPLETED);}@Overridepublic void handle(Session session) {// 完成收银}
}@Component
@Slf4j
public class WebhookHandlerChargeRefundImpl extends WebhookHandlerBase<Charge> {@Overridepublic boolean supports(StripeEventType stripeEventType) {return stripeEventType.equals(StripeEventType.CHARGE_REFUNDED);}@Overridepublic void handle(Charge charge) {// 订单退款}
}
配置 Stripe Webhook
管理平台 – FeloTranslator – Stripe [Test]

4.4.3 完成收银
步骤流程:
- 判断对应的订单是否存在
- 订单所有者
- 对应的Stripe checkout session 状态是否正常
- 订单完成
- 发送订单完成事件
- 事件订阅者处理后续流程
protected Session checkCheckoutSession(String sessionId) {// 查询是否完成Session session = null;try {session = stripeClient.checkout().sessions().retrieve(sessionId);} catch (StripeException e) {log.error("failed to query checkout session. {}", sessionId, e);throw new RuntimeException("failed to query checkout session:" + sessionId);}// https://stripe.com/docs/api/checkout/sessions/object#checkout_session_object-payment_statusString status = session.getPaymentStatus();if (StringUtils.notEquals(status, "paid")) {throw new RuntimeException("Checkout has no completed: " + status);}return session;
}
Ref
Documentation | Stripe 文档
Stripe-hosted page | Stripe 文档
stripe/stripe-java: Java library for the Stripe API. (github.com)
Stripe API Reference
相关文章:
Stripe Web 购买集成
图片被吞了可以来这里看:https://juejin.cn/post/7346388511338381364 1. 准备事项 Stripe 账号域名以及配套的网站Stripe 账号付款信息公钥和私钥 2. 配置产品以及价格 可以通过 API 或者 Stripe 管理后台来进行配置 产品:就是商品,只需…...
加密货币在网络违法犯罪活动中的利用情况调查
一、调查背景 区块链基于分布式共识和经济激励等手段,在开放式、无许可的网络空间中,为价值的确立、存储、转移提供了新的解决方案。然而随着加密生态在过去若干年的快速发展,加密货币也越来越多地被用于各类风险活动,为网络赌博…...
【测试知识】业务面试问答突击版3---bug、测试用例设计
文章目录 一个完整的缺陷报告包含一个完整的测试用例包含一个完整的测试计划包含缺陷严重等级简述等价类划分法并举例简述边界值分析法逻辑覆盖针对具体场景的测试用例设计软件中存在多个分支时如何设计测试用例静态代码检查什么白盒测试是?常用方法是? …...
使用大型语言模型进行实体提取
原文地址:Using A Large Language Model For Entity Extraction LLM 能否比传统 NLP 方法更好地提取实体? 2022 年 7 月 12 日 Large Language Models for Generative Information Extraction: A Survey 实体简介 使用Co:here大型语言模型。 实体可以被视…...
基础:TCP是什么?
1. TCP 是什么? TCP(Transmission Control Protocol 传输控制协议) 是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 [1]定义。 TCP旨在适应支持多网络应用的分层协议层次结构。连接到不同但互连的计算机…...
el-table中 el-popover 性能优化
场景:在 el-table 中使用 el-popover ,出现了 loading 加载卡顿的问题,接口返回的数据的时间大概是 140ms ,所以不是接口慢的原因;通过对表中结构的逐步排查,发现是表中的 某一行 所影响的;并且 其中含有 e…...
java数据结构与算法刷题-----LeetCode46. 全排列
java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 暴力回溯2. 分区法回溯 1. 暴力回溯 解题思路:时…...
听说过Nginx反向代理,那正向代理是什么?
Nginx 是一款轻量级的 Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,它以其高性能、稳定性、丰富的功能集、简单的配置和低资源消耗而闻名。在 Nginx 中,正向代理和反向代理是两种常见的代理配置方式,它…...
实现elasticsearch和数据库的数据同步
1. 数据同步 elasticsearch中的酒店数据来自于mysql数据库,因此mysql数据发生改变时,elasticsearch也必须跟着改变,这个就是elasticsearch与mysql之间的数据同步。 1.1. 思路分析 常见的数据同步方案有三种: 同步调用 异步通知…...
SwiftUI的Alert使用方式
SwiftUI的Alert使用方式 记录一下SwiftUI的Alert使用方式,比较简单直接上代码 import SwiftUIstruct AlertBootCamp: View {State var showAlert falsevar body: some View {Button {showAlert.toggle()} label: {Text("alert show")}/// 单按钮 // …...
FPGA高端项目:FPGA基于GS2971的SDI视频接收+GTX 8b/10b编解码SFP光口传输,提供2套工程源码和技术支持
目录 1、前言免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案的SDI接收转HDMI输出应用本方案的SDI接收图像缩放应用本方案的SDI接收纯verilog图像缩放纯verilog多路视频拼接应用本方案的SDI接收HLS图像缩放Video Mixer多路视频拼接应用本方案的SDI接收OSD动态字符叠加…...
【源码编译】Apache SeaTunnel-Web 适配最新2.3.4版本教程
Apache SeaTunnel新版本已经发布,感兴趣的小伙伴可以看之前版本发布的文章 本文主要给大家介绍为使用2.3.4版本的新特性,需要对Apache SeaTunnel-Web依赖的版本进行升级,而SeaTunnel2.3.4版本部分API跟之前版本不兼容,所以需要对 …...
数据集下载
一、数据集下载——谷歌Open images 谷歌Open-image-v6是由谷歌出资标注的一个超大型数据集,数据大小达到600多G,类别达到600多种分类,对于普通研究者而言,根本没办法全部下载下来做测试,也没必要。只需要下载与自己任…...
3、设计模式之工厂模式2(Factory)
一、什么是工厂模式 工厂模式属于创建型设计模式,它用于解耦对象的创建和使用。通常情况下,我们创建对象时需要使用new操作符,但是使用new操作符创建对象会使代码具有耦合性。工厂模式通过提供一个公共的接口,使得我们可以在不暴露…...
npm、nodejs和vue之间关系和区别介绍
本文讲解npm、Node.js和Vue.js这三者之间的关系和区别,以及它们各自的特点。 首先,让我们来了解一下Node.js。 **Node.js** 是一个开源的服务器端运行环境,它允许开发者使用JavaScript来编写服务器端的代码。在传统的Web开发中&#…...
DM数据库安装(Windows)
先解压安装包 点击setup安装 下一步 勾选接受然后下一步 下一步 选择典型安装下一步 下一步 搜索DM数据库配置助手然后一直下一步 然后搜索DM管理工具 登录 登录成功 widows版本安装成功...
Python的asyncio 多线程
-- 多线程、进程、协程是什么就不讲了,(就是你理解的一边呼吸,一边看文章) 仅解决问题的话,下边两篇不用看, Python 中的 async await 概念-CSDN博客 再深一点的看这个 Python中的多线程、进程、协程、…...
【分类讨论】【解析几何】【 数学】【推荐】1330. 翻转子数组得到最大的数组值
作者推荐 视频算法专题 本文涉及知识点 分类讨论 解析几何 LeetCode1330. 翻转子数组得到最大的数组值 给你一个整数数组 nums 。「数组值」定义为所有满足 0 < i < nums.length-1 的 |nums[i]-nums[i1]| 的和。 你可以选择给定数组的任意子数组,并将该子…...
一文了解Spring的SPI机制
文章目录 一文了解Spring的SPI机制Java SPIServiceLoader Spring SPISpringboot利用Spring SPI开发starter 一文了解Spring的SPI机制 Java SPI SPI 全称 Service Provider Interface ,是 Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用…...
django根据时间(年月日)动态修改表名--方法一
方法一: 第一步:在models创建一个类,里边存放数据表中需要的字段,如下 class TemplateModel(models.Model):NowTime models.CharField(max_length5)name models.CharFiedld(max_length5)class Meta:abstract True # 基础类设…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...
Pandas 可视化集成:数据科学家的高效绘图指南
为什么选择 Pandas 进行数据可视化? 在数据科学和分析领域,可视化是理解数据、发现模式和传达见解的关键步骤。Python 生态系统提供了多种可视化工具,如 Matplotlib、Seaborn、Plotly 等,但 Pandas 内置的可视化功能因其与数据结…...
