# One-off Purchases

The Allegro SDK provides `allegro.purchase.initiate()` to start a Stripe Checkout session for a plan. After the member completes payment, Stripe redirects them back to your site and the SDK automatically exchanges the result for a JWT and entitlement data.

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

* A Stripe account connected in the admin. See [Payment Provider](/product/entitlements/payment-provider.md).
* The [Stripe webhook](/developer/platform/stripe-webhooks.md) configured on the platform (recommended for reliable fulfillment).
* The plan ID for the offer plan you want to sell. Find plan IDs in the admin under **Entitlements → Offers**.

## Initiating Checkout[​](#initiating-checkout "Direct link to Initiating Checkout")

Call `allegro.purchase.initiate(planId)` to redirect the member to Stripe Checkout:

```js
window.allegro.push(function (allegro) {
    allegro.purchase.initiate('plan_abc123');
});

```

The member is redirected to Stripe. After payment, Stripe redirects them back to the current page URL by default.

### Custom Return URL[​](#custom-return-url "Direct link to Custom Return URL")

Pass a second argument to control where Stripe redirects after checkout:

```js
window.allegro.push(function (allegro) {
    allegro.purchase.initiate('plan_abc123', 'https://example.com/thank-you');
});

```

Use this if you want to send members to a dedicated confirmation page after checkout rather than the page they were on.

### Triggering from a Button[​](#triggering-from-a-button "Direct link to Triggering from a Button")

A typical integration attaches the call to a button click:

```html
<button id="subscribe-btn">Subscribe — $9.99/mo</button>

<script>
    document
        .getElementById('subscribe-btn')
        .addEventListener('click', function () {
            window.allegro.push(function (allegro) {
                allegro.purchase.initiate('plan_abc123');
            });
        });
</script>

```

## Handling the Return[​](#handling-the-return "Direct link to Handling the Return")

After checkout, Stripe redirects the member back to your site. The SDK detects the result automatically on page load.

**Success** — Stripe appends `allegro_purchase_token` to the return URL. The SDK exchanges it for a JWT and entitlement, then:

* Signs the member in (or upgrades their session if already signed in).
* Dispatches `allegro:purchase:completed` on `window`.
* Removes `allegro_purchase_token` from the URL.

**Failure or cancellation** — Stripe appends `allegro_purchase_error=payment_failed`. The SDK:

* Dispatches `allegro:purchase:failed` on `window`.
* Removes the error param from the URL.

### Listening for Completion[​](#listening-for-completion "Direct link to Listening for Completion")

```js
window.addEventListener('allegro:purchase:completed', function (event) {
    // event.detail.entitlement contains the newly granted entitlement
    const entitlement = event.detail.entitlement;
    console.log('Access granted to:', entitlement.resource.name);
    // e.g. show a confirmation banner, unlock content, etc.
});

window.addEventListener('allegro:purchase:failed', function (event) {
    // event.detail.reason is 'payment_failed' or similar
    console.log('Purchase failed:', event.detail.reason);
});

```

### The `allegro:purchase:completed` Event[​](#the-allegropurchasecompleted-event "Direct link to the-allegropurchasecompleted-event")

The `detail` object on `allegro:purchase:completed` contains:

| Property      | Type     | Description                                |
| ------------- | -------- | ------------------------------------------ |
| `method`      | `string` | Always `'purchase'`.                       |
| `entitlement` | `object` | The newly created entitlement (see below). |

The `entitlement` object:

| Property        | Type             | Description                                   |
| --------------- | ---------------- | --------------------------------------------- |
| `id`            | `string`         | Entitlement ID.                               |
| `resource.id`   | `string`         | Product ID.                                   |
| `resource.slug` | `string`         | Product slug.                                 |
| `resource.name` | `string`         | Human-readable product name.                  |
| `started_at`    | `string \| null` | ISO 8601 start date, or `null` if indefinite. |
| `ended_at`      | `string \| null` | ISO 8601 end date, or `null` if indefinite.   |
| `is_active`     | `boolean`        | Whether the entitlement is currently active.  |

### Checking Validation State[​](#checking-validation-state "Direct link to Checking Validation State")

`allegro.purchase.validation` reflects the outcome of token exchange for the current page load:

| Value         | Meaning                                         |
| ------------- | ----------------------------------------------- |
| `'completed'` | Purchase was verified and entitlements granted. |
| `'failed'`    | Payment failed or was cancelled.                |
| `null`        | No purchase token present on this page load.    |

warning

Do not read `validation` at `allegro:ready` time

Token exchange is asynchronous. `allegro.purchase.validation` may still be `null` when `allegro:ready` fires. Listen for `allegro:purchase:completed` or `allegro:purchase:failed` instead.

## Complete Example[​](#complete-example "Direct link to Complete Example")

```html
<!DOCTYPE html>
<html>
    <head>
        <script src="https://your-allegro-instance.com/client.js"></script>
    </head>
    <body>
        <div id="content-gate">
            <p>Subscribe for full access.</p>
            <button id="subscribe-btn">Subscribe — $9.99/mo</button>
        </div>

        <div id="confirmation" style="display:none">
            <p>Thanks for subscribing! You now have access.</p>
        </div>

        <script>
            document
                .getElementById('subscribe-btn')
                .addEventListener('click', function () {
                    window.allegro.push(function (allegro) {
                        allegro.purchase.initiate('plan_abc123');
                    });
                });

            window.addEventListener('allegro:purchase:completed', function () {
                document.getElementById('content-gate').style.display = 'none';
                document.getElementById('confirmation').style.display = 'block';
            });

            window.addEventListener('allegro:purchase:failed', function () {
                alert('Payment was not completed. Please try again.');
            });
        </script>
    </body>
</html>

```

## What Happens Server-Side[​](#what-happens-server-side "Direct link to What Happens Server-Side")

When a purchase is fulfilled:

1. Allegro creates a `Purchase` record linked to the member, plan, and Stripe session.
2. For each product on the plan, Allegro creates an `Entitlement` for the member with the plan's configured duration.
3. A `purchase` event is recorded in the member's event history.

If the webhook fires before the member returns to your site (e.g., the browser was closed), fulfillment is still completed via the webhook. The token exchange on the return page detects the already-fulfilled purchase without creating duplicates.

## Pending Purchases[​](#pending-purchases "Direct link to Pending Purchases")

Checkout sessions that are initiated but never completed (e.g., the member abandons the Stripe checkout page) are recorded as `pending` purchases. Pending purchases are automatically pruned after 24 hours — matching Stripe's checkout session expiry — via the `allegro:prune` scheduler command. Pending purchases do not appear in the admin purchases list.

## Related[​](#related "Direct link to Related")

* [Stripe Webhook Setup](/developer/platform/stripe-webhooks.md) — Platform-level webhook configuration for reliable fulfillment
* [Payment Provider (Product)](/product/entitlements/payment-provider.md) — Connecting a payment provider as an operator
* [Entitlements (Product)](/product/entitlements.md) — What members receive after purchase
* [Offers and Plans (Product)](/product/entitlements/offers.md) — Configuring plans and offers
