Paytia development - embedded iframe events
If you embed our checkout rather than redirecting, we send postMessage events to your page so you can resize the frame and react to the outcome. These are separate from webhooks.
Paytia embedded iframe events
Paytia’s web-checkout flow can return a checkout page as an iframe URL instead of redirecting the customer. While the customer pays inside that iframe, Paytia sends browser postMessage events to your host page so you can resize the iframe, react to payment outcome, and track which payment method was selected.
This is separate from webhooks. Webhooks are server-to-server HTTP POSTs to webhook_url for status updates and webhook_url_transaction for transaction outcomes. Iframe events are browser-only and go to whatever you declare in iframeevent_url.
Sources in this repo
How the flow works
1. Initiate checkout via payment_service
POST multipart form data to:
https://accounts.paytia.com/api/payment_service (production)
https://accounts.paytia.com/api/payment_service (dev)
Key fields for iframe mode:
Paytia responds with a checkout_url (sometimes a bare URL, sometimes HTML containing an iframe src). Your host page embeds it:
<iframe id="payment_iframe"
src="https://accounts.paytia.com/…/checkout/…"
style="width:100%; height:700px; border:1px solid #ccc"
referrerpolicy="strict-origin-when-cross-origin"></iframe>
The iframe is cross-origin (Paytia domain). You cannot read its DOM or style its internals. Communication is only via postMessage.
2. Register a listener on the host page
On the parent window (your merchant site, or the QA console API Tester tab):
window.addEventListener("message", function (event) {
const allowedOrigins = [
"https://accounts.paytia.com",
"https://accounts.paytia.com",
"https://accounts.paytia.com",
# check the domain you signed up your account with
];
if (!allowedOrigins.includes(event.origin)) return;
const data = event.data;
if (!data || typeof data !== "object") return;
if (data.type === "resize-iframe") {
const iframe = document.getElementById("payment_iframe");
if (iframe && typeof data.iframeHeight === "number") {
// Fractional heights (e.g. 242.97) need Math.ceil or you get a 1px scrollbar
iframe.style.height = Math.ceil(data.iframeHeight) + "px";
}
}
if (data.type === "payment-status") {
if (data.status === "success") { /* show thank-you, redirect, etc. */ }
if (data.status === "failed") { /* show decline message */ }
if (data.status === "error") { /* validation / system error */ }
}
if (data.type === "payment-method") {
console.log("Customer selected:", data.payment_type);
// card | google | paypal | bank | apple | "" (empty string)
}
});
Direction: Paytia checkout iframe (child) → window.parent.postMessage(...) → your listener.
Origin check: Always verify event.origin against Paytia’s checkout hosts. The QA console ships these defaults in GET /api/api-tester/capabilities → defaultIframeEventOrigins.
3. Paytia sends events as the customer interacts
While the checkout loads, resizes, or completes, the iframe posts structured JSON objects. Three documented event types:
Event types and message examples
resize-iframe — auto-height
Sent when checkout content height changes (form steps, validation messages, wallet buttons, etc.).
Example (toggle between two heights in the QA self-test page):
{
"type": "resize-iframe",
"iframeHeight": 480
}
After the customer expands a section:
{
"type": "resize-iframe",
"iframeHeight": 760
}
Real-world detail: heights are often fractional (e.g. 242.97). The QA console rounds up with Math.ceil() so the iframe is not clipped by a sub-pixel scrollbar.
Host behaviour: set iframe.style.height from iframeHeight; width stays under your control (100%, 375px, etc.).
payment-status — payment outcome
Sent when payment succeeds, fails, or errors. The status string drives branching:
Example — success (from relay-child.html simulator, matches fields the API Tester surfaces):
{
"type": "payment-status",
"status": "success",
"message": "Payment completed",
"payment_status": "paid",
"card_brand": "Visa",
"card_last_four_digit": "4242",
"gateway_response": "Approved",
"customer_message": "Thank you, your payment was successful",
"transaction_id": "TEST-1719408123456",
"amount": "10.00"
}
Example — failed:
{
"type": "payment-status",
"status": "failed",
"message": "Payment declined",
"payment_status": "declined",
"card_brand": "Mastercard",
"card_last_four_digit": "0002",
"gateway_response": "Do Not Honour",
"customer_message": "Your card was declined. Please try another card.",
"user_error_message": "Declined by issuer (51)",
"transaction_id": "TEST-1719408123456",
"amount": "10.00"
}
Fields commonly present on payment-status (API Tester summary table):
With payment_completionscreen=0, your host page should use this payload to show your own confirmation UI instead of Paytia’s built-in completion screen.
With payment_completionscreen=1 (default), Paytia keeps its completion screen inside the iframe; you may still receive the event for logging/analytics.
payment-method — wallet / method selection
Sent when the customer picks or changes payment method (card vs Apple Pay vs Google Pay, etc.).
Example:
{
"type": "payment-method",
"payment_type": "card"
}
Documented payment_type values (from the generated listener template):
card · google · paypal · bank · apple · "" (empty string)
Useful for analytics (“customer opened Google Pay”) or adjusting host-page chrome before payment completes.
Configuration rules
iframeevent_url vs webhooks — do not mix them up
In local dev you typically:
Point webhooks at the WAN relay (PAYTIA_QA_WEBHOOK_BASE_URL)
Point iframeevent_url at http://localhost:8787 (where the API Tester tab is open)
dashboardOrigin drives the auto-fill — it is never derived from the webhook env var. See docs/CONSOLE-ENVIRONMENTS.md.
iframeevent_url must match your listener page
From the master spec (paytia-api.json):
URL that receives checkout-iframe events (resize-iframe / payment-status / payment-method). Must EXACTLY match the page hosting your listener, or no events are sent.
In the QA console, click “Set iframeevent_url to this page” — it sets the value to the current dashboardOrigin (e.g. http://localhost:8787, or https://<relay-host>/wan/<slug> when using the WAN dashboard).
If you paste the test page into W3Schools Tryit, set iframeevent_url to W3Schools (that host) and add that origin to Allowed event origins.
Allowed origins on the listener
The parent filters event.origin. Paytia’s checkout iframe sends from Paytia domains, not from your site. Default allow-list in the console:
https://accounts.paytia.com
https://accounts.paytia.com
Note: The subdomain is variable and can change based on the partner associated with your account.
Messages from devtools, Vite, or setImmediate polyfills are ignored as noise in the API Tester.
End-to-end sequence
sequenceDiagram
participant Host as Your host page
participant PaytiaAPI as Paytia payment_service
participant Iframe as Paytia checkout iframe
Host->>PaytiaAPI: POST payment_service<br/>(is_embedded_url=1, iframeevent_url=Host, transaction_flag=8)
PaytiaAPI-->>Host: checkout_url
Host->>Iframe: embed checkout_url in #payment_iframe
Iframe->>Host: postMessage { type: resize-iframe, iframeHeight: 520 }
Host->>Host: iframe.style.height = 520px
Iframe->>Host: postMessage { type: payment-method, payment_type: card }
Note over Host: Customer enters card details
Iframe->>Host: postMessage { type: payment-status, status: success, ... }
Note over Host: Show your completion UI if payment_completionscreen=0
PaytiaAPI->>Host: POST webhook_url (server-side, separate channel)
More in API
Paytia Proxy Gateway
The Proxy Gateway posts captured payment data to your own endpoint as JSON you define. Nothing is hard-coded, so you shape the payload to whatever's waiting on the other end.
Webhook request button
The Webhook Request button shows every webhook tied to one call or transaction, in order. It's the quickest way to see what your system was actually sent.
Webhooks sent from Paytia to API users for the payment capture IVR flow
What we post back at each stage of an IVR payment, and how your reference_id is carried through every webhook so you can match them up.
API details
The API Details View exposes the internal exchanges between Paytia's telephony and API services. It's a troubleshooting tool — here's how to open it and how to read what's there.
CDR Details Webhooks: Push Data for Telephony Events
Paytia can push call information to a URL you nominate as it happens, so a third-party application can act on it straight away. Here's how the CDR webhook works and what it sends.
How do I add an enhanced API key security on Paytia?
Generating a key is step one. This covers the controls worth switching on afterwards — IP restriction and token-based access — and how to pre-authorise your requests.
Still need help?
Our support team is here to help. Submit a ticket and we'll get back to you within one business day.