# Sell event tickets

Put a **Get a ticket** button on an event page and issue a ticket only after your system has confirmed the order. This example is for a one-day workshop with an offer slug of `spring-workshop-ticket`.

## 1. Set up the ticket checkout

1. Create a ticket product and price in Plandalf. Connect the intended payment integration and check whether the offer is configured for sandbox or live checkout.
2. [Create an offer](https://plandalf.com/docs/product-guides/checkouts/create) for that ticket. Set the customer details you need for admission, check its confirmation behavior, and publish it as `spring-workshop-ticket`.
3. Preview the offer. Check the event name, ticket price, currency, tax and any quantity or add-on options. Keep the published **offer slug** for your site code.

Your event page should still carry the date, venue, and admission information. The offer handles the buying flow; your ticketing system handles capacity and issuing tickets.

## 2. Connect the event page

Replace `https://your-org.plandalf.dev` with your organization's host. Pick one of these ways to open the same ticket offer.

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

```html title="event.html"
<script defer src="https://your-org.plandalf.dev/js/plandalf-sdk.js"></script>
<button data-plandalf-present="spring-workshop-ticket">Get a ticket</button>
```

The script binds the button. HTML has no awaited checkout result; use your server's purchase record to issue the ticket.

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

```javascript title="tickets.js"
import { Plandalf } from '@plandalf/sdk'

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

button.addEventListener('click', async () => {
  button.disabled = true
  try {
    const result = await sdk.present('spring-workshop-ticket')
    message.textContent = result.status === 'complete'
      ? 'Checkout finished. We are confirming your ticket.'
      : 'You can come back for a ticket later.'
  } catch {
    message.textContent = 'Checkout could not open. Please try again.'
  } finally {
    button.disabled = false
  }
})
```

```html title="event.html"
<button id="get-ticket">Get a ticket</button>
<p id="ticket-message" role="status"></p>
```

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

```tsx title="TicketButton.tsx"
import { useState } from 'react'
import { PlandalfProvider, usePresent } from '@plandalf/react'

function TicketButton() {
  const { present, presenting } = usePresent()
  const [message, setMessage] = useState('')

  async function getTicket() {
    try {
      const result = await present('spring-workshop-ticket')
      setMessage(result.status === 'complete'
        ? 'Checkout finished. We are confirming your ticket.'
        : 'You can come back for a ticket later.')
    } catch {
      setMessage('Checkout could not open. Please try again.')
    }
  }

  return <>
    <button disabled={presenting} onClick={getTicket}>Get a ticket</button>
    <p role="status">{message}</p>
  </>
}

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

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

## 3. Verify the ticket handoff

1. Dismiss checkout. No ticket should be issued. Confirm the page is using the intended payment environment before proceeding; a `mode: 'test'` override can fall back to live for public visitors.
2. With an offer and integration explicitly configured for sandbox use, complete a provider-supported test payment. The JavaScript or React page can show a *pending confirmation* message from `status: 'complete'`.
3. Have your server verify the purchase in your payment/order records, record the ticket against that order, and send the admission confirmation once. Test the same order arriving twice so it does not issue two tickets.
4. Recheck the event page and the confirmation email or ticket in the test environment. The browser completion result alone is not a ticket record.

For an early price, [add promo tiers](https://plandalf.com/docs/product-guides/promos/tiers) and pass the promo slug to `present()`. For a complete event example, see [Sell tickets to an event](https://plandalf.com/docs/guides/sell-event-tickets). Exact checkout inputs and outcomes are in [`present()`](https://plandalf.com/docs/sdk/present) and [`FlowResult`](https://plandalf.com/docs/packages/sdk/interfaces/FlowResult).

Source: https://plandalf.com/docs/start/sell-event-tickets
