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

PayPal

Configure PayPal payments

PayPal covers a global buyer base and supports one-time payments and subscriptions.

Setup

  1. Create a PayPal Developer account

    • Go to PayPal Developer
    • Create an app and copy the Client ID and Client Secret
  2. Use the sandbox while you develop

    • In the Developer Dashboard, switch to Sandbox
    • Use the sandbox Client ID and Client Secret
    • Set PAYPAL_SANDBOX=true
  3. Configure the webhook

    • Developer Dashboard → Webhooks
    • Webhook URL: https://yourdomain.com/api/payment/webhook/paypal
    • Copy the Webhook ID into PAYPAL_WEBHOOK_ID

Environment variables

Add these to .env:

# PayPal
PAYPAL_CLIENT_ID=AYxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
PAYPAL_CLIENT_SECRET=ELxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
PAYPAL_WEBHOOK_ID=WH-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
PAYPAL_SANDBOX=true  # Set to false in production

PAYPAL_WEBHOOK_ID is the Webhook ID from the app where you registered https://yourdomain.com/api/payment/webhook/paypal.

One-time payments and subscriptions

One-time payment (Orders + Capture)

  • You do not create a Product or Plan
  • Use this for lifetime access and other single purchases

Subscription (Subscriptions)

  • Create a Product and a Plan in the PayPal Dashboard
    • Developer Dashboard → Products & Services → Subscriptions
    • Create a Product, then a Plan
  • Copy the Plan ID into paypalPlanId on the plan in config/payment.ts
  • Subscription status comes from the webhook or from an active status query

Sandbox

PayPal has a sandbox for development.

Turn the sandbox on

  1. Set the environment variable

    PAYPAL_SANDBOX=true
  2. Sandbox API: https://api-m.sandbox.paypal.com

  3. Sandbox accounts

    • Create sandbox buyer and seller accounts in the Developer Dashboard
    • Complete test checkouts with the sandbox buyer account

Sandbox and production webhooks are separate. Register the webhook on the sandbox app. Sandbox webhook events can take several seconds to arrive.

Switch to production

  1. Set PAYPAL_SANDBOX=false
  2. Use the live Client ID and Client Secret
  3. Register the webhook again on the live app, at https://yourdomain.com/api/payment/webhook/paypal, and update PAYPAL_WEBHOOK_ID

Webhook events

Subscribe to these events so one-time payments and the subscription lifecycle are both covered:

EventMeaning
PAYMENT.CAPTURE.COMPLETEDCapture succeeded
PAYMENT.CAPTURE.DENIEDCapture was denied
PAYMENT.CAPTURE.REFUNDEDPayment was refunded
BILLING.SUBSCRIPTION.ACTIVATEDSubscription activated
BILLING.SUBSCRIPTION.UPDATEDSubscription updated
BILLING.SUBSCRIPTION.CANCELLEDSubscription cancelled
BILLING.SUBSCRIPTION.EXPIREDSubscription expired
BILLING.SUBSCRIPTION.SUSPENDEDSubscription suspended
PAYMENT.SALE.COMPLETEDSale completed

Plan examples

The config/payment.ts examples below apply only to static pricing (the default). With dynamic pricing, you do not need to edit config/payment.ts. Create plans in the admin panel at /admin/pricing and fill in paypalPlanId there.

Subscription

config/payment.ts
monthlyPaypal: {
  provider: 'paypal',
  id: 'monthlyPaypal',
  amount: 10,
  currency: 'USD',
  duration: {
    months: 1,
    type: 'recurring'
  },
  paypalPlanId: 'P-XXXXXXXXXX', // Plan ID from the PayPal Dashboard
  i18n: {
    'en': {
      name: 'PayPal Monthly Plan',
      description: 'Monthly recurring subscription via PayPal',
      duration: 'month',
      features: ['All premium features', 'Priority support']
    },
  }
}

One-time payment (lifetime)

config/payment.ts
lifetimePaypal: {
  provider: 'paypal',
  id: 'lifetimePaypal',
  amount: 199,
  currency: 'USD',
  duration: {
    months: 9999,
    type: 'one_time'
  },
  i18n: {
    'en': {
      name: 'Lifetime (PayPal)',
      description: 'One-time payment, lifetime access',
      duration: 'lifetime',
      features: ['All premium features', 'Priority support', 'Free lifetime updates']
    },
  }
}

Payment flow

One-time payment (Orders + Capture)

Buyer selects a plan → order is created → redirect to PayPal →
return to /api/payment/return/paypal → server captures the order →
order and access update → success page

Subscription

Buyer selects a subscription → subscription is created → redirect to PayPal →
return to /api/payment/return/paypal?subscription=true →
server checks that the subscription is ACTIVE → access updates → success page

The webhook at /api/payment/webhook/paypal is the source of truth for delayed events, cancellations, refunds, and renewals.

Notes

  • PayPal supports multiple currencies (USD, EUR, GBP, and others)
  • One-time payments and subscriptions are both supported
  • Subscriptions need a Plan ID from the PayPal Dashboard
  • Callback URLs must use HTTPS
  • Sandbox and production are fully separate
  • Sandbox webhook events can be delayed

Back to Payment overview

Creem

Configure Creem payments

WeChat Pay

Configure WeChat Pay (mainland China, optional)

On this page

SetupEnvironment variablesOne-time payments and subscriptionsOne-time payment (Orders + Capture)Subscription (Subscriptions)SandboxTurn the sandbox onSwitch to productionWebhook eventsPlan examplesSubscriptionOne-time payment (lifetime)Payment flowOne-time payment (Orders + Capture)SubscriptionNotes