stripe 支付对接
# 注册 stripe 账户,选择沙箱环境
网址:Stripe 官网
一:
1:保存 公钥和私钥。
2:设置 Webhook
3:添加接收端 勾选 事件 设置 回调地址
4:保存回调验签密钥
二:创建支付意图并支付
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 Elementsconst 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 处理成功");}
stripe 用到的JSON 是格式化过的,目前测试只有 gson 才能验签通过。