Guides
Next.js Integration
Integrate StableOps payment processing into your Next.js application.
This guide uses the App Router and the current TypeScript SDK.
Want to use StableOps hosted checkout to accept USDC in Next.js? Read the blog tutorial and check out the complete example project that deploys to Vercel with one click.
Install
pnpm add @stableops/api-sdkConfigure
STABLEOPS_API_KEY=sk_sandbox_...
STABLEOPS_WEBHOOK_SECRET=whsec_...Create lib/stableops.ts:
import { StableOps } from '@stableops/api-sdk'
export const stableops = new StableOps({
apiKey: process.env.STABLEOPS_API_KEY!,
})Create an order
Create app/actions/payment.ts:
'use server'
import { revalidatePath } from 'next/cache'
import { stableops } from '@/lib/stableops'
export async function createPaymentOrder(formData: FormData) {
const amount = String(formData.get('amount') ?? '')
const merchantOrderId = String(formData.get('orderId') ?? '')
const order = await stableops.paymentOrders.create(
{
merchantOrderId,
amount,
acceptedAssets: [{ chain: 'base', asset: 'USDC' }],
expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
metadata: { source: 'nextjs_checkout' },
},
{ idempotencyKey: merchantOrderId },
)
revalidatePath('/payments')
return order
}Show one payment candidate from the returned order:
{
order.paymentInstructions[0] ? (
<div>
Send {order.amount} {order.paymentInstructions[0].asset} on{' '}
{order.paymentInstructions[0].chain} to:
<code>{order.paymentInstructions[0].address}</code>
</div>
) : null
}Verify webhooks
Create app/api/webhooks/stableops/route.ts:
import { SIGNATURE_HEADER, verifySignature } from '@stableops/api-sdk/webhooks'
const WEBHOOK_SECRET = process.env.STABLEOPS_WEBHOOK_SECRET!
export async function POST(req: Request) {
const rawBody = await req.text()
const result = verifySignature({
secrets: [WEBHOOK_SECRET],
header: req.headers.get(SIGNATURE_HEADER) ?? undefined,
rawBody,
})
if (!result.ok) {
return new Response(`invalid signature: ${result.reason}`, { status: 400 })
}
const event = JSON.parse(rawBody) as {
type: string
data: { payment_order_id?: string }
}
switch (event.type) {
case 'payment.detected':
break
case 'payment.confirmed':
break
case 'payment.finalized':
// Fulfill after your own idempotency check.
break
case 'payment.expired':
case 'payment.reverted':
break
}
return new Response('ok')
}Store X-Event-Id in your database before mutating local order state so webhook
retries do not duplicate fulfillment.
How is this guide?
Last updated