Next.js + Stripe Integration

This guide demonstrates how to integrate DaisyChain with a Next.js application using Stripe for payments.

Prerequisites

  • Next.js 13+ project
  • Stripe account
  • DaisyChain account

Installation

npm install @daisychain/sdk @stripe/stripe-js stripe

Environment Setup

Create a .env.local file:

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
DAISYCHAIN_API_KEY=dc_test_...
DAISYCHAIN_STORE_ID=store_...

Implementation Steps

1. DaisyChain Client Setup

Create lib/daisychain.ts:

import { DaisyChain } from '@daisychain/sdk';

export const daisychain = new DaisyChain({
  apiKey: process.env.DAISYCHAIN_API_KEY!,
  storeId: process.env.DAISYCHAIN_STORE_ID!,
  debug: process.env.NODE_ENV === 'development'
});

// Helper functions
export async function captureReferral() {
  return daisychain.captureFromURL();
}

export function addReferralField(formElement: HTMLFormElement) {
  daisychain.injectReferralField(formElement, {
    label: 'Referral Code (Optional)',
    placeholder: 'Enter email or code'
  });
}

export async function handleSuccessfulPayment(session: any) {
  const storedReferral = daisychain.getStoredReferral();
  
  if (storedReferral) {
    await daisychain.submitReferral({
      referrerIdentifier: storedReferral.referrerIdentifier,
      customerId: session.customer,
      orderId: session.id,
      orderAmount: session.amount_total / 100
    });
  }
}

2. Checkout Page

Create app/checkout/page.tsx:

'use client';

import { useEffect } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { captureReferral, addReferralField } from '@/lib/daisychain';

const stripePromise = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
);

export default function CheckoutPage() {
  useEffect(() => {
    // Capture referral from URL if present
    captureReferral();

    // Add referral field to form
    const form = document.getElementById('checkout-form');
    if (form) {
      addReferralField(form as HTMLFormElement);
    }
  }, []);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    
    try {
      const response = await fetch('/api/create-checkout-session', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          items: [
            {
              name: 'Product Name',
              price: 2999,
              quantity: 1
            }
          ]
        }),
      });

      const { sessionId } = await response.json();
      
      const stripe = await stripePromise;
      await stripe!.redirectToCheckout({ sessionId });
    } catch (err) {
      console.error('Checkout error:', err);
    }
  }

  return (
    <div className="max-w-md mx-auto p-6">
      <form id="checkout-form" onSubmit={handleSubmit}>
        {/* DaisyChain will inject the referral field here */}
        <button type="submit">
          Checkout
        </button>
      </form>
    </div>
  );
}

3. Stripe Checkout Session

Create app/api/create-checkout-session/route.ts:

import { NextResponse } from 'next/server';
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2023-10-16'
});

export async function POST(req: Request) {
  try {
    const { items } = await req.json();

    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line_items: items.map((item: any) => ({
        price_data: {
          currency: 'usd',
          product_data: {
            name: item.name,
          },
          unit_amount: item.price,
        },
        quantity: item.quantity,
      })),
      mode: 'payment',
      success_url: `${req.headers.get('origin')}/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${req.headers.get('origin')}/checkout`,
    });

    return NextResponse.json({ sessionId: session.id });
  } catch (err: any) {
    return NextResponse.json(
      { error: err.message },
      { status: 500 }
    );
  }
}

4. Webhook Handler

Create app/api/webhook/route.ts:

import { NextResponse } from 'next/server';
import Stripe from 'stripe';
import { handleSuccessfulPayment } from '@/lib/daisychain';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2023-10-16'
});

const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;

export async function POST(req: Request) {
  try {
    const body = await req.text();
    const signature = req.headers.get('stripe-signature')!;

    const event = stripe.webhooks.constructEvent(
      body,
      signature,
      webhookSecret
    );

    if (event.type === 'checkout.session.completed') {
      const session = event.data.object;
      await handleSuccessfulPayment(session);
    }

    return new NextResponse(null, { status: 200 });
  } catch (err: any) {
    console.error('Webhook error:', err.message);
    return new NextResponse(
      `Webhook Error: ${err.message}`,
      { status: 400 }
    );
  }
}

Testing

  1. Start your development server:
npm run dev
  1. Use Stripe test cards:
  • Success: 4242 4242 4242 4242
  • Decline: 4000 0000 0000 0002
  1. Test referral flow:
  • Add ?ref=test@example.com to your checkout URL
  • Complete a purchase
  • Verify referral in DaisyChain dashboard

Going Live

  1. Update environment variables with production keys
  2. Update Stripe webhook endpoint in dashboard
  3. Test the complete flow in production mode
  4. Monitor webhook delivery in both Stripe and DaisyChain dashboards

Troubleshooting

Common Issues

  1. Webhook Errors

    • Verify webhook secrets
    • Check webhook logs in both dashboards
    • Ensure proper error handling
  2. Referral Not Tracking

    • Check URL parameters
    • Verify localStorage access
    • Check browser console for errors
  3. Payment Issues

    • Verify Stripe keys
    • Check payment logs in Stripe dashboard
    • Verify webhook signature