What We’re Building
A Next.js application that lets users create an embedded wallet via social login, complete a KYC application, and receive a virtual Visa debit card funded by stablecoins (USDC). The complete flow covers:- Social login and automatic wallet creation with Dynamic
- Gasless transactions via ZeroDev account abstraction
- Multi-step KYC form submitted to the Rain Card API
- Virtual card issuance, balance display, and USDC deposits
- JWT-authenticated API routes using Dynamic’s JWKS endpoint
Prerequisites
Step 1: Install Dependencies
Step 2: Configure Environment Variables
.env.local
Step 3: Set Up Dynamic with Account Abstraction
Wrap your app withDynamicContextProvider, registering both standard Ethereum connectors and ZeroDev smart wallet connectors. The smart wallet connectors enable gasless USDC deposits from the user’s embedded wallet to their card account.
lib/providers.tsx
app/layout.tsx
Step 4: Authenticate API Routes with Dynamic JWTs
Dynamic issues a JWT for every authenticated user. Protect your server-side API routes by verifying that token against Dynamic’s JWKS endpoint. This gives you access to the user’s verified credentials (wallet address, email) inside every handler.lib/dynamic/dynamic-auth.ts
The JWKS URI must include your environment ID in production:
https://app.dynamicauth.com/api/v0/sdk/{environmentId}/.well-known/jwksStep 5: Build the KYC Application Flow
The KYC form collects personal, address, and financial information across three steps. On submit, a server action POSTs to your/api/apply route, which forwards the data to Rain and automatically creates a card.
Application API route
app/api/apply/route.ts
The Rain API auto-approves applications in test mode. In production, applications go through manual KYC review before a card can be created.
Calling the route from the client
Pass the Dynamic auth token as a Bearer token in every request to your API routes:Step 6: Display Card Details and Balance
Once a card is created, its reference is stored in the user’s Dynamic metadata. Retrieve the card and check the Rain balance from separate API routes, then render them in a card component.Balance API route
app/api/balance/route.ts
Card details (encrypted)
Card numbers, CVVs, and expiry dates come back from Rain encrypted. Retrieve them per session and render client-side with a show/hide toggle:app/api/card-details/route.ts
Step 7: Fund the Card with USDC
Users deposit USDC from their embedded wallet to the card’s on-chain account. Use viem’swriteContract to call the ERC-20 transfer function, with ZeroDev sponsoring gas so users don’t need ETH.
hooks/useDepositTokens.ts
components/FundCard.tsx