> ## 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.

# Create Checkout Session

> Create a Dodo Payments checkout session to start or upgrade a subscription

## Endpoint

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

## Authentication

Requires a valid API key with `billing:write` scope.

## Request Body

<ParamField body="priceId" type="string" required>
  Dodo Payments product ID for the subscription plan (e.g., `dodo_pay_...`)
</ParamField>

<ParamField body="plan" type="string" required>
  Plan name: `seed`, `sprout`, `harvest`, `grove`, `summit`
</ParamField>

## Response

Returns a Dodo Payments checkout session URL.

<ResponseField name="url" type="string">
  Dodo Payments checkout page URL to redirect user to
</ResponseField>

<RequestExample>
  ```bash cURL (Harvest Plan) theme={null}
  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"
    }'
  ```

  ```javascript JavaScript (Harvest Plan) theme={null}
  const response = await fetch(
    'https://www.wiseyield.co/api/billing/create-checkout-session',
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer wy_YOUR_API_KEY',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        priceId: 'price_1ABC2DEF3GHI4JKL',
        plan: 'harvest'
      })
    }
  );

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

  // Redirect user to Dodo Payments checkout
  window.location.href = url;
  ```

  ```javascript JavaScript (Seed Plan) theme={null}
  async function startCheckout(plan, priceId) {
    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 })
      });

      if (!response.ok) {
        const error = await response.json();
        toast.error(error.error);
        return;
      }

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

      // Redirect to Dodo Payments checkout
      window.location.href = url;
    } catch (error) {
      toast.error('Failed to start checkout');
    }
  }

  // Usage
  await startCheckout('seed', 'price_seed_monthly');
  ```

  ```python Python (Harvest Plan) theme={null}
  import requests

  response = requests.post(
      'https://www.wiseyield.co/api/billing/create-checkout-session',
      headers={'Authorization': 'Bearer wy_YOUR_API_KEY'},
      json={
          'priceId': 'price_1ABC2DEF3GHI4JKL',
          'plan': 'harvest'
      }
  )

  data = response.json()
  checkout_url = data['url']

  # Redirect user to this URL
  print(f"Checkout URL: {checkout_url}")
  ```
</RequestExample>

<ResponseExample>
  ```json 200 Success theme={null}
  {
    "url": "https://checkout.dodopayments.com/session/cs_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "error": "Price ID and plan are required"
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "error": "Unauthorized"
  }
  ```

  ```json 500 Internal Server Error theme={null}
  {
    "error": "Failed to create checkout session"
  }
  ```
</ResponseExample>

## Errors

<ResponseField name="400" type="error">
  Bad Request - Missing required fields (priceId or plan)
</ResponseField>

<ResponseField name="401" type="error">
  Unauthorized - Missing or invalid API key
</ResponseField>

<ResponseField name="500" type="error">
  Internal Server Error - Failed to create Dodo Payments checkout session
</ResponseField>

## 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

```javascript theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
// 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

<Tip>
  **Store Product IDs**: Keep Dodo Payments product IDs in environment variables for easy updates.
</Tip>

<Tip>
  **Show Confirmation**: Display pricing and features before redirecting to checkout.
</Tip>

<Tip>
  **Handle Success**: Check for `?success=true` query param on return URL to show confirmation.
</Tip>

<Tip>
  **Loading State**: Show loading indicator while creating checkout session.
</Tip>

<Warning>
  **Don't Hardcode Prices**: Use Dodo Payments product IDs from environment variables to support different pricing in development/production.
</Warning>

<Warning>
  **Test Mode**: Dodo Payments checkout runs in test mode if using test API keys. Use test cards for development.
</Warning>

<Warning>
  **Webhook Required**: Subscription won't activate without webhook endpoint configured. Ensure `/api/webhooks/dodo` is set up.
</Warning>

## Integration Examples

### Pricing Page Component

```javascript theme={null}
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

```javascript theme={null}
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>
  );
}
```

## Related Endpoints

* [Get Subscription](/api-reference/billing/get-subscription) - GET current subscription status
* [Create Portal Session](/api-reference/billing/create-portal-session) - POST to manage existing subscription
* [Get Invoices](/api-reference/billing/get-invoices) - GET payment history
* [Get Usage](/api-reference/billing/get-usage) - GET current usage metrics
