TinyShip
TinyShip
 TinyShip
TinyShip
TinyShip Documentation
TinyShip User GuideGetting StartedBasic Configuration
Payment configurationStripeDodo Payments ConfigurationCreemPayPalWeChat PayAlipayDynamic PricingPayment testing
Credits System ConfigurationAffiliate System Configuration
Storage Service ConfigurationDatabase ConfigurationCaptcha Configuration
Development Best PracticesLocal E2E Workflow
User GuidePayment

Payment configuration

TinyShip payment system configuration guide

Payments are a core part of TinyShip. For a global product, start with Stripe, PayPal, Creem, or Dodo Payments. WeChat Pay and Alipay are optional and only for buyers in mainland China. Three billing modes are supported: one-time payment, subscription, and credit packs. WeChat Pay and Alipay support one-time payments and credit packs only.

Credits: To configure AI credit consumption, see the credits guide.

AI setup: If you have Agent Skills installed, you can ask your AI to "help me configure payment". The tinyship-payment skill walks through the setup.

Pricing mode: static vs dynamic

Choose a pricing mode before you add plans:

  • Static pricing (default): Plans live in config/payment.ts and ship with the code. Use this when plans rarely change. The "Configure plans" section below is the static path.
  • Dynamic pricing: Plans live in the database. Create and edit them at /admin/pricing without a redeploy. This also adds strikethrough prices, locale filtering, and custom ordering. See Dynamic pricing.

Switch modes with the PRICING_MODE environment variable. The two modes do not overwrite each other.

Related pages

PagePathDescription
Pricing/pricingLists every plan
Payment success/payment-successReturn page after a successful payment
Payment cancel/payment-cancelReturn page after a cancelled payment
Dashboard/dashboardSubscription status and order history

Supported providers

ProviderOne-timeSubscriptionCreditsMarketCurrency
StripeYesYesYesGlobalMany currencies
PayPalYesYesYesGlobalMany currencies
CreemYesYesYesGlobalUSD, EUR, and others
Dodo PaymentsYesYesYesGlobalMany currencies
WeChat PayYesNoYesMainland China (optional)CNY
AlipayYesNoYesMainland China (optional)CNY

Configuration overview

Set providers in config/payment.ts. Enable the ones you will use.

Global

  • Stripe — cards, one-time payments, and subscriptions
  • PayPal — one-time payments and subscriptions
  • Creem — indie-friendly checkout, with a simpler review than Stripe
  • Dodo Payments — Merchant of Record that handles tax and compliance

China (optional)

  • WeChat Pay and Alipay — for customers in China. Both require a Chinese business entity and a merchant account.

Local testing: Prefer each provider's test or sandbox mode. Stripe, Creem, PayPal, Alipay, and Dodo Payments all have one. WeChat Pay does not. To test WeChat Pay, place a real charge of 0.01 CNY. Plan amounts for WeChat Pay are yuan. The provider multiplies by 100 before it calls WeChat.

Provider guides

Stripe

Cards, subscriptions, and one-time payments.

PayPal

Global checkout. Subscriptions and one-time payments.

Creem

Indie-friendly checkout with a lighter review than Stripe.

Dodo Payments

Merchant of Record. Tax and compliance are handled for you.

China (optional)

WeChat Pay (China, optional)

Customers in China. QR checkout. A Chinese business entity is required.

Alipay (China, optional)

Customers in China. A Chinese business entity is required.

Dynamic pricing

Store plans in the database and edit them without a redeploy.

Payment testing

Local test mode and webhook debugging.

Configure plans

Plans live on plans in config/payment.ts. They show up on /pricing.

Dynamic pricing: To change plans without a redeploy, use dynamic pricing. Plans are then read from the database and edited at /admin/pricing. You do not need to edit config/payment.ts.

Plan types

TypeWhat it doesTypical use
One-timeOne payment for long-term or permanent accessLifetime membership, a software license, a course
Subscription (recurring)Bills on a scheduleSaaS, memberships
CreditsA pack of credits, spent as the product is usedAI chat, image generation

Plan shape

Each plan is a typed object. Provider-specific IDs sit on that object (stripePriceId, paypalPlanId, creemProductId, dodoProductId).

config/payment.ts
plans: {
  lifetime: {
    provider: 'stripe',           // 'stripe' | 'dodo' | 'creem' | 'paypal' | 'wechat' | 'alipay'
    id: 'lifetime',               // Unique plan id
    amount: 999,                  // Price. WeChat Pay and Alipay amounts are yuan.
    currency: 'USD',              // 'USD' | 'EUR' | 'CNY' | …
    recommended: true,            // Highlight on the pricing page
    duration: {
      months: 999999,             // 999999 means lifetime (months >= 9999)
      type: 'one_time'            // 'one_time' | 'recurring' | 'credits'
    },
    stripePriceId: 'price_xxx',   // Stripe Price ID
    paypalPlanId: 'P-xxx',        // PayPal Plan ID (subscriptions)
    creemProductId: 'prod_xxx',   // Creem product ID
    dodoProductId: 'pdt_xxx',     // Dodo product ID
    i18n: { /* locale copy */ }
  }
}

Set only the provider ID that matches provider. A Stripe plan needs stripePriceId. A PayPal subscription needs paypalPlanId. A Creem plan needs creemProductId. A Dodo plan needs dodoProductId. WeChat Pay and Alipay do not use a product ID on the plan.

Fields

FieldTypeRequiredDescription
providerstringYesWhich provider charges this plan
idstringYesUnique plan id, used by the API
amountnumberYesPrice. WeChat Pay and Alipay amounts are yuan. For Stripe, the charge comes from stripePriceId.
currencystringYesISO 4217 currency code
recommendedbooleanNoHighlight on the pricing page
duration.monthsnumberOne-time and recurringLength in months. 9999 or more is lifetime. Omit for credit packs.
duration.typestringYesone_time, recurring, or credits
creditsnumberCredit packsCredits granted after purchase
stripePriceIdstringStripeStripe Price ID
paypalPlanIdstringPayPal subscriptionsPayPal Plan ID
creemProductIdstringCreemCreem product ID
dodoProductIdstringDodo PaymentsDodo product ID

Locale copy

config/payment.ts
i18n: {
  'en': {
    name: 'Monthly Plan',
    description: 'Perfect for short-term projects',
    duration: 'month',
    features: ['All premium features', 'Priority support']
  }
}

Full example (USD, Stripe)

config/payment.ts
lifetime: {
  provider: 'stripe',
  id: 'lifetime',
  amount: 999,
  currency: 'USD',
  recommended: true,
  duration: {
    months: 999999,
    type: 'one_time'
  },
  stripePriceId: 'price_1RL2IcDjHLfDWeHDMCmobkzb',
  i18n: {
    'en': {
      name: 'TinyShip Complete',
      description: 'Lifetime access to the complete SaaS starter kit',
      duration: 'lifetime',
      features: [
        'Complete SaaS template source code',
        'Free lifetime updates',
        'Priority customer support',
        'Access to a private GitHub repository'
      ]
    }
  }
}

Strikethrough prices use originalPrice on dynamic plans (pricing_plan), not a field in config/payment.ts.

Local testing: WeChat Pay and Alipay charge amount directly. Use 0.01 yuan while testing those providers. Stripe bills the Price in the Dashboard (stripePriceId). See Payment testing.

Payment flow

  1. Buyer selects a plan → 2. Order is created → 3. Redirect to the provider → 4. Webhook is handled → 5. Order status is updated

The factory in libs/payment/index.ts is createPaymentProvider('stripe' | 'wechat' | 'creem' | 'dodo').

API routes

// Start checkout
POST /api/payment/initiate
{
  "planId": "monthly",
  "provider": "stripe"
}

// Order status (query string, not a path segment)
GET /api/payment/query?orderId=<orderId>&provider=<provider>

// Webhooks
POST /api/payment/webhook/stripe
POST /api/payment/webhook/paypal
POST /api/payment/webhook/creem
POST /api/payment/webhook/dodo
POST /api/payment/webhook/wechat
POST /api/payment/webhook/alipay

// Cancel (query string). Implemented for WeChat Pay.
POST /api/payment/cancel?orderId=<orderId>&provider=<provider>

References

  • Payment testing — local development and webhooks
  • Credits — credit packs and consumption

Provider docs

  • Stripe docs
  • PayPal developer docs
  • Creem API docs
  • Dodo Payments docs
  • WeChat Pay docs
  • Alipay Open Platform

SMS Verification Login

Configure phone SMS verification login

Stripe

Configure Stripe payments

On this page

Pricing mode: static vs dynamicRelated pagesSupported providersConfiguration overviewProvider guidesChina (optional)Configure plansPlan typesPlan shapeFieldsLocale copyFull example (USD, Stripe)Payment flowAPI routesReferencesProvider docs