Sell your first offer
Build one checkout for a course and put a working Join the course button on your own site. The same steps work for a download, workshop, or physical product; replace the example name and slug with yours.
1. Prepare the offer
- In Plandalf, create or import the product and its price. Connect the payment integration this offer will use.
- Create an offer for that product. Choose a checkout template, set the customer fields and confirmation behavior, and give the offer a slug. This guide uses
ceramics-course. - Preview the offer. Check the product, currency, total, discounts, and what the buyer sees after checkout. Publish it when that flow is right. Offer setup covers each editor step.
Keep the offer slug, not the product ID or the offer title. Also note your organization's host. The examples below use https://your-org.plandalf.dev; replace it with your real host.
2. Put the button on your page
Choose the integration that matches the page you already have. All three open the same published offer.
Add the script once and put the attribute on the button. The browser SDK binds the click for you.
<script defer
src="https://your-org.plandalf.dev/js/plandalf-sdk.js"></script>
<button data-plandalf-present="ceramics-course">
Join the course
</button>Install @plandalf/sdk in your JavaScript app. The returned handle is awaitable; a dismissal is a result, while an opening failure rejects.
import { Plandalf } from '@plandalf/sdk'
const sdk = new Plandalf({
apiBase: 'https://your-org.plandalf.dev',
})
const button = document.querySelector('#join-course')
const message = document.querySelector('#course-message')
button.addEventListener('click', async () => {
button.disabled = true
try {
const result = await sdk.present('ceramics-course')
message.textContent = result.status === 'complete'
? 'Checkout finished. We are confirming your place.'
: 'You can join later.'
} catch {
message.textContent =
'Checkout could not open. Please try again.'
} finally {
button.disabled = false
}
})<button id="join-course">Join the course</button>
<p id="course-message" role="status"></p>Install @plandalf/react and place the provider above the button.
import { useState } from 'react'
import { PlandalfProvider, usePresent } from '@plandalf/react'
function CourseButton() {
const { present, presenting } = usePresent()
const [message, setMessage] = useState('')
async function join() {
try {
const result = await present('ceramics-course')
setMessage(result.status === 'complete'
? 'Checkout finished. We are confirming your place.'
: 'You can join later.')
} catch {
setMessage('Checkout could not open. Please try again.')
}
}
return <>
<button disabled={presenting} onClick={join}>
Join the course
</button>
<p role="status">{message}</p>
</>
}
export function CoursePage() {
return <PlandalfProvider apiBase="https://your-org.plandalf.dev">
<CourseButton />
</PlandalfProvider>
}3. Test the whole journey
- Load your page and click the button. Confirm the published
ceramics-courseoffer opens with the expected price and payment environment. If you plan to make a test payment, use an offer and integration that your organization has explicitly configured for sandbox use. Amode: 'test'override can fall back to live checkout for public visitors; it is not a test-mode safety switch. - Dismiss checkout. The page should remain usable; JavaScript and React receive
status: 'dismissed'. - On a confirmed sandbox setup, complete a provider-supported test payment. JavaScript and React receive
status: 'complete', but that browser result does not prove the payment settled. - Check the payment in the intended provider/account and have your server confirm the order before granting course access, sending a download, or shipping a product.
If checkout cannot find the offer, check the host, published slug, and payment setup. For a fuller course integration, see the SDK example. The exact call and result fields are in present(), FlowOpenOptions, and FlowResult.