SignalCartel

Setup guide · 2 of 4

Wire PayPal Subscriptions

PayPal handles recurring revenue. Money flows to your merchant account; we never act as merchant of record. Typical setup: 45–90 minutes including sandbox smoke testing.

  1. 1.Register a PayPal REST app

    In PayPal Developer Dashboard, create a new REST app under your Business account. PayPal generates a Client ID and Secret for both Sandbox and LIVE. Copy the Sandbox pair first — you will switch to LIVE only after the end-to-end smoke test.

  2. 2.Set PAYPAL_* environment variables

    Add PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, and PAYPAL_ENV=sandbox to your .env.local and Vercel Production. PAYPAL_WEBHOOK_ID stays blank until step 4. Trigger a Vercel redeploy.

  3. 3.Create subscription plans in PayPal + mirror them locally

    In the PayPal Dashboard, create a Product, then create one Plan per pricing tier (Starter monthly, Starter annual, Pro monthly, Pro annual). Note each PayPal plan ID. Insert matching rows into the public.plans Supabase table — paypal_plan_id, key, name, price_usd, interval, entitled_strategies. Pro plans store entitled_strategies as array["*"]; Starters store one specific key.

  4. 4.Register the webhook

    In the PayPal app settings, add a webhook pointed at https://<your-domain>/api/paypal/webhook. Subscribe to BILLING.SUBSCRIPTION.ACTIVATED, BILLING.SUBSCRIPTION.CANCELLED, BILLING.SUBSCRIPTION.EXPIRED, and PAYMENT.SALE.COMPLETED. Save and copy the Webhook ID into your PAYPAL_WEBHOOK_ID env var. Redeploy.

  5. 5.Run a sandbox subscription end-to-end

    Create a Sandbox buyer account in the PayPal Developer Dashboard. Open your /pricing page, pick Starter, run through PayPal Sandbox checkout. Confirm in your Supabase subscriptions table that a row appears with status=active. Check /account/subscriptions renders the subscription. Verify the Telegram invite link arrives.

  6. 6.Cancel the sandbox subscription + verify the kick

    Cancel the test subscription through your /account/subscriptions page (which calls /api/billing/portal). Confirm subscriptions.status flips to cancelled, the user_entitlements row updates, and on next billing cycle the chat_member webhook kicks the test account from your paid Telegram channel.

  7. 7.Switch to LIVE

    Only when steps 5 and 6 are green: change PAYPAL_ENV=live, swap PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET to the LIVE pair, register a separate LIVE webhook (different ID), update PAYPAL_WEBHOOK_ID. Redeploy. Run one small real subscription against yourself to confirm money actually moves, then refund.

Common gotchas

  • Business account required.PayPal Personal accounts can't accept subscriptions. Upgrade before you start.
  • Sandbox and LIVE webhooks are separate.They have different webhook IDs. Don't reuse one for the other or signature verification fails silently.
  • Plan ID mismatch. If checkout 400s with PLAN_DOES_NOT_EXIST, your local plans.paypal_plan_id doesn't match what PayPal returns. Re-copy from the PayPal Dashboard.
  • Emerging markets. PayPal supports more African/Asian markets than Stripe at the moment — relevant if your subscribers are in regions where Stripe pulls the plug. The codebase has Stripe scaffolding in src/lib/stripe/ if you ever need to switch back.