← 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 receipt
payment.failedCharge failed — notify the customer, prompt retry
payment.refundedFull or partial refund processed
booking.confirmedBooking auto-confirmed after payment
booking.hold_expiredCustomer didn't pay in time — seat released

Create a paid booking

book-with-payment.ts
1import { Seshn } from '@seshn/sdk';
2
3const seshn = new Seshn('sk_live_...');
4
5// 1. Create a held booking with payment
6const 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.00
13 currency: 'usd',
14 },
15});
16
17// 2. Pass the clientSecret to your frontend
18// 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';
3
4const stripe = await loadStripe('pk_live_...');
5
6function CheckoutForm({ clientSecret }: { clientSecret: string }) {
7 const stripe = useStripe();
8
9 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 };
18
19 return (
20 <form onSubmit={handleSubmit}>
21 <PaymentElement />
22 <button type="submit">Pay</button>
23 </form>
24 );
25}
26
27// 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';
2
3const WEBHOOK_SECRET = process.env.SESHN_WEBHOOK_SECRET!;
4
5export async function POST(req: Request) {
6 const body = await req.text();
7 const signature = req.headers.get('x-webhook-signature');
8
9 const expected = 'sha256=' +
10 createHmac('sha256', WEBHOOK_SECRET).update(body).digest('hex');
11
12 if (signature !== expected) {
13 return new Response('Invalid signature', { status: 401 });
14 }
15
16 const event = req.headers.get('x-webhook-event');
17 const payload = JSON.parse(body);
18
19 switch (event) {
20 case 'payment.succeeded':
21 // Record the payment in your database
22 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;
30
31 case 'payment.failed':
32 // Notify customer to retry
33 await sendPaymentFailedEmail(payload.contact.email);
34 break;
35
36 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;
43
44 case 'booking.hold_expired':
45 // Customer didn't pay — log it, maybe follow up
46 await logExpiredHold(payload.id, payload.contact.email);
47 break;
48 }
49
50 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.