FieldCraftPayKitDocsServicesBlogWork With Me →

Stripe Guide

End-to-end guide for integrating Stripe with PayKit's unified payment abstraction — covering backend setup, frontend checkout, 3D Secure, and webhooks.

Installation

Install the core PayKit package along with the Stripe adapter:

Terminal
npm install @squaredr/paykit stripe

Backend Setup

Import PayKit and the Stripe adapter, then initialise the client with your Stripe secret key. You can optionally pass an apiVersion to pin a specific Stripe API version.

src/lib/paykit.ts
import { PayKit } from '@squaredr/paykit'
import { StripeAdapter } from '@squaredr/paykit/stripe'

const paykit = new PayKit({
adapter: new StripeAdapter({
secretKey: process.env.STRIPE_SECRET_KEY!,
apiVersion: '2024-06-20', // optional
}),
})

Creating a Payment Intent

Use paykit.charges.create to create a payment intent on the server. The returned clientSecret is passed to your frontend to confirm the payment.

src/app/api/checkout/route.ts
import { paykit } from '@/lib/paykit'

export async function POST(request: Request) {
const { amount, currency, orderId } = await request.json()

const charge = await paykit.charges.create({
amount,
currency,
metadata: { orderId },
_providerOptions: {
automatic_payment_methods: { enabled: true },
},
})

return Response.json({ clientSecret: charge.clientSecret })
}

Frontend (React)

Wrap your checkout page with PayKitProvider and use the CheckoutForm component to collect card details. The StripeClientAdapter bridges PayKit's UI layer with the Stripe Payment Element.

src/components/Checkout.tsx
'use client'

import {
PayKitProvider,
CheckoutForm,
} from '@squaredr/paykit-react'
import { StripeClientAdapter } from '@squaredr/paykit/stripe/client'

const stripeAdapter = new StripeClientAdapter({
publishableKey: process.env.NEXT_PUBLIC_STRIPE_PK!,
})

export default function Checkout({
clientSecret,
}: {
clientSecret: string
}) {
return (
<PayKitProvider
adapter={stripeAdapter}
clientSecret={clientSecret}
>
<CheckoutForm
onSuccess={(result) => {
console.log('Payment succeeded:', result.id)
}}
onError={(err) => {
console.error('Payment failed:', err.message)
}}
/>
</PayKitProvider>
)
}

Frontend (Vanilla JS)

If you are not using React, the imperative PayKitClient API gives you full control over the payment lifecycle.

src/checkout.ts
import { PayKitClient } from '@squaredr/paykit-js'
import { StripeClientAdapter } from '@squaredr/paykit/stripe/client'

const pk = new PayKitClient({
adapter: new StripeClientAdapter({
publishableKey: 'pk_test_...',
}),
})

async function checkout(clientSecret: string) {
await pk.loadProvider()

pk.mountCardInput('#card-element')

const result = await pk.confirmPayment({
clientSecret,
returnUrl: window.location.origin + '/order/confirm',
})

if (result.error) {
console.error(result.error.message)
} else {
console.log('Payment confirmed:', result.payment.id)
}
}

Handling 3D Secure

Stripe's Payment Element handles 3D Secure authentication automatically when required by the card issuer. No additional code is needed for inline challenges.

For redirect-based 3D Secure flows, pass a returnUrl so the customer is sent back to your site after completing authentication:

const result = await pk.confirmPayment({
clientSecret,
returnUrl: 'https://example.com/order/confirm',
})

On the return page, use pk.retrievePayment(clientSecret) to check the final payment status and display the appropriate confirmation or error message.

Webhooks

Use the Stripe adapter's webhooks.construct method to verify incoming webhook events and handle them safely:

src/app/api/webhooks/stripe/route.ts
import { paykit } from '@/lib/paykit'

export async function POST(request: Request) {
const payload = await request.text()
const signature = request.headers.get('stripe-signature')!
const secret = process.env.STRIPE_WEBHOOK_SECRET!

const event = paykit.webhooks.construct({
payload,
signature,
secret,
})

switch (event.type) {
case 'payment.succeeded':
// fulfil the order
break
case 'payment.failed':
// notify the customer
break
case 'refund.created':
// update records
break
}

return new Response('ok', { status: 200 })
}

Test Cards

Use these card numbers in Stripe's test mode to simulate different payment scenarios:

Card NumberScenario
4242 4242 4242 4242Success
4000 0025 0000 31553D Secure required
4000 0000 0000 9995Declined

Use any future expiry date and any 3-digit CVC. For the full list of test cards, see the Stripe testing documentation.