← Integrations
Payments
Seshn + Stripe
Seshn has Stripe built in. This guide covers the full payment lifecycle — from hold to charge to refund — and how to use Seshn's payment webhooks for reconciliation.
How it works
Seshn manages the Stripe integration for you. When you create a booking with payment, Seshn creates a Stripe PaymentIntent, holds the seat, and auto-confirms when the payment succeeds. You never need to call the Stripe API directly for booking payments.
1Create a booking with hold: true and a payment amount
2Seshn returns a Stripe clientSecret for frontend payment collection
3Customer pays via Stripe.js on your frontend
4Stripe notifies Seshn via webhook — booking auto-confirms
Payment webhook events
Seshn fires its own payment events so you can track payment state without touching the Stripe API:
payment.succeededCharge completed — update your records, send a receiptpayment.failedCharge failed — notify the customer, prompt retrypayment.refundedFull or partial refund processedbooking.confirmedBooking auto-confirmed after paymentbooking.hold_expiredCustomer didn't pay in time — seat releasedCreate a paid booking
book-with-payment.ts
1import { Seshn } from '@seshn/sdk';23const seshn = new Seshn('sk_live_...');45// 1. Create a held booking with payment6const booking = await seshn.bookings.create({7 slotId: 'slot_abc123',8 contactId: 'ct_jane_doe',9 seats: 2,10 hold: true,11 payment: {12 amount: 4000, // $40.0013 currency: 'usd',14 },15});1617// 2. Pass the clientSecret to your frontend18// booking.payment.clientSecret → 'pi_xxx_secret_yyy'Collect payment on the frontend
checkout.tsx
1import { loadStripe } from '@stripe/stripe-js';2import { Elements, PaymentElement, useStripe } from '@stripe/react-stripe-js';34const stripe = await loadStripe('pk_live_...');56function CheckoutForm({ clientSecret }: { clientSecret: string }) {7 const stripe = useStripe();89 const handleSubmit = async () => {10 const { error } = await stripe!.confirmPayment({11 clientSecret,12 confirmParams: {13 return_url: 'https://yourdomain.com/booking/success',14 },15 });16 if (error) console.error(error.message);17 };1819 return (20 <form onSubmit={handleSubmit}>21 <PaymentElement />22 <button type="submit">Pay</button>23 </form>24 );25}2627// Wrap in <Elements stripe={stripe} options={{ clientSecret }}>28// <CheckoutForm clientSecret={clientSecret} />29// </Elements>Payment reconciliation handler
Use Seshn's payment webhooks to keep your own records in sync — no need to listen to Stripe webhooks directly.
app/api/webhooks/seshn/route.ts
1import { createHmac } from 'crypto';23const WEBHOOK_SECRET = process.env.SESHN_WEBHOOK_SECRET!;45export async function POST(req: Request) {6 const body = await req.text();7 const signature = req.headers.get('x-webhook-signature');89 const expected = 'sha256=' +10 createHmac('sha256', WEBHOOK_SECRET).update(body).digest('hex');1112 if (signature !== expected) {13 return new Response('Invalid signature', { status: 401 });14 }1516 const event = req.headers.get('x-webhook-event');17 const payload = JSON.parse(body);1819 switch (event) {20 case 'payment.succeeded':21 // Record the payment in your database22 await db.payments.create({23 bookingId: payload.bookingId,24 amount: payload.amount,25 currency: payload.currency,26 stripePaymentIntentId: payload.stripePaymentIntentId,27 status: 'paid',28 });29 break;3031 case 'payment.failed':32 // Notify customer to retry33 await sendPaymentFailedEmail(payload.contact.email);34 break;3536 case 'payment.refunded':37 await db.payments.update({38 bookingId: payload.bookingId,39 status: payload.refundAmount === payload.amount ? 'refunded' : 'partial_refund',40 refundAmount: payload.refundAmount,41 });42 break;4344 case 'booking.hold_expired':45 // Customer didn't pay — log it, maybe follow up46 await logExpiredHold(payload.id, payload.contact.email);47 break;48 }4950 return new Response('OK', { status: 200 });51}Next steps
- Issue refunds via
POST /v1/payments/:id/refund— Seshn handles the Stripe refund for you. - Combine with Resend to send receipts on
payment.succeeded. - Use entitlements for prepaid packs — customers buy N sessions via Stripe, then book without per-session payments.