@swft-checkout/nextjs
v1.0.0
Published
Next.js helpers for Swft Checkout
Readme
@swft-checkout/nextjs
Next.js helpers for Swft Checkout. Wraps @swft-checkout/js with App Router server actions, Pages Router helpers, a webhook route handler, and a client checkout button component.
Installation
npm install @swft-checkout/nextjs @swft-checkout/jsApp Router usage
Server action + redirect
Create a server action that builds the session and redirects immediately — no client-side JS needed.
// app/actions/checkout.ts
'use server'
import { swftCheckout } from '@swft-checkout/nextjs'
export async function startCheckout(cartData: { items: any[] }) {
await swftCheckout({
merchantApiKey: process.env.SWFT_API_KEY!,
currency: 'GBP',
lineItems: cartData.items.map(item => ({
name: item.name,
quantity: item.quantity,
price: item.price,
product_id: item.id,
image: item.image,
})),
shippingMethods: [
{ id: 'standard', label: 'Standard (3-5 days)', cost: 499 },
{ id: 'express', label: 'Express (next day)', cost: 999 },
],
subtotal: cartData.items.reduce((sum, i) => sum + i.price * i.quantity, 0),
})
// swftCheckout calls redirect() internally — this line is unreachable
}Trigger from a Server Component form:
// app/cart/page.tsx
import { startCheckout } from '../actions/checkout'
export default function CartPage() {
return (
<form action={startCheckout}>
<button type="submit">Proceed to checkout</button>
</form>
)
}API route + client button
If you need client interactivity (custom loading state, error handling), use an API route with SwftCheckoutButton.
// app/api/checkout/route.ts
import { NextResponse } from 'next/server'
import { createSession } from '@swft-checkout/nextjs'
import { getCart } from '@/lib/cart'
export async function POST() {
const cart = await getCart()
const session = await createSession({
merchantApiKey: process.env.SWFT_API_KEY!,
currency: 'GBP',
lineItems: cart.items.map(item => ({
name: item.name,
quantity: item.quantity,
price: item.price,
product_id: item.productId,
})),
shippingMethods: [
{ id: 'standard', label: 'Standard Delivery', cost: 499 },
],
subtotal: cart.subtotal,
})
return NextResponse.json({ sessionUrl: session.sessionUrl })
}// app/cart/CheckoutButton.tsx
'use client'
import { SwftCheckoutButton } from '@swft-checkout/nextjs/client'
export function CheckoutButton() {
return (
<SwftCheckoutButton
action="/api/checkout"
className="btn btn-primary w-full"
loadingText="Opening checkout..."
onError={(err) => alert(err.message)}
>
Checkout with Swft
</SwftCheckoutButton>
)
}Pages Router usage
Use createSession directly in getServerSideProps and redirect server-side:
// pages/cart.tsx
import type { GetServerSideProps } from 'next'
import { createSession } from '@swft-checkout/nextjs'
import { getCart } from '@/lib/cart'
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const cart = await getCart(ctx.req)
const session = await createSession({
merchantApiKey: process.env.SWFT_API_KEY!,
currency: 'GBP',
lineItems: cart.items.map(item => ({
name: item.name,
quantity: item.quantity,
price: item.price,
})),
shippingMethods: [
{ id: 'standard', label: 'Standard Delivery', cost: 499 },
],
subtotal: cart.subtotal,
})
return {
redirect: {
destination: session.sessionUrl,
permanent: false,
},
}
}
export default function CartPage() {
return null
}Or handle it from an API route (pages/api/checkout.ts) and redirect client-side with SwftCheckoutButton pointing at that route.
Webhook route handler
// app/api/webhooks/swft/route.ts
import { createSwftWebhookHandler } from '@swft-checkout/nextjs'
import { fulfillOrder } from '@/lib/orders'
import { sendConfirmationEmail } from '@/lib/email'
export const POST = createSwftWebhookHandler(
process.env.SWFT_WEBHOOK_SECRET!,
async (payload) => {
switch (payload.event) {
case 'payment_succeeded':
await fulfillOrder({
sessionId: payload.sessionId,
paymentIntentId: payload.paymentIntentId,
amount: payload.amount,
customer: payload.customer,
shippingAddress: payload.shippingAddress,
lineItems: payload.lineItems,
})
await sendConfirmationEmail(payload.customer?.email)
break
case 'payment_failed':
console.warn('Payment failed for session:', payload.sessionId)
break
case 'session_expired':
console.log('Session expired:', payload.sessionId)
break
}
// return nothing — the handler sends { received: true } automatically
}
)The handler verifies the x-swft-signature header automatically. Unauthenticated requests get a 401. Unhandled errors in your callback get a 500.
SwftCheckoutButton props
import { SwftCheckoutButton } from '@swft-checkout/nextjs/client'
<SwftCheckoutButton
action="/api/checkout" // required: API route returning { sessionUrl: string }
loadingText="Please wait…" // shown while fetching session (default: 'Loading checkout...')
onError={(err) => {}} // called if fetch or redirect fails
className="..." // all standard button props pass through
disabled={cartIsEmpty}
>
Buy now
</SwftCheckoutButton>The button POSTs to action, reads sessionUrl from the JSON response, then sets window.location.href. It disables itself during loading and re-enables on error.
Full TypeScript example
import {
createSession,
swftCheckout,
createSwftWebhookHandler,
} from '@swft-checkout/nextjs'
import type {
SwftSession,
SwftCreateSessionOptions,
SwftWebhookPayload,
} from '@swft-checkout/nextjs'
const options: SwftCreateSessionOptions = {
merchantApiKey: 'sk_live_...',
currency: 'GBP',
lineItems: [
{ name: 'Widget', quantity: 2, price: 1999 },
],
shippingMethods: [
{ id: 'free', label: 'Free delivery', cost: 0 },
],
subtotal: 3998,
}
// Server action (App Router)
await swftCheckout(options)
// Manual session creation
const session: SwftSession = await createSession(options)
// Webhook handler
export const POST = createSwftWebhookHandler(
process.env.SWFT_WEBHOOK_SECRET!,
async (payload: SwftWebhookPayload) => {
if (payload.event === 'payment_succeeded') {
// ...
}
}
)