Karelia TicketsSell event tickets from Statamic with Stripe

Documentation

Getting started, features and limits of Karelia Tickets.

On this page
  1. What you need
  2. Install
  3. Setup
  4. How a purchase works
  5. At the door
  6. Refunds and free tickets
  7. Where orders are kept
  8. Configuration
  9. Permissions
  10. Good to know

What you need

  • Statamic 6.20 or later, on PHP 8.3 or later.
  • Composer on the server or in your deployment.
  • A Stripe account for paid tickets. Free events work without one.
  • Email set up in Laravel (MAIL_MAILER), so the tickets can be sent.

Install

  1. In the folder where your site is installed, run:
composer require karelialabs/statamic-tickets
  1. Buy a licence on the Statamic Marketplace and attach it to your site in your statamic.com account, as with any paid addon. Local and staging copies of the site need no licence.

Tickets and Check-in appear under Tools in the Control Panel. Then follow the setup below: Stripe keys, the webhook, your events and the ticket form.

Setup

1. Stripe keys go in .env

KARELIA_TICKETS_STRIPE_SECRET=sk_live_...
KARELIA_TICKETS_STRIPE_WEBHOOK_SECRET=whsec_...
KARELIA_TICKETS_CURRENCY=EUR

Use a restricted key if you like. It needs write access to Checkout Sessions and Refunds. With an sk_test_ key everything runs in Stripe's test mode and the Control Panel says so.

2. The Stripe webhook

The webhook lets Stripe tell your site about a payment even when the buyer closes the tab before coming back. In the Stripe Dashboard, go to Developers → Webhooks → Add endpoint:

  • Endpoint URL: https://your-site.com/!/statamic-tickets/webhook (shown under Tools → Tickets)
  • Events: checkout.session.completed, checkout.session.async_payment_succeeded, checkout.session.async_payment_failed, checkout.session.expired, charge.refunded

Copy the endpoint's signing secret into KARELIA_TICKETS_STRIPE_WEBHOOK_SECRET. Events without a valid signature are refused.

3. Your events

Karelia Tickets adds a Tickets tab to the entries of your events collection, events by default. Choose another collection, the date field and the currency under Tools → Addons → Karelia Tickets → Settings.

Open an event, switch on Sell tickets and add ticket types:

Field
Namee.g. Early bird, Standard, Student
Price0 for free tickets
Tickets availableLeave empty for no limit
Sales endOptional date and time
Most per orderDefault 10
DescriptionShown under the name

The event's date comes from its start_date field (or the field you choose), or the entry date.

4. The ticket form

Put the tag in your event template:

{{ tickets:form }}

It shows the ticket types with prices, what is left, a name and email field and the total, and sends the buyer to Stripe. It follows your site's font and colours. Set --kt-accent to brand the button:

.kt { --kt-accent: #0f766e; --kt-radius: 0.5rem; }

For your own markup, use the tag as a pair. The form, the CSRF token and the event are added for you:

{{ tickets:form class="tickets" }}
    {{ if has_errors }}
        <ul class="errors">{{ errors }}<li>{{ value }}</li>{{ /errors }}</ul>
    {{ /if }}

    {{ types }}
        <label>
            {{ name }} – {{ price_formatted }}
            {{ if sold_out }}Sold out{{ elseif few_left }}Only {{ left }} left{{ /if }}
            {{ if on_sale }}
                <select name="{{ input_name }}">
                    {{ options }}<option value="{{ value }}">{{ value }}</option>{{ /options }}
                </select>
            {{ /if }}
        </label>
    {{ /types }}

    <input name="name" value="{{ old_name }}" required>
    <input type="email" name="email" value="{{ old_email }}" required>
    <button>Get tickets</button>
{{ /tickets:form }}

Variables inside the pair: title, date, types, selling, sold_out, on_sale, any_paid, currency, errors, has_errors, message, old_name, old_email. Each type has id, name, description, price, price_formatted, free, left, few_left, sold_out, on_sale, sales_ended, sales_end, max, options, input_name.

Other tags: {{ tickets:sold_out }}, {{ tickets:on_sale }} and {{ tickets:types }}...{{ /tickets:types }}. All of them use the current entry, or event="entry-id".

To change the ready-made form, the ticket page or the email, publish the views:

php artisan vendor:publish --tag=statamic-tickets-views

How a purchase works

  1. The buyer chooses tickets and enters their name and email. The tickets are held for 30 minutes.
  2. They pay on Stripe Checkout.
  3. Stripe sends them back to their ticket page and calls the webhook. Whichever comes first issues the tickets; the other changes nothing.
  4. They get an email with the ticket codes, a link to the ticket page with the QR codes, and the event as a calendar file.

If they go back from Stripe without paying, the order is cancelled, the Stripe session is closed and the tickets go back on sale. Free tickets skip Stripe and are issued straight away.

At the door

Open Tools → Check-in on a phone, choose the event and press Scan with camera, or type the code. Codes are 8 letters and digits without look-alikes (no 0/O, 1/I/L), and dashes, spaces and lower case are fine. Each ticket gets in once; Undo reverses a check-in. Several people can check in at the same time.

Give door staff a role with only the Check in attendees permission: they see the check-in page and the name on each scanned ticket, not your sales or the attendee list.

Refunds and free tickets

On an event's page under Tools → Tickets, each order has a menu:

  • Refund order refunds the full amount through Stripe and the tickets stop working. Refunds made in the Stripe Dashboard are picked up by the webhook too. A partial refund keeps the tickets valid.
  • Email tickets again resends the confirmation.
  • Cancel tickets for free orders.

Add free tickets issues tickets for guests, press or staff without payment, optionally by email. They count towards the number available.

Where orders are kept

Orders are JSON files in storage/statamic/karelia-tickets/orders, one per order, outside git. They hold the buyer's name and email address, the tickets and the Stripe payment ID. No card details ever reach your site. Back up the storage folder with the rest of your site.

Configuration

Publish the config file to change the defaults:

php artisan vendor:publish --tag=statamic-tickets-config
KeyDefault
stripe.secretKARELIA_TICKETS_STRIPE_SECRETStripe secret or restricted key
stripe.webhook_secretKARELIA_TICKETS_STRIPE_WEBHOOK_SECRETSigning secret of the webhook endpoint
stripe.version2026-09-30.endiveStripe API version
collections['events']Collections that sell tickets
date_fieldstart_dateField with the event's start
currencyKARELIA_TICKETS_CURRENCY, USDMust be a currency Stripe supports for your account
reservation_minutes30How long tickets are held during checkout (30 to 1440)
pathstorage/statamic/karelia-ticketsWhere orders are stored

Permissions

  • View tickets and orders: sales, orders and attendee lists
    • Refund, cancel and resend orders, and add free tickets
  • Check in attendees: the check-in page only

Good to know

  • One currency per site. Changing it later does not convert old orders.
  • The ticket page link in the email works without logging in. Anyone with the link can see the tickets, like a paper ticket.
  • The price you set is what the buyer pays. Karelia Tickets does not add tax on top.
  • Camera scanning needs HTTPS (or localhost) and permission for the camera. Typing the code always works.
  • On sites with several servers, use a shared cache store (Redis, database) so the locks that stop overselling are shared too.