Stripe
Stripe uses Checkout Sessions: the user pays on Stripe's hosted page and the source of truth is the webhook, never the redirect back.
Configuration
import { StripeGateway } from '@kaizen/payments-gateway'
StripeGateway.register({
secret_key: process.env.STRIPE_SECRET_KEY!, // sk_test_… / sk_live_…
webhook_secret: process.env.STRIPE_WEBHOOK_SECRET!, // whsec_…
})| Field | Type | Description |
|---|---|---|
secret_key | string | Stripe API secret key |
webhook_secret | string | Webhook endpoint secret, to verify the signature |
Environment variables
You provide the values from your own environment and pass them to StripeGateway.register():
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxx| Variable | Field | Description |
|---|---|---|
STRIPE_SECRET_KEY | secret_key | API secret key (sk_test_… / sk_live_…) |
STRIPE_WEBHOOK_SECRET | webhook_secret | Webhook endpoint secret (whsec_…) to verify the signature |
Create a charge
curl -X POST http://localhost:3000/payments/charge \
-H 'content-type: application/json' \
-d '{
"gateway": "stripe",
"amount": 4900,
"currency": "eur",
"reference": "order-001",
"redirect": {
"success_url": "http://localhost:3000/ok",
"cancel_url": "http://localhost:3000/ko"
}
}'amountis in the currency's smallest unit (4900 = €49.00).redirect.success_url/cancel_urltravel per payment (in the body), not in the config.- The response has a
redirect_url(the Stripe Checkout): send the user there.
The response has the same shape as every other gateway:
{
"payment_id": "b9c1…",
"gateway": "stripe",
"status": "pending",
"redirect_url": "https://checkout.stripe.com/c/pay/…"
}End-to-end flow locally (Stripe CLI)
Start your consumer (
pnpm start).Open the Stripe CLI tunnel and copy the
whsec_…it prints intoSTRIPE_WEBHOOK_SECRET; restart so it's picked up:bashstripe listen --events checkout.session.completed \ --forward-to localhost:3000/webhooks/stripeTIP
--eventsfilters to the only thing we handle. Without it, the CLI forwards every event and the gateway responds400to the ones it doesn't support.Create a payment (the
curlabove) and open theredirect_url.Pay with the test card
4242 4242 4242 4242, a future date and any CVC.Stripe fires
checkout.session.completed→ the CLI forwards it → the gateway verifies the signature over therawBody, the core matches thePaymentbygateway_ref(=session.id), moves it tocapturedand emitspayment.captured.Replaying the same event does not re-process: idempotency by
(gateway, event_id)returns200.
Integration notes
client_reference_idcarries yourreferenceinside Stripe;gateway_refis thesession.id, which is what matches the webhook to thePayment.createPaymentsends anIdempotency-Key(yourreference), so retries don't create duplicate Checkout sessions.- The signature is always verified with
stripe.webhooks.constructEventover the raw body. Invalid signature →400.