Skip to main content

Stripe Integration

Connect your Stripe account to enable payment processing, subscription management, and automatic customer synchronization with Tether.

Prerequisites

  • A Stripe account (create one at stripe.com)
  • Access to your Stripe Dashboard
  • Basic understanding of webhooks

Step 1: Get Your API Keys

Navigate to the API Keys section in your Stripe Dashboard to get your keys.

⚠️ Important: Start with test mode keys to safely test your integration before going live.

You'll need two keys:

  • Publishable Key (pk_test_...): Used in your frontend code
  • Secret Key (sk_test_...): Used in your backend code

Step 2: Configure Environment Variables

Add your Stripe keys to your environment configuration:

1# Stripe API Keys
2NEXT_PUBLIC_STRIPE_TETHER_PUBLISHABLE_KEY=pk_test_your_publishable_key
3STRIPE_TETHER_SECRET_KEY=sk_test_your_secret_key
4
5# Stripe Webhook Secret (we'll get this in Step 4)
6STRIPE_TETHER_WEBHOOK_SECRET=whsec_your_webhook_secret

Step 3: Install Stripe SDK

The Stripe SDK is already included in Tether, but if you need to verify:

1npm install stripe @stripe/stripe-js

Step 4: Set Up Webhooks

Webhooks allow Stripe to notify Tether when events occur (like a successful payment or new customer).

Create a Webhook Endpoint

Go to the Webhooks section in your Stripe Dashboard and click "Add endpoint".

Enter your webhook URL:

1https://your-domain.com/api/webhooks/stripe

Select Events to Listen To

Important: Subscribe to exactly these 6 events. Tether only handles these; other events are ignored.

  • checkout.session.completed - When checkout completes (syncs subscription to products)
  • customer.subscription.updated - Subscription changes (plan, status, trial)
  • customer.subscription.deleted - Subscription cancelled
  • invoice.payment_succeeded - When invoice is paid (sets active, trial conversion)
  • invoice.payment_failed - Payment failed (sets past_due)
  • customer.subscription.trial_will_end - Trial ending soon

Note: Use invoice.payment_succeeded (not invoice.paid). Products must have stripe_customer_id set for invoice events to update correctly.

Get Your Webhook Secret

After creating the webhook, copy the "Signing secret" (starts with whsec_) and add it to your environment variables.

Step 5: Test the Integration

Test your Stripe connection with a simple API call:

1import Stripe from 'stripe';
2
3const stripe = new Stripe(process.env.STRIPE_TETHER_SECRET_KEY!, {
4  apiVersion: '2024-11-20.acacia',
5});
6
7// Test the connection
8async function testStripeConnection() {
9  try {
10    const customers = await stripe.customers.list({ limit: 1 });
11    console.log('✓ Stripe connection successful');
12    return true;
13  } catch (error) {
14    console.error('✗ Stripe connection failed:', error);
15    return false;
16  }
17}

Step 6: Create Your First Customer

Create a test customer to verify everything works:

1import { stripe } from '@/lib/stripe';
2
3async function createCustomer(email: string, name: string) {
4  const customer = await stripe.customers.create({
5    email,
6    name,
7    metadata: {
8      source: 'tether_platform',
9    },
10  });
11
12  return customer;
13}
14
15// Usage
16const customer = await createCustomer(
17  'test@example.com',
18  'Test Customer'
19);
20
21console.log('Customer ID:', customer.id);

Step 7: Process a Test Payment

Use Stripe's test card numbers to process a test payment:

1import { stripe } from '@/lib/stripe';
2
3async function createPaymentIntent(amount: number, currency: string = 'usd') {
4  const paymentIntent = await stripe.paymentIntents.create({
5    amount, // Amount in cents
6    currency,
7    automatic_payment_methods: {
8      enabled: true,
9    },
10  });
11
12  return paymentIntent;
13}
14
15// Create a $10 payment intent
16const paymentIntent = await createPaymentIntent(1000);

Test card numbers provided by Stripe:

Card NumberDescription
4242 4242 4242 4242Successful payment
4000 0000 0000 0002Card declined
4000 0000 0000 9995Insufficient funds

Step 8: Monitor Webhooks

Use the Stripe Dashboard to monitor webhook deliveries and debug any issues.

💡 Development Tip

Use the Stripe CLI to test webhooks locally during development:

stripe listen --forward-to localhost:3000/api/webhooks/stripe

Going Live

When you're ready to go live:

  1. Complete your Stripe account activation
  2. Replace test API keys with live keys
  3. Create a new webhook endpoint for production
  4. Update your environment variables
  5. Test with real cards (small amounts)
  6. Monitor the first few transactions closely

Troubleshooting

Webhook Signature Verification Failed

Make sure your STRIPE_TETHER_WEBHOOK_SECRET matches the signing secret from your Stripe webhook endpoint.

API Key Invalid

Verify you're using the correct keys for your environment (test vs live). Test keys start with pk_test_ or sk_test_.

Payment Intent Creation Failed

Check that the amount is in the smallest currency unit (cents for USD). For example, $10 = 1000 cents.

Subscription / Product Not Updating When Payment Succeeds

If the products table (subscription_status, trial_ends_at) isn't updating when customers pay:

  1. Webhook endpoint configured? In Stripe Dashboard → Webhooks, ensure you have an endpoint for https://your-domain.com/api/webhooks/stripe with the 6 events listed above.
  2. Webhook secret set? STRIPE_TETHER_WEBHOOK_SECRET must be set in your deployment environment. Without it, the route returns 500.
  3. Test vs live mode? The webhook secret is different for test and live mode. Use the secret from the endpoint that matches your Stripe API keys.
  4. Product has stripe_customer_id? Invoice events (payment_succeeded, payment_failed) look up the product by stripe_customer_id. If the product was created before Stripe setup or the link failed, it may be missing. Use Admin → Products → connect Stripe, or call POST /api/admin/products/connect-stripe with the product ID.

Check Stripe Dashboard → Webhooks → your endpoint → Recent deliveries to see if events are being sent and whether they succeed or fail.

Next Steps