Payment Sessions

Create a payment server-side, then send the donor through Repejo's hosted checkout (redirect) or an embedded <repejo-checkout> element, and receive the outcome via webhooks. This is Repejo's "Pay by Link" capability. The donor completes the recurring mandate (e.g. Autogiro) with BankID on their own device.

Machine-readable spec

The OpenAPI document is the source of truth — it contains every operation, schema, enum and error. Coding agents: point your tool at the OpenAPI URL.

Authentication

All requests use a Bearer API token scoped to a single organisation. An organisation admin mints one in the back office under Settings → API-nycklar; the secret is shown once.

Authorization: Bearer <api_token>

A missing or invalid token returns 401:

{ "errors": { "detail": "Unauthorized" } }

One checkout per periodicity

An api checkout is locked to a single payment period. To offer both one-time and monthly donations, create two separate checkouts — one per periodicity — each with its own checkout_id (chk_…), and pick the one that matches the donation type when creating the session. A payment_method whose periodicity doesn't match the checkout is rejected with 422 payment_method_not_valid_for_period.

Quickstart

  1. Mint an org-scoped API token (back office, once).
  2. Create an api ("API") type checkout per periodicity and note each checkout_id (shown on the checkout's Användning tab).
  3. Create a payment session (below).
  4. Redirect the donor to payment_url, or embed the checkout.
  5. Reconcile from the payment_session.* webhooks.
curl -X POST https://app.repejo.se/api/v1/payment_session \
  -H "Authorization: Bearer <api_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 200,
    "currency": "SEK",
    "checkout_id": "chk_123412",
    "payment_method": "autogiro",
    "metadata": { "lead_id": "12345" },
    "success_url": "https://example.org/success",
    "cancel_url": "https://example.org/cancel"
  }'

Response 201:

{
  "payment_url": "https://app.repejo.se/s/aunt123?rp_token=abc123",
  "session_token": "abc123",
  "short_code": "aunt123"
}

Request — POST /api/v1/payment_session

  • amount (number, required) — the donation amount, > 0, in the checkout's currency. The checkout's periodicity decides how it is charged: on a recurring checkout it is the per-period amount (e.g. monthly), confirmed by payment_session.succeeded (mandate signed); on a one-time checkout it is the single charge.
  • currency (required) — one of SEK, EUR. Must be a currency the checkout offers.
  • checkout_id (string, required) — must be an api-type checkout.
  • payment_method (optional) — preselects and opens that method; one of autogiro, swish, swish_onetime, card, apple_pay, google_pay, sepa_direct_debit. Must be enabled on the checkout. Omit to let the donor choose.
  • payer (optional object) — first_name, last_name, phone_number, email, all optional.
  • metadata (optional object) — echoed on every webhook (e.g. lead_id).
  • success_url / cancel_url (required) — absolute http/https URLs the donor returns to.

Errors

  • 400 — malformed body, or currency/payment_method outside the enum.
  • 401 — missing/invalid token.
  • 422 {"error":"checkout_not_found"} — the checkout_id is unknown or malformed (e.g. missing the chk_ prefix).
  • 422 {"error":"checkout_not_api"} — the checkout isn't an api checkout.
  • 422 {"error":"currency_not_enabled"} — the currency isn't one the checkout offers (e.g. EUR on a SEK-only checkout).
  • 422 {"error":"payment_method_not_enabled"} — that method isn't enabled on the checkout.
  • 422 {"error":"payment_method_not_valid_for_period"} — that method's periodicity doesn't match the checkout (e.g. swish_onetime on a recurring checkout, or autogiro on a one-time checkout).
  • 422 {"error":"invalid_success_url"} / 422 {"error":"invalid_cancel_url"} — the URL isn't an absolute http/https URL. Non-http(s) schemes (javascript:, data:), relative paths and empty strings are rejected.

Integration modes

Redirect

Redirect the donor's browser to payment_url. The hosted checkout skips the amount screen and opens the (optionally preselected) payment method. On completion Repejo redirects to your success_url; the back button cancels and redirects to your cancel_url.

Embedded

Render the custom element on your own page (load checkout.js once), passing the short_code and session_token from the response.

<script defer src="https://app.repejo.se/assets/checkout.js" data-host="https://app.repejo.se"></script>

<repejo-checkout short_code="aunt123" session-token="abc123" host="https://app.repejo.se"></repejo-checkout>

In embedded mode the element dispatches DOM events instead of redirecting. They bubble from the <repejo-checkout> element up to window, so listen on either. Both recurring (mandate signed) and one-time (payment captured) completions fire repejo:completed; event.detail carries type ("recurring" | "onetime").

window.addEventListener("repejo:completed", (e) => { /* e.detail.type → show your thank-you */ });
window.addEventListener("repejo:cancelled", (e) => { /* donor cancelled */ });

The element also emits informational events you can ignore: repejo:first-interaction (donor started) and repejo:registered (left the amount step). Only repejo:completed / repejo:cancelled are part of the contract.

Treat the payment_session.succeeded webhook as the authoritative signal; the DOM events are a UX convenience.

Webhooks

Outcomes are delivered to the tenant webhook endpoints you register in the back office (subscribe them to the payment_session.* events). Delivery is asynchronous, signed and retried.

type PaymentSessionEvent =
| "payment_session.created"    // session created (donor not yet finished)
| "payment_session.succeeded"  // recurring mandate signed OR one-time payment captured — mark the lead done
| "payment_session.cancelled"; // donor cancelled

payment_session.succeeded is the single reconciliation signal for both periodicities: a recurring checkout fires it when the mandate is signed, a one-time checkout when the single payment (Swish / card) is captured. You do not need to consume transaction.* to confirm a one-time gift.

Each delivery has this envelope; data.metadata carries your lead_id:

{
  "sent_at": "2026-06-13T12:00:00Z",
  "event_type": "payment_session.succeeded",
  "data": {
    "id": "pls_8x…",
    "checkout_id": "chk_123412",
    "status": "succeeded",
    "amount": "200",
    "currency": "SEK",
    "payment_method": "autogiro",
    "metadata": { "lead_id": "12345" }
  }
}

See the Webhooks reference for the full signing (Repejo-Signature: sha256=<hex>), retry and idempotency contract.