Skip to main content
POST
/
api
/
billing
/
create-checkout-session
curl -X POST https://www.wiseyield.co/api/billing/create-checkout-session \
  -H "Authorization: Bearer wy_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "priceId": "price_1ABC2DEF3GHI4JKL",
    "plan": "harvest"
  }'
{
  "url": "https://checkout.dodopayments.com/session/cs_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}

Documentation Index

Fetch the complete documentation index at: https://docs.wiseyield.co/llms.txt

Use this file to discover all available pages before exploring further.

Endpoint

POST https://www.wiseyield.co/api/billing/create-checkout-session

Authentication

Requires a valid API key with billing:write scope.

Request Body

priceId
string
required
Dodo Payments product ID for the subscription plan (e.g., dodo_pay_...)
plan
string
required
Plan name: seed, sprout, harvest, grove, summit

Response

Returns a Dodo Payments checkout session URL.
url
string
Dodo Payments checkout page URL to redirect user to
curl -X POST https://www.wiseyield.co/api/billing/create-checkout-session \
  -H "Authorization: Bearer wy_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "priceId": "price_1ABC2DEF3GHI4JKL",
    "plan": "harvest"
  }'
{
  "url": "https://checkout.dodopayments.com/session/cs_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}

Errors

400
error
Bad Request - Missing required fields (priceId or plan)
401
error
Unauthorized - Missing or invalid API key
500
error
Internal Server Error - Failed to create Dodo Payments checkout session

How It Works

Checkout Flow:
  1. API Call: Client sends priceId and plan name
  2. Customer Creation: If first subscription, creates Dodo Payments customer
  3. Session Creation: Dodo Payments checkout session created with:
    • Subscription mode
    • Card payment method
    • Success/cancel redirect URLs
    • User metadata
  4. Redirect: User redirected to Dodo Payments checkout page
  5. Payment: User enters payment details on Dodo Payments
  6. Webhook: Dodo Payments sends webhook to create subscription in database
  7. Redirect Back: User redirected to success URL
Redirect URLs:
  • Success: https://www.wiseyield.co/dashboard/billing?success=true
  • Cancel: https://www.wiseyield.co/pricing?canceled=true
Dodo Customer:
  • First checkout creates Dodo Payments customer
  • Customer ID stored in users.dodoCustomerId
  • Subsequent checkouts reuse same customer

Subscription Plans & Pricing

Seed Plan

  • Price: €22/month
  • Features:
    • 1 farm
    • 50 AI requests/month
    • Email support
    • Basic analytics
  • Price ID: Contact support for production price IDs

Sprout Plan

  • Price: €49/month
  • Features:
    • 3 farms
    • 200 AI requests/month
    • Team collaboration (5 members)
    • Farm budgeting
  • Price ID: Contact support for production price IDs

Harvest Plan

  • Price: €89/month (recommended)
  • Features:
    • 7 farms
    • 500 AI requests/month
    • Vision AI Full (50 scans/mo)
    • Market Intelligence (20 queries/mo)
  • Price ID: Contact support for production price IDs

Grove Plan

  • Price: €149/month
  • Features:
    • 15 farms
    • 1,000 AI requests/month
    • Financial AI Insights
    • P&L reports, invoices
  • Price ID: Contact support for production price IDs

Summit Plan

  • Price: €299+/month (enterprise)
  • Features:
    • Unlimited farms
    • Unlimited AI requests
    • Dedicated support
    • API access
    • SLA guarantee
  • Price ID: Contact support for production price IDs

Use Cases

Start Subscription Checkout

async function subscribeToSeed() {
  const response = await fetch('/api/billing/create-checkout-session', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      priceId: process.env.NEXT_PUBLIC_DODO_SEED_PRICE_ID,
      plan: 'seed'
    })
  });

  if (!response.ok) {
    toast.error('Failed to start checkout');
    return;
  }

  const { url } = await response.json();
  window.location.href = url;
}

Upgrade Plan

async function upgradeToHarvest() {
  // Show confirmation
  const confirmed = confirm(
    'Upgrade to Harvest plan for €89/month?'
  );

  if (!confirmed) return;

  const response = await fetch('/api/billing/create-checkout-session', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      priceId: process.env.NEXT_PUBLIC_DODO_HARVEST_PRICE_ID,
      plan: 'harvest'
    })
  });

  const { url } = await response.json();
  window.location.href = url;
}

Choose Plan with Price Selector

async function checkoutWithPlan(planName) {
  const priceIds = {
    seed: process.env.NEXT_PUBLIC_DODO_SEED_PRICE_ID,
    sprout: process.env.NEXT_PUBLIC_DODO_SPROUT_PRICE_ID,
    harvest: process.env.NEXT_PUBLIC_DODO_HARVEST_PRICE_ID,
    grove: process.env.NEXT_PUBLIC_DODO_GROVE_PRICE_ID,
    summit: process.env.NEXT_PUBLIC_DODO_SUMMIT_PRICE_ID,
  };

  const priceId = priceIds[planName];

  if (!priceId) {
    toast.error('Invalid plan selected');
    return;
  }

  const response = await fetch('/api/billing/create-checkout-session', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ priceId, plan: planName })
  });

  const { url } = await response.json();
  window.location.href = url;
}

// Usage
await checkoutWithPlan('harvest');

Handle Checkout Success

// In /dashboard/billing page
function BillingPage() {
  const searchParams = useSearchParams();
  const success = searchParams.get('success');

  useEffect(() => {
    if (success === 'true') {
      toast.success('Subscription activated! Welcome to WiseYield.');

      // Refresh subscription data
      refreshSubscription();

      // Clear query param
      router.replace('/dashboard/billing');
    }
  }, [success]);

  return <div>Billing Dashboard</div>;
}

Best Practices

Store Product IDs: Keep Dodo Payments product IDs in environment variables for easy updates.
Show Confirmation: Display pricing and features before redirecting to checkout.
Handle Success: Check for ?success=true query param on return URL to show confirmation.
Loading State: Show loading indicator while creating checkout session.
Don’t Hardcode Prices: Use Dodo Payments product IDs from environment variables to support different pricing in development/production.
Test Mode: Dodo Payments checkout runs in test mode if using test API keys. Use test cards for development.
Webhook Required: Subscription won’t activate without webhook endpoint configured. Ensure /api/webhooks/dodo is set up.

Integration Examples

Pricing Page Component

function PricingPage() {
  const [loading, setLoading] = useState(false);

  const plans = [
    {
      name: 'Seed',
      price: '€22',
      priceId: process.env.NEXT_PUBLIC_DODO_SEED_PRICE_ID,
      features: ['1 farm', '50 AI requests/mo', 'Email support']
    },
    {
      name: 'Harvest',
      price: '€89',
      priceId: process.env.NEXT_PUBLIC_DODO_HARVEST_PRICE_ID,
      features: ['7 farms', '500 AI requests/mo', 'Vision AI Full', 'Market Intelligence']
    },
    {
      name: 'Grove',
      price: '€149',
      priceId: process.env.NEXT_PUBLIC_DODO_GROVE_PRICE_ID,
      features: ['15 farms', '1,000 AI requests/mo', 'Financial AI', 'P&L Reports']
    },
  ];

  async function handleSubscribe(plan) {
    setLoading(true);
    try {
      const response = await fetch('/api/billing/create-checkout-session', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          priceId: plan.priceId,
          plan: plan.name.toLowerCase()
        })
      });

      if (!response.ok) {
        toast.error('Failed to start checkout');
        return;
      }

      const { url } = await response.json();
      window.location.href = url;
    } finally {
      setLoading(false);
    }
  }

  return (
    <div className="pricing-grid">
      {plans.map(plan => (
        <div key={plan.name} className="pricing-card">
          <h3>{plan.name}</h3>
          <p className="price">{plan.price}/month</p>

          <ul>
            {plan.features.map(feature => (
              <li key={feature}>{feature}</li>
            ))}
          </ul>

          <button
            onClick={() => handleSubscribe(plan)}
            disabled={loading}
          >
            {loading ? 'Loading...' : 'Subscribe'}
          </button>
        </div>
      ))}
    </div>
  );
}

Upgrade Button Component

function UpgradeButton({ currentPlan, targetPlan }) {
  const [upgrading, setUpgrading] = useState(false);

  const priceIds = {
    seed: process.env.NEXT_PUBLIC_DODO_SEED_PRICE_ID,
    sprout: process.env.NEXT_PUBLIC_DODO_SPROUT_PRICE_ID,
    harvest: process.env.NEXT_PUBLIC_DODO_HARVEST_PRICE_ID,
    grove: process.env.NEXT_PUBLIC_DODO_GROVE_PRICE_ID,
    summit: process.env.NEXT_PUBLIC_DODO_SUMMIT_PRICE_ID,
  };

  async function handleUpgrade() {
    setUpgrading(true);
    try {
      const response = await fetch('/api/billing/create-checkout-session', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          priceId: priceIds[targetPlan],
          plan: targetPlan
        })
      });

      const { url } = await response.json();
      window.location.href = url;
    } finally {
      setUpgrading(false);
    }
  }

  if (currentPlan === targetPlan) {
    return <span>Current Plan</span>;
  }

  return (
    <button onClick={handleUpgrade} disabled={upgrading}>
      {upgrading ? 'Loading...' : `Upgrade to ${targetPlan}`}
    </button>
  );
}