Skip to content

Communication Protocol

The host app uses postMessage to send messages to the embedded app. This is the recommended and secure method for authentication and session management. Alternatively, for convenience in specific scenarios, the signature may be passed via URL parameters, though this approach offers lower security and should be avoided when possible.

Events

The embedded myFlowpay application emits and listens for the following events:

The following diagram illustrates the typical event flow between the host application and the embedded app:

sequenceDiagram
    participant PartnerBackend as Partner: Backend Server
    participant Host as Partner: Host App
    participant Embedded as Flowpay: Embedded App
    participant Backend as Flowpay: Backend Server

    Note over Embedded: Iframe loads
    Embedded->>Host: fp:READY
    Note over Host: App is ready<br />to receive login

    Host->>PartnerBackend: Request signature<br />(payload)
    PartnerBackend->>Host: Payload & signature
    Host->>Embedded: fp:LOGIN<br />(with payload & signature)
    Note over Embedded: Validates signature

    alt Authentication Success
        Embedded->>Backend: Authenticate<br />(payload & signature)
        Backend->>Embedded: Auth token
        Note over Embedded: Saves token to<br />browser cookies
        Embedded->>Host: fp:LOGIN_SUCCESS
        Note over Host,Embedded: Session active

        loop During Active Session
            Note over Embedded: ~60 seconds before<br />token expiry
            Embedded->>Host: fp:SESSION_EXPIRING
            Note over Host: Refresh session
            Host->>PartnerBackend: Request signature<br />(payload)
            PartnerBackend->>Host: Payload & new signature
            Host->>Embedded: fp:LOGIN<br />(with payload & signature,<br />reason: "refresh")
            Embedded->>Backend: Authenticate<br />(payload & signature)
            Backend->>Embedded: Auth token
            Note over Embedded: Updates token in<br />browser cookies
            Embedded->>Host: fp:LOGIN_SUCCESS
        end

        opt User Action: Logout
            Host->>Embedded: fp:LOGOUT
            Note over Embedded: Clears cookies
            Embedded->>Host: fp:LOGOUT_SUCCESS
        end

        opt External URL Needed
            Embedded->>Host: fp:OPEN_EXTERNAL (url)
            Note over Host: Opens URL in<br />new window
        end
    else Authentication Failure
        Embedded->>Host: fp:ERROR
        Note over Host: Can retry with<br />new fp:LOGIN
    end

Events Emitted by Embedded App (Embedded → Host)

Event Direction Payload Description
fp:READY Embedded → Host undefined Emitted when the embedded app is loaded and ready to receive authentication messages.
fp:LOGIN_SUCCESS Embedded → Host undefined Emitted after successful authentication. The session is now active.
fp:LOGOUT_SUCCESS Embedded → Host undefined Emitted after logout is complete. The session has been terminated.
fp:SESSION_EXPIRING Embedded → Host { secondsLeft?: number } Emitted approximately 60 seconds before the JWT token expires. The host should refresh the session by sending a new fp:LOGIN message.
fp:ERROR Embedded → Host { code?: string; message: string; detail?: unknown } Emitted when an error occurs during authentication or session management.
fp:OPEN_EXTERNAL Embedded → Host { url: string } Emitted when the embedded app requests opening a URL in a new window/tab (e.g. new open banking connection, contract signature). The host should handle this by opening the URL.

Events Sent by Host App (Host → Embedded)

Event Direction Payload Description
fp:LOGIN Host → Embedded Base64URL-encoded canonical JSON payload Initiates authentication. Must include payload and signature fields in the envelope.
fp:LOGOUT Host → Embedded undefined Terminates the current session.
fp:SIMULATION Host → Embedded { enableToolbar: boolean } Enables or disables the simulation toolbar in the embedded app (development only).

Event Handling Example:

window.addEventListener("message", (event) => {
  // Always verify origin for security
  if (event.origin !== "https://my.flowpay.io") return;

  const envelope = event.data;
  if (!envelope || envelope.channel !== "flowpay-embedded") return;

  switch (envelope.event) {
    case "fp:READY":
      console.log("Flowpay is ready, can now send login");
      // Send login message here
      break;

    case "fp:LOGIN_SUCCESS":
      console.log("Login successful");
      break;

    case "fp:LOGOUT_SUCCESS":
      console.log("Logout complete");
      break;

    case "fp:SESSION_EXPIRING":
      const secondsLeft = envelope.payload?.secondsLeft;
      console.log(`Session expiring in ${secondsLeft} seconds`);
      // Refresh session by sending new fp:LOGIN
      refreshSession();
      break;

    case "fp:ERROR":
      const error = envelope.payload;
      console.error("Flowpay error:", error.message, error.code);
      break;

    case "fp:OPEN_EXTERNAL":
      const url = envelope.payload?.url;
      if (url) {
        window.open(url, "_blank");
      }
      break;
  }
});

Logout

The host application is responsible for explicitly terminating the user's session inside the embedded myFlowpay app. To log the user out, the host must send a fp:LOGOUT message to the iframe using postMessage. After receiving this event, the embedded myFlowpay app clears the active session and returns to its initial unauthenticated state, then emits fp:LOGOUT_SUCCESS to confirm.

// Host App posting logout message
const iframe = document.getElementById("fp-embedded");
iframe.contentWindow.postMessage(
  {
    channel: "flowpay-embedded",
    version: "1.0",
    event: "fp:LOGOUT",
  },
  "https://my.flowpay.io"
);

The embedded app will respond with fp:LOGOUT_SUCCESS when the logout is complete.

External URL Handling

The embedded myFlowpay application may need to open external URLs in certain scenarios (e.g. new open banking connection, contract signature). When this occurs, the embedded app emits a fp:OPEN_EXTERNAL event with the URL to open.

The host application should listen for this event and open the URL in a new window or tab:

window.addEventListener("message", (event) => {
  if (event.origin !== "https://my.flowpay.io") return;

  const envelope = event.data;

  if (envelope.event === "fp:OPEN_EXTERNAL") {
    const url = envelope.payload?.url;
    if (url) {
      window.open(url, "_blank", "noopener,noreferrer");
    }
  }
});

Security Considerations:

  • Always verify the event.origin before processing messages
  • Validate that the URL is from a trusted domain if needed

Handling of session expiration

The JWT generated by myFlowpay is valid for 60 minutes. The embedded app emits fp:SESSION_EXPIRING approximately 60 seconds before expiry to allow the host application to refresh the session.

Session Refresh Process:

  1. When fp:SESSION_EXPIRING is received, the host should prepare a new login payload.
  2. The host should update the createdAt timestamp to the current time.
  3. The host should generate a new signature for the updated payload.
  4. The host should send a new fp:LOGIN message with the refreshed payload and signature.

If the host fails to respond with a valid fp:LOGIN message before the token expires, the session will be terminated and the user will need to re-authenticate.

Example Session Refresh:

let launchPayload = {
  country: "CZ",
  merchantId: "merchant-123",
  partnerCode: "SomePartner",
  userId: "user-999",
  regNum: "12345678",
  email: "user@example.com",
  phone: "+420123456789",
};

window.addEventListener("message", async (event) => {
  if (event.origin !== "https://my.flowpay.io") return;

  const envelope = event.data;
  if (!envelope || envelope.channel !== "flowpay-embedded") return;

  switch (envelope.event) {
    case "fp:SESSION_EXPIRING":
      // Refresh session by updating createdAt and sending new LOGIN
      const refreshedPayload = {
        ...launchPayload,
        createdAt: new Date().toISOString(),
      };

      // Canonicalize, encode, and sign the refreshed payload
      // Note: If using the SDK, session refresh is handled automatically.
      // For manual implementation, you need to implement payload canonicalization
      // and signature generation. See [Implementation with SDK](secure-embed-sdk.md) and [Authentication](secure-embed-auth.md) for reference.
      const { payload: encodedPayload, signature } = await createSignedLogin(
        refreshedPayload
      );

      // Send refreshed login
      const iframe = document.getElementById("fp-embedded");
      iframe.contentWindow.postMessage(
        {
          channel: "flowpay-embedded",
          version: "1.0",
          event: "fp:LOGIN",
          payload: encodedPayload,
          signature: signature,
          meta: {
            sentAt: new Date().toISOString(),
            reason: "refresh",
          },
        },
        "https://my.flowpay.io"
      );
      break;
  }
});

Note: The SDK libraries (@flowpay-io/embed-core, @flowpay-io/embed-react) handle session refresh automatically when configured with a signature provider. If signatureProvider is not configured, you must handle fp:SESSION_EXPIRING events manually as shown in the example above.

Error Handling

The embedded myFlowpay application performs strict validation of all authentication messages received from the host. If any validation step fails, the embedded app will not proceed with session initialization and will instead display an error message to the end user. The host application is not required to take further action unless it chooses to retry the login flow.

The following conditions result in an error being shown inside the embedded iframe:

  • Invalid or malformed signature

    The provided signature does not match the canonicalized payload when verified with the partner's shared secret.

  • Expired signature

    The createdAt timestamp in the payload differs by more than 5 minutes from server time.

  • Malformed payload

    The payload does not follow the required JSON structure, contains unsupported fields, or violates character constraints.

  • Unknown or unauthorized partner identifier

    The value of partner does not correspond to an active partner onboarding record.

  • Missing required fields

    One or more mandatory fields (e.g., merchantId, userId, country, createdAt) are missing.

  • Invalid tenant structure

    A tenant object is missing an id, contains duplicate IDs, or includes characters outside the allowed identifier set.

  • Internal activation or identity provisioning failure

    Any backend failure during service activation, tenant linking, or user identity creation prevents login and results in an error screen.

When an error occurs, the embedded app:

  1. Displays a clear error message within the iframe (e.g., "Authentication failed. Please try again or contact support.").
  2. Stops further initialization and does not issue or store a JWT token.
  3. Waits for a new valid fp:LOGIN message from the host if the host chooses to retry.

Errors are contained inside the iframe and do not affect the host page or its navigation flow.

For a signature computation example, see Authentication.