Payment
How to set up and use Stripe for payments and subscriptions
TanStarter uses Stripe for payment processing, supporting both one-time payments and subscriptions.
Setup
TanStarter template provides three pricing plans by default: a free plan, a pro subscription plan (monthly/yearly), and a lifetime plan (one-time payment). Follow these steps to set up:
Create Stripe Account
Create a Stripe account at stripe.com.
Get API Keys
Get your API keys from the Stripe Dashboard:
- Go to Stripe Dashboard >
Developers>API keys - Copy the secret key (Note: test mode starts with
sk_test_, production mode starts withsk_live_) - Save it to your environment file as
STRIPE_SECRET_KEY
Set Up Webhook
Set up Webhook and get your Webhook secret:
- Go to Stripe Dashboard >
Developers>Webhooks - Click
Add endpoint - Enter Webhook URL:
https://YOUR-DOMAIN.com/api/webhooks/stripe - Select events to listen for:
invoice.paidcheckout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deleted
- Click
Revealto view the Webhook signing secret (starts withwhsec_) - Save it to your environment file as
STRIPE_WEBHOOK_SECRET
Create Products and Pricing Plans
Create products in Stripe and set up pricing plans:
- Go to Stripe Dashboard >
Product Catalog - Create the Pro subscription plan product:
- Click
Add product - Name:
Pro Plan - Description:
Premium features with subscription pricing - Add monthly price:
- Click
Add price - Price: $9.90 (currency: USD)
- Recurring: Monthly
- Save and copy the Price ID (starts with
price_), this will be used forVITE_STRIPE_PRICE_PRO_MONTHLY
- Click
- Add yearly price:
- Click
Add price - Price: $99.00 (currency: USD)
- Recurring: Yearly
- Save and copy the Price ID (starts with
price_), this will be used forVITE_STRIPE_PRICE_PRO_YEARLY
- Click
- Click
- Create the Lifetime plan product:
- Click
Add product - Name:
Lifetime Plan - Description:
One-time payment for lifetime access - Add price:
- Price: $199.00 (currency: USD)
- Type: One-time
- Save and copy the Price ID (starts with
price_), this will be used forVITE_STRIPE_PRICE_LIFETIME
- Click
Add Environment Variables
Add the following environment variables:
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Price plans
VITE_STRIPE_PRICE_PRO_MONTHLY=price_...
VITE_STRIPE_PRICE_PRO_YEARLY=price_...
VITE_STRIPE_PRICE_LIFETIME=price_...Set Up Customer Portal
Set up Stripe Customer Portal:
- Go to Stripe Dashboard >
Settings>Billing>Customer Portal - Customize the content displayed in the customer portal, for example, you can add a custom logo and title
- Click
Save changesto save the portal configuration
Update Website Configuration
Update the website.ts file to use Stripe as the payment provider, and configure the pricing plans:
import { clientEnv } from '@/env/client';
export const websiteConfig: WebsiteConfig = {
// ...other config
payment: {
enable: true,
provider: 'stripe',
price: {
plans: {
free: {
id: 'free',
prices: [],
isFree: true,
isLifetime: false,
},
pro: {
id: 'pro',
prices: [
{
type: 'subscription',
priceId: clientEnv.VITE_STRIPE_PRICE_PRO_MONTHLY!,
amount: 990,
currency: 'USD',
interval: 'month',
trialPeriodDays: 7,
},
{
type: 'subscription',
priceId: clientEnv.VITE_STRIPE_PRICE_PRO_YEARLY!,
amount: 9900,
currency: 'USD',
interval: 'year',
trialPeriodDays: 7,
},
],
isFree: false,
isLifetime: false,
popular: true,
},
lifetime: {
id: 'lifetime',
prices: [
{
type: 'one_time',
priceId: clientEnv.VITE_STRIPE_PRICE_LIFETIME!,
amount: 19900,
currency: 'USD',
allowPromotionCode: true,
},
],
isFree: false,
isLifetime: true,
},
},
},
},
// ...other config
}If you are setting up your environment, you can now go back to the Environment Configuration and continue. The rest of this document can be read later.
Environment Configuration
Set up environment variables
Core Features
- One-time payment for lifetime membership
- Recurring subscription payments (monthly/yearly)
- Free trial period support
- Subscription management with customer portal integration
- Webhook handling for payment events
- Subscription status tracking and verification
- Built-in pricing components (table, card, button)
- Server-side actions for secure payment operations
- Multiple pricing plan support (free, pro, lifetime)
Development
For local development, you can use the Stripe CLI to forward events to your local server:
pnpm install -g stripe/stripe-cliLog in to Stripe:
stripe loginForward events to your local server:
stripe listen --forward-to localhost:3000/api/webhooks/stripeThe webhook secret is printed in the terminal, copy it and add it to your environment file:
STRIPE_WEBHOOK_SECRET=whsec_...Finally, you can make payment operations on the website to test whether the event processing flow works as expected.
Production
- Go to Stripe Dashboard > Developers > Webhooks
- Click
Add endpoint - Enter Webhook URL:
https://YOUR-DOMAIN.com/api/webhooks/stripe - Select events to listen for:
invoice.paidcheckout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deleted
- After creation, click
Revealto view the webhook signing secret - Copy the webhook signing secret (starts with
whsec_) and add it to your environment variables
Custom Payment Provider
TanStarter supports extending with new payment providers:
- Create a new file in the
src/payment/providerdirectory - Implement the
PaymentProviderinterface fromtypes.ts - Register the new provider in the
providerRegistryinindex.ts
Example implementation for a new payment provider:
import type {
PaymentProvider,
CreateCheckoutParams,
CheckoutResult,
CreatePortalParams,
PortalResult,
} from '../types';
export class MyProvider implements PaymentProvider {
getProviderName(): string {
return 'my-provider';
}
public async createCheckout(params: CreateCheckoutParams): Promise<CheckoutResult> {
// Create checkout session implementation
}
public async createCustomerPortal(params: CreatePortalParams): Promise<PortalResult> {
// Create customer portal implementation
}
public async handleWebhookEvent(payload: string, signature: string): Promise<void> {
// Handle webhook events implementation
}
}Then register the new provider in the providerRegistry in index.ts:
import { MyProvider } from './provider/my-provider';
const providerRegistry: Record<PaymentProviderName, ProviderFactory> = {
stripe: () => new StripeProvider(),
'my-provider': () => new MyProvider(),
};Test Cards
To test Stripe integration, use Stripe's test mode and test credit cards:
- 4242 4242 4242 4242 - Successful payment
- 4000 0000 0000 3220 - Requires 3D Secure authentication
- 4000 0000 0000 9995 - Insufficient funds failure
You can find more about Stripe test cards in the Stripe documentation.
Creating Invoices
TanStarter has invoice creation configured for one-time payments.
// Automatically create invoice for one-time payments
checkoutParams.invoice_creation = {
enabled: true,
};If you want to automatically send paid invoices, you can enable it in Customer emails settings, under Email customers about, select Successful payments. After that, you can access invoices in Stripe Dashboard > Invoices.
You can find more about automatically sending paid invoices in the Stripe documentation.
Payment Flow Diagram
This is the complete payment flow diagram.

FAQ
How to activate WeChat Pay and Alipay?
You can activate WeChat Pay and Alipay in Stripe Dashboard > Settings > Payment methods. You can find more about WeChat Pay and Alipay in the Stripe documentation.
How to limit users to only one subscription?
You can enable Limit customers to 1 subscription in Stripe Dashboard > Settings > Checkout and Payment Links > Subscriptions. You can find more about limiting customers to one subscription in the Stripe documentation.
Best Practices
- Protect API keys: Never expose your Stripe secret key in client-side code
- Validate webhook signatures: Always verify the signature of webhook events
- Handle errors gracefully: Provide user-friendly error messages when payments fail
- Test webhooks thoroughly: Ensure all webhook events are handled correctly
References
Next Steps
Now that you understand how to use payments in TanStarter, you might want to explore these related features:
TanStarter Docs