StrimzStrimz
Recipes

Embedded checkout with Reown AppKit

Add Strimz embedded checkout to an app that already uses Reown AppKit. AppKit stays yours; the Strimz checkout runs its own wallet flow.

Your app already uses Reown AppKit for its own wallet features, and you want to take a payment without leaving the page. This recipe adds the Strimz embedded checkout next to AppKit.

The two don't share state. The Strimz checkout runs its own wallet connect (it uses Reown AppKit internally) inside the embedded frame, so the payer connects there and signs there. The Strimz React SDK does not read your wagmi config, and there is no hook that signs a payment with your app's connected wallet.

Install

pnpm add @strimz/sdk @strimz/sdk-react @reown/appkit @reown/appkit-adapter-wagmi wagmi viem @tanstack/react-query

AppKit, the wagmi adapter, wagmi, viem, and TanStack Query are for your app's own wallet features. Strimz needs only @strimz/sdk on the server and @strimz/sdk-react in the browser.

Wire the providers

AppKit's setup is unchanged. StrimzProvider can sit anywhere above the components that use it; it only needs your publishable key.

app/providers.tsx
'use client'
 
import { ReactNode, useState } from 'react'
import { WagmiProvider } from 'wagmi'
import { createAppKit } from '@reown/appkit/react'
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
import { arcTestnet } from '@reown/appkit/networks'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { StrimzProvider } from '@strimz/sdk-react'
 
const projectId = process.env.NEXT_PUBLIC_REOWN_PROJECT_ID!
const networks = [arcTestnet]
 
const wagmiAdapter = new WagmiAdapter({
  networks,
  projectId,
})
 
createAppKit({
  adapters: [wagmiAdapter],
  networks,
  projectId,
  defaultNetwork: arcTestnet,
  metadata: {
    name: 'Acme',
    description: 'Pay with USDC on Arc',
    url: 'https://acme.com',
    icons: ['https://acme.com/icon.png'],
  },
})
 
export function Providers({ children }: { children: ReactNode }) {
  const [queryClient] = useState(() => new QueryClient())
 
  return (
    <WagmiProvider config={wagmiAdapter.wagmiConfig}>
      <QueryClientProvider client={queryClient}>
        <StrimzProvider publishableKey={process.env.NEXT_PUBLIC_STRIMZ_PUBLISHABLE_KEY!}>
          {children}
        </StrimzProvider>
      </QueryClientProvider>
    </WagmiProvider>
  )
}

Mount it in your root layout:

app/layout.tsx
import { Providers } from './providers'
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  )
}

Server-side: create the session

Same as any Strimz integration. Your server creates the session, returns the id to the client.

app/api/checkout/route.ts
import { StrimzClient } from '@strimz/sdk'
 
const strimz = new StrimzClient({ apiKey: process.env.STRIMZ_API_KEY! })
 
export async function POST(req: Request) {
  const { description, amount } = await req.json()
 
  const session = await strimz.paymentSessions.create({
    amount,            // raw 6-decimal integer string
    currency: 'USDC',
    description,
  })
 
  return Response.json({ sessionId: session.id })
}

Client-side: the embedded checkout

app/checkout/page.tsx
'use client'
 
import { useState, useEffect } from 'react'
import { StrimzCheckoutEmbed, useStrimzSession } from '@strimz/sdk-react'
 
export default function CheckoutPage() {
  const [sessionId, setSessionId] = useState<string | null>(null)
  const [failed, setFailed] = useState(false)
 
  // Create the session when the page loads.
  useEffect(() => {
    fetch('/api/checkout', {
      method: 'POST',
      body: JSON.stringify({ description: 'Pro plan, monthly', amount: '99000000' }),
    })
      .then((r) => {
        if (!r.ok) throw new Error(`checkout request failed: ${r.status}`)
        return r.json()
      })
      .then((data) => setSessionId(data.sessionId))
      .catch(() => setFailed(true))
  }, [])
 
  if (failed) return <div>Could not start the checkout. Please try again.</div>
  if (!sessionId) return <div>Loading…</div>
 
  return (
    <div className="checkout">
      <h1>Pay 99 USDC</h1>
      <p>Pro plan, monthly. Pay once, use Acme for the month.</p>
      <PaymentPanel sessionId={sessionId} />
    </div>
  )
}
 
function PaymentPanel({ sessionId }: { sessionId: string }) {
  const { session, error } = useStrimzSession(sessionId)
 
  if (session?.status === 'confirmed') return <div>Paid. Thank you.</div>
  if (error) return <p>Could not load the payment: {error.message}</p>
 
  return <StrimzCheckoutEmbed sessionId={sessionId} height={640} />
}

What's happening

The user lands on /checkout. The page creates a session on mount (one fetch to your server, which holds the secret key). They see "Loading…" briefly.

The session id comes back and the page renders the Strimz checkout inline. The payer connects a wallet inside the checkout, through its own AppKit modal, and signs the payment there.

useStrimzSession polls the session with your publishable key. When the indexer confirms the on-chain transaction, the status moves to confirmed and the panel shows the success state.

Fulfil the order from your webhook handler, not from this page. The page is a UI signal; the signed webhook is the source of truth.

Customizing further

The page around the checkout is yours. useStrimzSession gives you session, loading, and error; render the amount, a spinner, or a richer success state however you like. To open the checkout in a popup from your own button instead of inline, use <StrimzPayButton sessionId={sessionId} />.

Your own Reown modal can be themed as usual. See their docs.

On this page