Payment Integration (Stripe & PayPal)
Complete step-by-step guide to setting up Stripe and PayPal in TikShip — API keys, webhook configuration, product price IDs, subscription plans, local testing with the Stripe CLI, and a production checklist.
TikShip ships with a dual payment provider architecture: Stripe and PayPal can both be active simultaneously and are selected per-product. Everything flows through the unified service at src/lib/payment/unified-service.ts — no code changes are needed, only environment variables and dashboard configuration.
Architecture Overview
User clicks Pay
│
▼
/api/payment/create ← UnifiedPaymentService
│
┌────┴────┐
▼ ▼
Stripe PayPal
Checkout Orders/Subscriptions API
│ │
▼ ▼
Webhook Webhook
│ │
└────┬────┘
▼
Order status updated in DB
Email notification sentEach product in the admin panel has one or more Payment Methods attached to it. A payment method records:
provider:stripeorpaypalmode:payment(one-time) orsubscriptionstripePriceId: the Stripe Price ID (for Stripe methods)paypalPlanId: the PayPal Plan ID (for PayPal subscriptions)
Part 1 — Stripe
Create a Stripe Account
- Go to stripe.com and sign up.
- Complete the account activation steps (business info, banking details for live mode).
- Navigate to Developers → API keys in the Stripe Dashboard.
Copy API Keys
You will see two key pairs: Test and Live. Always start with Test keys.
# .env
STRIPE_SECRET_KEY="sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
STRIPE_PUBLISHABLE_KEY="pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"Never commit secret keys to git. sk_test_... and especially sk_live_... must stay in .env (which is in .gitignore).
Create a Product and Price in Stripe
TikShip maps each purchasable item to a Stripe Price ID (price_xxx). You create these in the Stripe Dashboard:
- Go to Product Catalog → Add product.
- Enter a name (e.g. "Pro Plan").
- Under Pricing, choose:
- One time — for a one-time payment
- Recurring — for a subscription (set billing period: monthly, yearly, etc.)
- Enter the price and currency.
- Click Save product.
- On the product detail page, copy the Price ID (format:
price_1ABC...).
You will paste this Price ID into the TikShip admin panel when configuring a product's payment method.
Configure Webhook
Stripe sends payment events to your server via webhook. TikShip's webhook handler lives at:
POST /api/stripe/webhookFor local development — use the Stripe CLI
- Install the Stripe CLI:
# macOS brew install stripe/stripe-cli/stripe # or download from https://github.com/stripe/stripe-cli/releases - Log in:
stripe login - Forward events to your local server:
stripe listen --forward-to localhost:3000/api/stripe/webhook - The CLI prints a webhook signing secret (format:
whsec_...). Copy it.
# .env (development only — use CLI secret)
STRIPE_WEBHOOK_SECRET="whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"For production — register in the Dashboard
- Go to Developers → Webhooks → Add endpoint.
- Endpoint URL:
https://yourdomain.com/api/stripe/webhook - Select these events to listen for:
| Event | Why |
|---|---|
checkout.session.completed | One-time payment success |
checkout.session.expired | Checkout window timed out |
checkout.session.async_payment_succeeded | Async payment methods (e.g. bank transfers) |
checkout.session.async_payment_failed | Async payment failure |
- After saving, reveal the Signing secret and add it to your production
.env:
STRIPE_WEBHOOK_SECRET="whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"TikShip verifies the webhook signature on every request using stripe.webhooks.constructEvent(). Requests with invalid signatures are rejected with HTTP 400.
Set Success and Cancel URLs
# .env
STRIPE_SUCCESS_URL="https://yourdomain.com/payment/success"
STRIPE_CANCEL_URL="https://yourdomain.com/payment/cancel"For local development:
STRIPE_SUCCESS_URL="http://localhost:3000/payment/success"
STRIPE_CANCEL_URL="http://localhost:3000/payment/cancel"Add a Product's Payment Method in the Admin Panel
- Go to Admin → Products → Edit (or create a new product).
- Scroll to the Payment Methods section.
- Click Add Payment Method.
- Select Provider: Stripe, Mode: payment (one-time) or subscription.
- Paste the Price ID from step 3.
- Toggle Active on.
- Save the product.
The customer will now see a Stripe checkout option on the product page.
Test with Stripe Test Cards
With the Stripe CLI running (stripe listen ...), start your dev server and attempt a purchase:
| Scenario | Card Number | Expiry | CVC |
|---|---|---|---|
| Successful payment | 4242 4242 4242 4242 | Any future date | Any 3 digits |
| Payment declined | 4000 0000 0000 0002 | Any future date | Any 3 digits |
| Insufficient funds | 4000 0000 0000 9995 | Any future date | Any 3 digits |
| 3D Secure required | 4000 0025 0000 3155 | Any future date | Any 3 digits |
| Async payment | 4000 0038 0000 0446 | Any future date | Any 3 digits |
Use ZIP 00000 and any billing name. After a successful test payment you should see:
- The order status change to
completedin Admin → Orders - A confirmation email in the server logs (or delivered to the user's inbox)
Part 2 — PayPal
Create a PayPal Developer Account
- Go to developer.paypal.com and log in with your PayPal account.
- Navigate to Apps & Credentials.
- Make sure you are in Sandbox mode (toggle at the top right).
Create a REST API App
- Click Create App under the REST API apps section.
- Give it a name (e.g.
TikShip). - Select Merchant as the app type.
- Click Create App.
- Copy the Client ID and Secret from the app detail page.
# .env
PAYPAL_MODE="sandbox" # sandbox | live
PAYPAL_CLIENT_ID="Axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
PAYPAL_CLIENT_SECRET="Exxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Create Sandbox Test Accounts
PayPal provides auto-generated sandbox accounts for testing:
- Go to Testing → Sandbox Accounts.
- You should see a Personal account (buyer) and a Business account (seller) already created.
- Click on the personal account → View/Edit Account to see the sandbox buyer's email and password.
Use these credentials when approving a sandbox payment in the PayPal popup.
Create a Subscription Plan (for subscriptions only)
For one-time payments, no plan is needed — TikShip passes the amount directly to the PayPal Orders API.
For subscriptions:
- In the PayPal Developer Dashboard, go to Subscriptions → Plans → Create Plan.
- First create a Product (not the same as a TikShip product — this is PayPal's internal product).
- Then create a Plan linked to that product, set the billing cycle (monthly, yearly), and the price.
- After saving, copy the Plan ID (format:
P-xxxxxxxxxxxxxxxxxxxxxxxx).
You will paste this Plan ID into the TikShip admin panel.
Configure Webhook
TikShip's PayPal webhook handler is at:
POST /api/paypal/webhookFor local development — use ngrok or similar
PayPal does not provide a CLI forwarder. Use ngrok to expose your local server:
# Install ngrok (macOS)
brew install ngrok
# Start ngrok tunnel
ngrok http 3000ngrok gives you a public URL like https://abc123.ngrok-free.app. Use this as the webhook URL.
Register the Webhook in PayPal
- In your PayPal app settings, scroll to Webhooks → Add Webhook.
- Webhook URL:
https://abc123.ngrok-free.app/api/paypal/webhook(or your production URL). - Select these events:
| Event | Why |
|---|---|
CHECKOUT.ORDER.APPROVED | Buyer approved the order — triggers capture |
PAYMENT.CAPTURE.COMPLETED | Payment captured successfully |
PAYMENT.CAPTURE.DENIED | Payment capture failed |
BILLING.SUBSCRIPTION.ACTIVATED | Subscription activated |
BILLING.SUBSCRIPTION.CANCELLED | Subscription cancelled |
BILLING.SUBSCRIPTION.EXPIRED | Subscription expired |
- After saving, copy the Webhook ID (format:
0SY50921UE9614629):
# .env
PAYPAL_WEBHOOK_ID="0SY50921UE9614629"TikShip verifies the webhook signature on every request by calling PayPal's /v1/notifications/verify-webhook-signature API. Requests that fail verification are rejected.
Add a Product's Payment Method in the Admin Panel
- Go to Admin → Products → Edit.
- Scroll to Payment Methods → Add Payment Method.
- Select Provider: PayPal.
- For Mode: payment (one-time) — no Plan ID needed.
- For Mode: subscription — paste the PayPal Plan ID from step 4.
- Toggle Active on and save.
Test a PayPal Sandbox Payment
- Ensure
PAYPAL_MODE="sandbox"in.env. - Start your dev server and ngrok.
- Click Pay with PayPal on a product page.
- Log in with your sandbox Personal (buyer) account credentials.
- Approve the payment.
- PayPal sends a webhook to your ngrok URL → forwarded to
localhost:3000/api/paypal/webhook. - Check Admin → Orders — the order should be
completed.
Switching to Live / Production
Stripe
- In the Stripe Dashboard, switch to Live mode.
- Copy your live API keys (
sk_live_.../pk_live_...). - Create a new webhook endpoint pointing to your production domain.
- Update
.envon the server:
STRIPE_SECRET_KEY="sk_live_xxxx"
STRIPE_PUBLISHABLE_KEY="pk_live_xxxx"
STRIPE_WEBHOOK_SECRET="whsec_xxxx"