Docs / Build

Hosted screens and the web SDK

Open A2A InstaPay's hosted screens from a web app (WAP) with the A2A Link SDK, including installed web apps on phones.

The hosted screens always run on the A2A InstaPay domain in their own window, never inside your page, so that trusted devices and passkeys work and your app never sees a PIN. The A2A Link SDK opens them and hands the result back to your app.

How the screen opens#

Where your web app runsHow the screen opensHow the result returns
Desktop browserA window centered over your pageHanded to the SDK; the window closes
Mobile browserA window or tabHanded to the SDK; the tab closes
Installed on the home screenThe phone's in-app browser view, over your appHanded to the SDK; the view closes
A browser that refuses windowsA full-page transition in the same tabThe customer returns to your registered return page with the result

Procedure#

  1. Register your web app. In the portal settings, add your web app's origins, your return page and your bank-linking return page.
  2. Allow A2A InstaPay in your content security policy. Allow the A2A InstaPay domain in script-src and connect-src. No frame-src is needed for the hosted screens.
  3. Keep credentials on your server. The browser only ever holds single-use session and result tokens.
  4. Load and initialize the SDK with your SDK key.
  5. Create the record and the session on your server. The response carries the session token.
  6. Open the screen from a tap. Call A2ALink.open inside the click or tap handler.
  7. Receive the result. In onSuccess, send the result token to your server, which exchanges it.
  8. Handle the return page for browsers that refuse windows.

Open a screen#

Browsers allow a window only in direct response to a tap or click. Call A2ALink.open at once inside the handler. If you do not have the token yet, pass fetchToken: the SDK opens the window immediately and loads the screen when the token arrives.

Your web app
<script src="https://sandbox.a2ainstapay.com/v1/a2a-link.js"></script>
<script>
  A2ALink.init({ sdkKey: "pk_sbx_your_sdk_key", environment: "sandbox" });

  document.getElementById("pay").addEventListener("click", () => {
    A2ALink.open({
      fetchToken: async () => {
        const res = await fetch("/api/checkout/pay", { method: "POST", body: JSON.stringify({ orderId }) });
        return (await res.json()).sessionToken; // confirmation_session.token from POST /v1/payments
      },
      returnUrl: "https://app.example.com/a2a/return",
      onSuccess: async ({ resultToken }) => {
        await fetch("/api/a2a/exchange", { method: "POST", body: JSON.stringify({ resultToken }) });
        showProcessing(); // the final status arrives by webhook
      },
      onExit: ({ reason }) => showNotCompleted(reason), // closed, canceled or expired
      onEvent: (name, details) => console.debug(name, details),
    });
  });
</script>

SDK reference#

CallParametersDescription
A2ALink.init(options)sdkKey, environment (sandbox or production)Once per page load
A2ALink.open(options)token or fetchToken; onSuccess, onExit, onEvent; optional returnUrlOpens a hosted screen. Must be called inside a tap or click handler
onSuccess(result)resultToken, flow, sessionIdCalled once when the customer completes the step
onExit(info)reason: closed, canceled or expiredThe customer left, or the session expired
onEvent(name, details)opened, window_refused, full_page_transition, completed, exitedProgress, for logging. No customer data
A2ALink.handleReturn()On the return page: returns status, resultToken, flow and sessionId

Exchange the result on your server#

Your server (Node.js)
const res = await fetch(`${A2A_BASE_URL}/v1/sessions/exchange`, {
  method: "POST",
  headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
  body: JSON.stringify({ result_token: resultToken }),
});
const outcome = await res.json(); // verification_status, target_status, terms_decision

A result token can be exchanged once, only with your credentials.

The return page#

https://app.example.com/a2a/return
<script src="https://sandbox.a2ainstapay.com/v1/a2a-link.js"></script>
<script>
  A2ALink.init({ sdkKey: "pk_sbx_your_sdk_key", environment: "sandbox" });
  const result = A2ALink.handleReturn(); // { status, resultToken, flow, sessionId }
  if (result && result.status === "completed") {
    fetch("/api/a2a/exchange", { method: "POST", body: JSON.stringify({ resultToken: result.resultToken }) })
      .then(() => resumeWhereTheCustomerLeftOff(result.flow));
  } else {
    showNotCompleted(result ? result.status : "unknown");
  }
</script>

Rules for web apps#

  • Open hosted screens only from a tap or click, and never in an iframe or a WebView.
  • Call the Tenant API only from your servers.
  • Trusted-device status is remembered by each browser and each installed web app. On an iPhone, an installed web app keeps its own storage, so a customer who uses both Safari and the installed app completes the new-device check once in each.
  • Passkeys are offered where the browser and device support them; otherwise the customer uses the PIN.
  • If your web app is distributed through an app store inside a native shell, use the native SDKs through the shell.

Test it#

The demo app uses exactly this code. Block pop-ups in your browser to see the full-page transition and return page at work.

Questions about this page? Contact developer support.