How to add plans, checkout, and paywalls to a job board
Present employer plans, posting products, candidate offers, checkout, and entitlement return states.
A
JCavuno is authoritative for products, prices, checkout requirements, and entitlements. Your frontend presents those decisions and owns the return route. Never hard-code a price or infer access from a successful redirect alone.
Present employer plans
1234const [plans, salesLed] = await Promise.all([board.plans.list(),board.plans.salesLed(),]);
Render the returned currency, billing interval, limits, and call to action. A sales-led plan should enter the returned contact path rather than a checkout intended for self-service products.
Build public job-posting checkout
Load board.jobPosting.plans(), collect the posting fields, and complete any required billing-email verification before calling board.jobPosting.create(). Use checkBilling, sendBillingVerification, and getBillingOptions for that pre-submission verification flow. The create result is a discriminated union: narrow on status before reading status-specific fields.
12345678910111213141516const result = await board.jobPosting.create(input);switch (result.status) {case 'checkout':return redirect(result.checkoutUrl);case 'published':return { step: 'published', jobSlug: result.jobSlug };case 'pending_approval':return { step: 'pending-approval', jobId: result.jobId };case 'invoice_sent':return { step: 'invoice-sent', jobId: result.jobId };default: {const unhandled: never = result;throw new Error(`Unhandled posting result: ${String(unhandled)}`);}}
Do not publish a success page solely because the payment provider redirected back. Retrieve the posting or entitlement state from Cavuno.
Build candidate paywall offers
12345const offers = await board.paywall.offers();const access = await board.me.access(undefined, {cache: 'no-store',headers: { authorization: `Bearer ${accessToken}` },});
Use board.me.access.checkout to start checkout, retrieveCheckout on return, and portal for subscription management. The access response decides whether gated candidate data may render.
Keep checkout routes private and replay-safe
Return routes can be revisited. Read current state every time and render the same final outcome for an already-completed checkout. Do not store an access grant only in browser state. Never cache checkout, portal, or entitlement responses publicly.
Verify monetization
- Render every returned currency and interval without client-side price conversion.
- Exercise free, self-service paid, sales-led, billing-verification, canceled, and completed states available on the development board.
- Reload the checkout return route and confirm it remains correct and does not create another purchase.
- Sign in without candidate access and confirm gated data is absent from HTML and API responses.
- Open the customer portal, change access, and confirm the next account load reflects the new server entitlement.
Finish with Build an SEO-complete board to connect every public route into one crawlable system.