Documentation menu
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 runs | How the screen opens | How the result returns |
|---|---|---|
| Desktop browser | A window centered over your page | Handed to the SDK; the window closes |
| Mobile browser | A window or tab | Handed to the SDK; the tab closes |
| Installed on the home screen | The phone's in-app browser view, over your app | Handed to the SDK; the view closes |
| A browser that refuses windows | A full-page transition in the same tab | The customer returns to your registered return page with the result |
Procedure#
- Register your web app. In the portal settings, add your web app's origins, your return page and your bank-linking return page.
- Allow A2A InstaPay in your content security policy. Allow the A2A InstaPay domain in
script-srcandconnect-src. Noframe-srcis needed for the hosted screens. - Keep credentials on your server. The browser only ever holds single-use session and result tokens.
- Load and initialize the SDK with your SDK key.
- Create the record and the session on your server. The response carries the session token.
- Open the screen from a tap. Call
A2ALink.openinside the click or tap handler. - Receive the result. In
onSuccess, send the result token to your server, which exchanges it. - 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.
<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#
| Call | Parameters | Description |
|---|---|---|
A2ALink.init(options) | sdkKey, environment (sandbox or production) | Once per page load |
A2ALink.open(options) | token or fetchToken; onSuccess, onExit, onEvent; optional returnUrl | Opens a hosted screen. Must be called inside a tap or click handler |
onSuccess(result) | resultToken, flow, sessionId | Called once when the customer completes the step |
onExit(info) | reason: closed, canceled or expired | The customer left, or the session expired |
onEvent(name, details) | opened, window_refused, full_page_transition, completed, exited | Progress, for logging. No customer data |
A2ALink.handleReturn() | On the return page: returns status, resultToken, flow and sessionId |
Exchange the result on your server#
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_decisionA result token can be exchanged once, only with your credentials.
The return page#
<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.