Stripe PaymentIntent in Svelte/SvelteKit: stripe-js directly, server route mints the secret
Svelte apps confirm PaymentIntents with stripe-js directly: no wrapper needed. This skill shows the SvelteKit pattern with a server route minting the client secret.
Stripe PaymentIntent in Svelte/SvelteKit
Svelte apps do not need a wrapper library. stripe-js works directly: a SvelteKit server route mints the PaymentIntent with the secret key, and the page component mounts a card element and confirms.
Server route mints the secret
// src/routes/api/payment-intent/+server.ts
import Stripe from 'stripe';
import { STRIPE_API_KEY_VALUE } from '$env/static/private';
import { json } from '@sveltejs/kit';
const stripe = new Stripe(STRIPE_API_KEY_VALUE);
export async function POST({ request }) {
const { amount, currency } = await request.json();
const intent = await stripe.paymentIntents.create({
amount,
currency,
automatic_payment_methods: { enabled: true },
});
return json({ 'clientSecret': intent.client_secret });
}Page confirms the card
[script]
import { onMount } from 'svelte';
import { loadStripe } from '@stripe/stripe-js';
let cardEl;
let stripe, card;
onMount(async () => {
stripe = await loadStripe('YOUR_PUBLISHABLE_KEY_VALUE');
card = stripe.elements().create('card');
card.mount(cardEl);
});
async function pay() {
const { clientSecret } = await fetch('/api/payment-intent', {
method: 'POST',
body: JSON.stringify({ amount: 2000, currency: 'usd' }),
}).then((r) => r.json());
const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
payment_method: { card },
});
if (error) console.log(error.message);
else if (paymentIntent.status === 'succeeded') console.log('paid');
}
[/script]
[div bind:this={cardEl}][/div]
[button on:click={pay}]Pay[/button]Notes
- The secret key lives in
$env/static/private, imported only by+server.tsfiles. bind:thisgives stripe-js the DOM node; mount afterloadStriperesolves.confirmCardPaymenthandles 3D Secure automatically.