A developer builds a SaaS application where users sign up through a custom Clerk authentication flow and then complete subscription payment via Stripe, with both UIs styled to match the product's brand design system for a seamless onboarding experience.
Use this workflow when building a SaaS product that requires a fully branded onboarding experience, where users authenticate via a custom Clerk flow and immediately transition to a styled Stripe subscription checkout without breaking your design system.
@clerk/nextjs. Create a dedicated /auth/sign-up route and mount <SignUp routing="path" path="/auth/sign-up" /> to bypass default hosted pages.appearance prop to Clerk components to enforce brand tokens: appearance={{ variables: { colorPrimary: '#0F172A', fontFamily: 'Inter, sans-serif' }, elements: { formButtonPrimary: 'bg-brand-600 hover:bg-brand-700' } }}.const customer = await stripe.customers.create({ email: user.emailAddresses[0].emailAddress, metadata: { clerk_user_id: user.id } }).const elements = stripe.elements({ clientSecret: intent.client_secret, appearance: { theme: 'stripe', variables: { colorPrimary: '#0F172A', borderRadius: '8px' } } }).<PaymentElement /> and handle submission: await stripe.confirmPayment({ elements, confirmParams: { return_url: ${process.env.NEXT_PUBLIC_URL}/dashboard } })./webhooks/stripe endpoint. Verify with stripe.webhooks.constructPayload(), then update Clerk: await clerkClient.users.updateUserMetadata(clerkId, { publicMetadata: { stripeSubscriptionId: event.data.object.id } }).Clerk owns identity, session management, and user metadata. Stripe owns payment collection, subscription lifecycle, and billing events. The backend bridges the two: it maps Clerk user.id to a Stripe customer.id, generates a client_secret for the payment form, and consumes Stripe webhooks to write subscription status back into Clerk’s publicMetadata. The frontend gates routes by reading Clerk session metadata.
CLERK_SECRET_KEY and NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYSTRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET@clerk/nextjs and @stripe/stripe-js installedcolorPrimary, fontFamily, borderRadius)stripe.webhooks.constructPayload() fails signature verification; always use the raw req.body buffer.appearance.variables only accepts documented CSS properties; injecting arbitrary keys silently breaks form rendering.stripeSubscriptionId causes dashboard access errors on first load.afterSignInUrl in Clerk or failing to whitelist return_url in Stripe triggers infinite auth/payment redirects.