注册 stripe 账户,选择沙箱环境
网址:Stripe 官网
一:
1:保存 公钥和私钥。

2:设置 Webhook

3:添加接收端 勾选 事件 设置 回调地址

4:保存回调验签密钥

二:创建支付意图并支付
Maven 进行引包
<!-- stripe 支付模块 -->
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>29.4.0</version>
</dependency>
import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.PaymentIntent;
import com.stripe.param.PaymentIntentCreateParams;
/**
* @author: damo
* @date: 2025-10-14 16:41
* @description:
*/
public class StripePayService {
/**
* 创建支付意图订单
*
* @param stripeApiKey 私钥
* @param customerName 付款姓名
* @param customerEmail 付款邮箱
* @param recordId 商品ID
* @param amount 金额
* @param currency 货币
* @return PaymentIntent
*/
public static PaymentIntent createPaymentIntent(String stripeApiKey, String customerName, String customerEmail, String recordId, Long amount, String currency) throws StripeException {
Stripe.apiKey = stripeApiKey;
PaymentIntentCreateParams params = PaymentIntentCreateParams.builder()
.setAmount(amount)
.setCurrency(currency)
.setAutomaticPaymentMethods(
PaymentIntentCreateParams.AutomaticPaymentMethods
.builder()
.setEnabled(true)
.build()
)
.putMetadata("customer_name", customerName)
.putMetadata("customer_email", customerEmail)
.putMetadata("product_id", recordId)
.build();
return PaymentIntent.create(params);
}
}
支付意图创建成功后,将 Id 和 clientSecret 返回给 前端用,注意id名字换为 paymentIntentId,前端调用 stripe 时用的参数名为 paymentIntentId。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stripe信用卡支付演示</title>
<script src="https://js.stripe.com/v3/"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
:root {
--primary: #6772e5;
--primary-dark: #5469d4;
--success: #28a745;
--danger: #dc3545;
}
body {
background: linear-gradient(135deg, #f5f7fa 0%, #e4e7f4 100%);
min-height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
}
.payment-container {
max-width: 800px;
margin: 2rem auto;
background: white;
border-radius: 16px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
overflow: hidden;
}
.header-section {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
color: white;
padding: 2rem;
text-align: center;
}
.product-card {
border-radius: 12px;
overflow: hidden;
transition: transform 0.3s ease;
margin: 20px;
}
.product-card:hover {
transform: translateY(-5px);
}
.product-image {
height: 200px;
object-fit: cover;
background-color: #f8f9fa;
}
.card-element-container {
background: white;
padding: 1.5rem;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
margin: 20px;
}
.payment-form input {
padding: 12px;
border-radius: 6px;
}
.btn-pay {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
border: none;
padding: 14px;
font-weight: 600;
font-size: 1.1rem;
border-radius: 8px;
transition: all 0.3s;
width: 100%;
color: white;
margin-top: 20px;
}
.btn-pay:hover {
transform: translateY(-3px);
box-shadow: 0 7px 14px rgba(103, 114, 229, 0.4);
}
.status-card {
display: none;
padding: 2rem;
text-align: center;
}
.success-status {
background-color: rgba(40, 167, 69, 0.1);
border-left: 4px solid var(--success);
}
.error-status {
background-color: rgba(220, 53, 69, 0.1);
border-left: 4px solid var(--danger);
}
.status-icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.spinner-border {
width: 1.5rem;
height: 1.5rem;
border-width: 0.2em;
display: none;
}
.test-card-info {
background-color: #e9ecef;
border-radius: 6px;
padding: 10px;
font-size: 0.85rem;
margin: 20px;
}
.card-element {
padding: 12px;
border: 1px solid #ced4da;
border-radius: 6px;
margin-bottom: 1rem;
}
#card-element {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 15px;
}
#card-errors {
color: #dc3545;
margin-bottom: 15px;
}
</style>
</head>
<body>
<div class="payment-container">
<div class="header-section">
<h1 class="display-5 fw-bold">信用卡支付集成</h1>
<p class="lead">使用Stripe处理支付,安全便捷</p>
</div>
<div class="row">
<!-- 产品信息 -->
<div class="col-md-6">
<div class="product-card">
<div class="card">
<div class="text-center p-4">
<svg xmlns="http://www.w3.org/2000/svg" width="140" height="140" fill="var(--primary)" viewBox="0 0 16 16">
<path d="M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5l2.404.961L10.404 1l-2.218-.887zm3.564 1.426L5.596 5 8 5.961 14.154 3.5l-2.404-.961zm3.25 1.7-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.82V7.838L1 5.239v7.923l6.5 2.658zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464L7.443.184z"/>
</svg>
</div>
<div class="card-body text-center">
<h4 class="card-title">高级订阅服务</h4>
<p class="card-text">解锁所有高级功能,尊享专属服务</p>
<div class="pricing mb-4">
<h2 class="fw-bold">$99.99<span class="fs-6 fw-normal">/年</span></h2>
</div>
<ul class="list-unstyled text-start">
<li class="mb-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="var(--success)" viewBox="0 0 16 16" class="me-2">
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
</svg>
无限访问所有功能
</li>
<li class="mb-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="var(--success)" viewBox="0 0 16 16" class="me-2">
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
</svg>
24/7高级技术支持
</li>
<li class="mb-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="var(--success)" viewBox="0 0 16 16" class="me-2">
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
</svg>
专属资源访问权限
</li>
<li>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="var(--success)" viewBox="0 0 16 16" class="me-2">
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
</svg>
免费产品更新
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- 支付表单 -->
<div class="col-md-6">
<div class="card-element-container">
<h3 class="mb-4">支付信息</h3>
<form id="payment-form">
<div class="mb-3">
<label class="form-label">姓名</label>
<input type="text" id="name" class="form-control" placeholder="持卡人姓名" value="测试用户">
</div>
<div class="mb-3">
<label class="form-label">邮箱</label>
<input type="email" id="email" class="form-control" placeholder="您的邮箱地址" value="test@example.com">
</div>
<div class="mb-3">
<label class="form-label">信用卡信息</label>
<div id="card-element" class="card-element"></div>
<div id="card-errors" role="alert" class="text-danger mt-2"></div>
</div>
<div class="mb-4">
<label class="form-label">账单地址</label>
<div class="row g-3">
<div class="col-md-6">
<input type="text" class="form-control" placeholder="国家" value="美国">
</div>
<div class="col-md-6">
<input type="text" class="form-control" placeholder="邮编" value="10001">
</div>
</div>
</div>
<button id="submit-button" class="btn btn-pay">
<span id="button-text">支付 $99.99</span>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" id="spinner"></span>
</button>
</form>
<!-- 支付状态 -->
<div id="payment-status" class="status-card mt-4">
<div id="success-message" class="d-none">
<div class="success-status p-4 rounded">
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" fill="var(--success)" viewBox="0 0 16 16" class="status-icon mb-3">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
</svg>
<h3>支付成功!</h3>
<p class="mb-0">感谢您的购买。交易ID: <span id="order-id" class="fw-bold"></span></p>
<button class="btn btn-outline-success mt-3" onclick="resetPaymentForm()">返回</button>
</div>
</div>
<div id="error-message" class="d-none">
<div class="error-status p-4 rounded">
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" fill="var(--danger)" viewBox="0 0 16 16" class="status-icon mb-3">
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
</svg>
<h3>支付失败</h3>
<p id="error-details" class="mb-0"></p>
<button class="btn btn-outline-danger mt-3" onclick="resetPaymentForm()">重试</button>
</div>
</div>
</div>
</div>
<div class="test-card-info">
<h5>测试卡信息</h5>
<div class="d-flex flex-wrap gap-2">
<div class="badge bg-primary">
信用卡: <strong>4242 4242 4242 4242</strong>
</div>
<div class="badge bg-info">
有效期: <strong>未来日期</strong>
</div>
<div class="badge bg-success">
CVC: <strong>任意三位数</strong>
</div>
</div>
<p class="mt-2 mb-0 small">注意:此演示使用Stripe测试环境,不会产生实际费用</p>
</div>
</div>
</div>
</div>
<script>
// 初始化Stripe - 替换为您的Stripe公钥
const stripe = Stripe('替换为您的Stripe公钥');
// 创建Stripe Elements
const elements = stripe.elements();
const cardElement = elements.create('card', {
style: {
base: {
fontSize: '16px',
color: '#32325d',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#dc3545'
}
}
});
// 挂载信用卡输入组件
cardElement.mount('#card-element');
// 表单元素
const form = document.getElementById('payment-form');
const submitButton = document.getElementById('submit-button');
const buttonText = document.getElementById('button-text');
const spinner = document.getElementById('spinner');
const cardErrors = document.getElementById('card-errors');
const successMessage = document.getElementById('success-message');
const errorMessage = document.getElementById('error-message');
const paymentStatus = document.getElementById('payment-status');
// 监听信用卡输入错误
cardElement.addEventListener('change', (event) => {
if (event.error) {
cardErrors.textContent = event.error.message;
} else {
cardErrors.textContent = '';
}
});
// 处理表单提交
form.addEventListener('submit', async (event) => {
event.preventDefault();
// 禁用按钮,显示加载状态
submitButton.disabled = true;
buttonText.textContent = '处理中...';
spinner.style.display = 'inline-block';
try {
// 1. 创建支付意图
const response = await fetch('你创建支付意图的后端接口', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
"operatorId": "1970017596304076800",
"orderId": "1977675882809556992",
"orderType": "exp",
"orderSubtype": "11",
"payProvider": "Stripe",
"payType": "31",
"total": 99,
"payParams": {
"customerName": "gondar",
"customerEmail": "1541998@qq.com"
}
})
});
console.log("************************************")
console.log(response)
console.log("************************************")
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || '创建支付意图失败');
}
// 2. 确认支付
const { paymentIntent, error } = await stripe.confirmCardPayment(
data.clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: document.getElementById('name').value,
email: document.getElementById('email').value
}
}
}
);
if (error) {
throw new Error(error.message);
}
if (paymentIntent.status === 'succeeded') {
// 3. 显示成功并开始验证
showSuccess(paymentIntent.id);
} else {
throw new Error(`支付状态: ${paymentIntent.status}`);
}
} catch (error) {
showError(error.message);
} finally {
spinner.style.display = 'none';
buttonText.textContent = '支付 $99.99';
}
});
// 显示成功消息
function showSuccess(orderId) {
form.style.display = 'none';
successMessage.classList.remove('d-none');
document.getElementById('order-id').textContent = orderId;
paymentStatus.style.display = 'block';
}
// 显示错误消息
function showError(message) {
submitButton.disabled = false;
form.style.display = 'block';
errorMessage.classList.remove('d-none');
document.getElementById('error-details').textContent = message;
paymentStatus.style.display = 'block';
}
// 重置表单
function resetPaymentForm() {
form.style.display = 'block';
successMessage.classList.add('d-none');
errorMessage.classList.add('d-none');
cardElement.clear();
document.getElementById('name').value = '';
document.getElementById('email').value = '';
paymentStatus.style.display = 'none';
submitButton.disabled = false;
}
</script>
</body>
</html>
有前端页面完成支付。
三:回调处理
public ResponseEntity<String> handleStripeWebhook(@RequestHeader("Stripe-Signature") String stripeSignature,
@RequestBody String payload) {
// 1. 获取Stripe商户密钥
String webhookSecret = "您的密钥";
// 2. 格式化参数
try {
com.google.gson.Gson gson = new com.google.gson.GsonBuilder().setPrettyPrinting().serializeNulls().create();
com.google.gson.JsonObject jsonObject = gson.fromJson(payload, com.google.gson.JsonObject.class);
payload = gson.toJson(jsonObject);
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("处理事件时出错");
}
// 3. 验证签名
Event event;
try {
event = Webhook.constructEvent(payload, stripeSignature, webhookSecret);
} catch (SignatureVerificationException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("签名验证失败");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("处理事件时出错");
}
// 4. 解析事件
PaymentIntent paymentIntent = null;
try {
if (event != null && event.getType() != null) {
if (event.getType().equals("payment_intent.succeeded") || event.getType().equals("payment_intent.payment_failed") || event.getType().equals("payment_intent.canceled")) {
EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer();
if (dataObjectDeserializer != null && dataObjectDeserializer.getObject().isPresent())
paymentIntent = (PaymentIntent) dataObjectDeserializer.getObject().get();
}else {
return ResponseEntity.ok(String.format("Webhook 暂未处理 %s 事件", event.getType()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (event == null || paymentIntent == null)
return ResponseEntity.ok("Webhook 解析到结果为空");
// 5. 处理事件
switch (event.getType()) {
// 支付成功
case "payment_intent.succeeded" -> {
}
// 支付失败
case "payment_intent.payment_failed" -> {
}
// 取消支付
case "payment_intent.canceled" -> {
}
// 其他事件
default -> {
return ResponseEntity.ok("未处理的事件类型: " + event.getType());
}
}
// 6. 处理支付结果
return ResponseEntity.ok("Webhook 处理成功");
}