Mediquo documentation home

mq-checkout

A use-case agnostic payment gateway web component. Embeds the Mediquo payment portal, polls for completion and reports the outcome through generic events.

<mq-checkout> renders only the payment gateway: it embeds the Mediquo payment portal in an iframe, polls the backend until the payment resolves, and emits a generic outcome. It knows nothing about appointments, services or any specific product — the host decides how a payment session is created by supplying a provideSession callback.

If you are charging for an appointment, use <mq-appointment-checkout> instead — it wraps <mq-checkout> and creates the session for you from a slot and service.

How it works

  1. You provide a provideSession() async callback that returns { sessionId, paymentUrl }.
  2. <mq-checkout> authenticates (api-key + token), then calls provideSession() and embeds paymentUrl.
  3. It polls the payment status every 3 seconds (up to 10 minutes).
  4. On paid it fires payment-success; on denied it fires payment-denied; on timeout/auth failure it fires payment-error.
  5. If the portal asks to retry, <mq-checkout> calls provideSession() again to get a fresh session — the integrator does nothing.

Dependencies

<mq-checkout> must be rendered inside an <mq-theme-provider> ancestor, which supplies the design tokens it styles against.

It does not require <mq-query-client-provider> or <mq-socket-provider>.

Installation

npm install @mediquo/web-components
import "@mediquo/web-components/mq-checkout";
import "@mediquo/web-components/mq-theme-provider";

Usage

provideSession is a function and therefore must be assigned as a JS property — it cannot be an HTML attribute.

HTML / vanilla JS

<mq-theme-provider theme="mediquo">
  <mq-checkout
    id="checkout"
    env="production"
    api-key="<your-api-key>"
    token="<your-token>"
    locale="es_ES"
  ></mq-checkout>
</mq-theme-provider>

<script type="module">
  const checkout = document.getElementById("checkout");

  // Called for the initial load AND on every retry.
  checkout.provideSession = async () => {
    const res = await fetch("https://your-backend.example/payments", {
      method: "POST",
    });
    const { sessionId, paymentUrl } = await res.json();
    return { sessionId, paymentUrl };
  };

  checkout.addEventListener("payment-success", (e) => {
    console.log("paid", e.detail.sessionId, e.detail.reference);
  });
</script>

React / Next.js

Set the callback through a ref (web component properties cannot be passed as JSX attributes when they are functions):

"use client";
import { useEffect, useRef } from "react";

export function Checkout({ apiKey, token, createSession }) {
  const ref = useRef<HTMLElement>(null);

  useEffect(() => {
    import("@mediquo/web-components/mq-checkout");
    import("@mediquo/web-components/mq-theme-provider");
  }, []);

  useEffect(() => {
    const el = ref.current as (HTMLElement & { provideSession?: () => Promise<unknown> }) | null;
    if (!el) return;

    el.provideSession = createSession;

    const onSuccess = (e: Event) => console.log((e as CustomEvent).detail);
    el.addEventListener("payment-success", onSuccess);

    return () => {
      el.removeEventListener("payment-success", onSuccess);
    };
  }, [createSession]);

  return (
    <div style={{ height: "700px" }}>
      {/* @ts-expect-error — custom element */}
      <mq-theme-provider theme="mediquo">
        {/* @ts-expect-error — custom element */}
        <mq-checkout
          ref={ref}
          env="production"
          api-key={apiKey}
          token={token}
          locale="es_ES"
        />
      </mq-theme-provider>
    </div>
  );
}

Attributes & properties

NameKindTypeRequiredDescription
envattributestringNoBackend environment: "development" or "production". Applied once at mount.
api-keyattributestringYesAPI key for the Mediquo platform. Used to authenticate the status polling.
tokenattributestringYesMediquo user token, exchanged internally for an access token.
localeattributestringNoLocale code: es_ES, en_US, pt_PT, de_DE, ca_ES. Defaults to es_ES.
provideSessionproperty() => Promise<{ sessionId: string; paymentUrl: string }>YesAsync factory returning the payment session. Invoked for the initial load and on every retry. Must be set as a JS property.

Events

All events bubble and cross shadow boundaries (composed: true).

EventDetailDescription
payment-success{ sessionId: string; reference: string }Payment confirmed. reference is the backend reference for the paid resource.
payment-denied{ sessionId: string }Payment was rejected by the gateway. Polling stops immediately.
payment-error{ reason: "auth" | "session" | "timeout" }Authentication failed, the session could not be created, or polling timed out (10 minutes).
payment-retryNotification only. The portal requested a retry; <mq-checkout> re-creates the session automatically. No action required.

Listening to events

const checkout = document.querySelector("mq-checkout");

checkout.addEventListener("payment-success", (e) => {
  console.log("paid", e.detail.reference);
});
checkout.addEventListener("payment-denied", () => {
  console.log("denied");
});
checkout.addEventListener("payment-error", (e) => {
  console.log("error", e.detail.reason);
});

Environment

Select the backend with the env attribute ("development" or "production", default "production").

Set env once at mount. Changing it after the component has authenticated rebuilds the underlying API client and drops the in-flight session.

Content Security Policy

The component embeds the payment portal in an <iframe>. If your app sets a CSP, allowlist the portal origin under frame-src (the origin matches the paymentUrl returned by provideSession, and differs per environment).

Theming

Wrap the component in <mq-theme-provider> to apply a preset or custom tokens:

<mq-theme-provider theme="custom" style="--color-primary: #e63946;">
  <mq-checkout ...></mq-checkout>
</mq-theme-provider>

Available preset themes: mediquo, adeslas, default.