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
Dodo Payments product ID for the subscription plan (e.g., dodo_pay_...)
Plan name: seed, sprout, harvest, grove, summit
Response
Returns a Dodo Payments checkout session URL.
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
Bad Request - Missing required fields (priceId or plan)
Unauthorized - Missing or invalid API key
Internal Server Error - Failed to create Dodo Payments checkout session
How It Works
Checkout Flow:
- API Call: Client sends priceId and plan name
- Customer Creation: If first subscription, creates Dodo Payments customer
- Session Creation: Dodo Payments checkout session created with:
- Subscription mode
- Card payment method
- Success/cancel redirect URLs
- User metadata
- Redirect: User redirected to Dodo Payments checkout page
- Payment: User enters payment details on Dodo Payments
- Webhook: Dodo Payments sends webhook to create subscription in database
- 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>
);
}
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>
);
}