# Sell a course from your own site

Imagine you run a ceramics course. Your website explains the lessons and has a **Join the course** button. Plandalf handles the checkout when someone clicks it; your site stays responsible for giving them access to the lessons.

## Set it up

1. Publish a Plandalf offer for the course. In this example its slug is `ceramics-course`.
2. Replace `https://your-org.plandalf.dev` with your organization's host.
3. Put the button on your course page. Choose HTML attributes, JavaScript, or React below.

The offer slug is just a name you choose in Plandalf. The same slug appears in the code below.

<scalar-tabs default="HTML">
<scalar-tab title="HTML">

Load the browser bundle from your organization's host. The SDK finds the button and opens the offer when it is clicked.

```html title="course.html" lineNumbers lineNumberStart=1 highlight="2"
<script defer src="https://your-org.plandalf.dev/js/plandalf-sdk.js"></script>
<button data-plandalf-present="ceramics-course">Join the course</button>
```

The HTML button opens checkout. Use a server purchase confirmation to unlock lessons; this markup does not receive an awaited result.

</scalar-tab>
<scalar-tab title="JavaScript">

```javascript title="course.js" lineNumbers lineNumberStart=1 highlight="7-8"
import { Plandalf } from '@plandalf/sdk'

const sdk = new Plandalf({ apiBase: 'https://your-org.plandalf.dev' })
const button = document.querySelector('#join-course')

button.addEventListener('click', async () => {
  const handle = sdk.present('ceramics-course')
  const result = await handle
  if (result.status === 'complete') {
    // Ask your server to confirm this purchase, then unlock lessons.
  }
})
```

```html title="course.html"
<button id="join-course">Join the course</button>
```

</scalar-tab>
<scalar-tab title="React">

```tsx title="CourseButton.tsx" lineNumbers lineNumberStart=1 highlight="4,7-8"
import { PlandalfProvider, usePresent } from '@plandalf/react'

function CourseButton() {
  const { present, presenting } = usePresent()

  async function join() {
    const handle = present('ceramics-course')
    const result = await handle
    if (result.status === 'complete') {
      // Ask your server to confirm this purchase, then unlock lessons.
    }
  }

  return <button disabled={presenting} onClick={join}>Join the course</button>
}

export function CoursePage() {
  return (
    <PlandalfProvider apiBase="https://your-org.plandalf.dev">
      <CourseButton />
    </PlandalfProvider>
  )
}
```

</scalar-tab>
</scalar-tabs>

## What happens for the buyer

They click **Join the course**, complete or dismiss the checkout, and return to your page. In JavaScript and React, the awaited `result.status` tells your page which happened. The browser result is useful for the page message; your server must confirm the purchase before it grants course access. A failed checkout request rejects the handle, so add your site's normal error message around the call.

Want the buy button to open ticket checkout instead? See [Sell event tickets](https://plandalf.com/docs/guides/sell-event-tickets). Want a deadline that changes the next offer? See [Event merch deadline](https://plandalf.com/docs/guides/event-merch-deadline).

## Follow the types

- [`Plandalf.present()`](https://plandalf.com/docs/packages/sdk/classes/Plandalf#present) lists the JavaScript call signature.
- [`usePresent()`](https://plandalf.com/docs/packages/react/functions/usePresent) lists the React hook signature.
- [`FlowOpenOptions`](https://plandalf.com/docs/packages/sdk/interfaces/FlowOpenOptions) lists frame, continuity, identity, and metadata inputs.
- [`FlowResult`](https://plandalf.com/docs/packages/sdk/interfaces/FlowResult) lists the awaited outcome.
- [`FlowHandleEvent`](https://plandalf.com/docs/packages/sdk/type-aliases/FlowHandleEvent) lists live events if you later want a progress indicator.

Source: https://plandalf.com/docs/guides/await-a-checkout
