# Verification Flows Source: https://docs.fortepayments.io/access_levels_flows Preview UI flows and identity requirements for access levels 1 through 3 in the Forte Compliance widget. # Forte Compliance Widget – Product Guide This guide helps developers quickly visualize how the Forte Compliance widget behaves at different access levels. Each level unlocks a new set of contact details, identity requirements, and UI steps. ## Access Level 1 ### Identity Requirements * Email address verification * Wallet screening ### UI Screens 1. Email address collection 2. Email OTP verification > 🧪 Use the OTP code sent to your email in sandbox mode to complete email verification. ## Access Level 2 ### Identity Requirements * Full name * Verified phone number ### UI Screens 1. Requirements preview 2. Full name 3. Phone input & verification (SMS OTP) > 🧪 Use the OTP `123456` in sandbox mode to bypass Phone SMS OTP verification. 4. Success / Pending / Rejected ## Access Level 3 ### Identity Requirements * Full name * Verified phone number The above identity steps only apply if the user has not completed Level 2 verification. * Residential address * Government-issued ID * Live selfie ### UI Screens 1. Requirements preview 2. Full name 3. Residential address 4. Phone input & verification (SMS OTP) > 🧪 Use the OTP `123456` in sandbox mode to bypass Phone SMS OTP verification. 5. ID document verification & live selfie > **QR code for verification flow link** > > Users can scan the QR code to prompt user flow that captures their ID photos and live selfie via their mobile devices 6. Success / Pending / Rejected *** *** # Product Overview Source: https://docs.fortepayments.io/access_levels_guide Preview Forte Compliance features and access levels. # Forte Compliance – Overview This guide helps developers understand how the Forte Compliance Widget works across multiple access levels. Each level introduces new contact details and identity requirements and corresponding UI steps, ensuring verification without compromising user experience. ### Access Level Flow Architecture Forte implements a **graduated access level model**, where each level builds upon the previous one. > Example: If a user is prompted for Level 3 verification but has not completed Level 2, the widget will automatically walk them through all Level 2 and Level 3 requirements in sequence. This progressive structure removes the need for custom logic or separate API calls at each step, simplifying integration for developers. ### Session Resumption The Forte on-chain compliance widget is state-aware and supports **resumable flows**. If a user drops off mid-session, you can re-initiate the flow with the same wallet and level, and the widget will resume from the last completed checkpoint. To resume: * Use the same `wallet.address` and `level` * Call the [Initiate Access Level Verification Request API](/api-reference/endpoint/initiate-kyc-request) * Embed the returned `widget_data` again ### **On-Chain Compliance Levels** Forte Compliance is **interoperable** with the Forte Rules Engine to publish a wallet’s access level as an **on-chain access signal**. This allows developers and smart contracts to enforce rules based on a wallet’s access level status, without accessing sensitive off-chain data or relying on external APIs. While the Forte Compliance widget and API can operate independently of Rules Engine, publishing access levels on-chain makes them interoperable with on-chain protocols, decentralized applications, token-gated experiences, etc. **How It Works** 1. A user completes an Access Level session via the Forte widget. 2. Forte and its partners verify user data and assign an access level (e.g., Level 2) to the associated wallet. 3. The assigned level is published to a dedicated **on-chain compliance contract** using the Rules Engine policy execution layer. 4. Smart contracts can query this registry to determine whether a wallet meets the minimum access level for gated actions. Forte and its Regulated Partners will complete standard compliance checks before assigning a level, in order to ensure that eligible users are able to proceed while maintaining regulatory and AML compliance. **Example Use Cases** * **Smart Contract Access Control**: Permit mints, purchases, or staking only for wallets that have completed Level 2+ verification. * **Gameplay Unlocks**: Enable reward claiming, in-game asset upgrades, or PvP matchmaking based on verified compliance level. **Notes:** * Access levels committed on-chain via the Forte Compliance \<> [Forte Rules Engine](https://docs.forterulesengine.io) integration do not require any custom policy to function out-of-the-box. * If Forte Compliance widget is integrated, verification and on-chain access level updates are handled via Forte Compliance. * No user PII is exposed on-chain; only the wallet address and corresponding level are stored. ## Next Steps Preview widget verification flows # Access Control Level Contract Source: https://docs.fortepayments.io/acl_contract Overview of the on-chain Access Control Level (ACL) smart contract. The Access Control Level Contract is an on-chain component which can be used to manage user permissions based on compliance status. It enables your application to determine whether a user has passed specific verification steps, by checking against the on-chain access levels assigned through the Forte Compliance widget flow.\ \ This contract is especially useful for enforcing compliance logic directly within your dApp or smart contracts—such as allowing verified users to access payment flows. ## Deployment Details > Network: Base Sepolia > > Contract Address: `Contract Address will be provided directly by Forte team` > > Status: Verified & Published **Who can set Access Levels?** Only the Forte Compliance widget is authorized to set or remove access levels. When a user completes a required compliance check, the system updates their access level on-chain using this contract. This creates a transparent, verifiable source of truth that can be queried by any smart contract or application. ## Retrieving a User’s Access Level To check a user’s access level, call the `getAccessLevel` function, passing in their wallet address. This is a read-only function that can be queried on-chain or from your dApp's frontend. > *function getAccessLevel(address \_account) public view returns (uint256)* ### Expected Output This function will return a uint8 value: * `0`: Indicates that the user has not been verified. * `1-3`: Indicates the Access Level the user has reached through the Forte Compliance widget. You can integrate this function into your dApp frontend using [ethers.js](http://ethers.js), or [web3.js](http://web3.js)  to dynamically allow/disallow actions based on access level. ### ABI Definition ```javascript theme={null} { "type": "function", "name": "getAccessLevel", "inputs": [ { "name": "_account", "type": "address", "internalType": "address" } ], "outputs": [ { "name": "", "type": "uint256", "internalType": "uint256" } ], "stateMutability": "view" } ``` # Request Access Token Source: https://docs.fortepayments.io/api-reference/endpoint/authentication api-reference/openapi-auth.json POST /auth/v1/oauth2/token Use your `client_id` and `client_secret` to request an access token to authorize your API requests. # Create Payment Intent Source: https://docs.fortepayments.io/api-reference/endpoint/create-payment-intent api-reference/openapi-payments.json POST /payments/v2/intent Create a payment intent for a specific transaction type. # Create Redirect Intent Source: https://docs.fortepayments.io/api-reference/endpoint/create-redirect-intent api-reference/openapi-payments.json POST /payments/v2/intent/redirect Create a payment intent for the customer to be redirected on hosted UI and make a purchase there. # Get Payment Statuses Source: https://docs.fortepayments.io/api-reference/endpoint/get-payment-statuses api-reference/openapi-payments.json POST /payments/v1/payments/statuses Request statuses in batch for payments by their intent ids. # Create KYC Intent Source: https://docs.fortepayments.io/api-reference/endpoint/initiate-kyc-request api-reference/openapi-payments.json POST /compliance/v2/kyc Request a KYC session for a specific action. # Create Subscription Source: https://docs.fortepayments.io/api-reference/endpoint/notifications/create-subscription api-reference/openapi-notifications.json POST /notifications/v1/subscriptions Creates and returns a new webhook subscription. # Delete Subscription Source: https://docs.fortepayments.io/api-reference/endpoint/notifications/delete-subscription api-reference/openapi-notifications.json DELETE /notifications/v1/subscriptions/{id} Delete a webhook subscription with specific ID if it is currently disabled. This action is irreversible. # Disable Subscription Source: https://docs.fortepayments.io/api-reference/endpoint/notifications/disable-subscription api-reference/openapi-notifications.json POST /notifications/v1/subscriptions/{id}/disable Disable a webhook subscription with specific ID if it is currently enabled. # Enable Subscription Source: https://docs.fortepayments.io/api-reference/endpoint/notifications/enable-subscription api-reference/openapi-notifications.json POST /notifications/v1/subscriptions/{id}/enable Enable a webhook subscription with specific ID if it is currently disabled. # Get Subscriptions Source: https://docs.fortepayments.io/api-reference/endpoint/notifications/get-subscriptions api-reference/openapi-notifications.json GET /notifications/v1/subscriptions Get webhook subscription(s) belonging to the merchant. # Replay Webhook Source: https://docs.fortepayments.io/api-reference/endpoint/notifications/replay-webhook api-reference/openapi-notifications.json POST /notifications/v1/webhooks/replay/{webhook_id} Replay webhook with the given id, given linked subscription is still enabled. # Replay Webhooks Source: https://docs.fortepayments.io/api-reference/endpoint/notifications/replay-webhooks api-reference/openapi-notifications.json POST /notifications/v1/webhooks/replay Replay all webhooks sent out in time range, given linked subscription is still enabled. # Test Subscription Source: https://docs.fortepayments.io/api-reference/endpoint/notifications/test-subscription api-reference/openapi-notifications.json POST /notifications/v1/subscriptions/{id}/test Post a test webhook to the webhook subscription with specific ID. # Update Subscription Source: https://docs.fortepayments.io/api-reference/endpoint/notifications/update-subscription api-reference/openapi-notifications.json PATCH /notifications/v1/subscriptions/{id} Update a webhook subscription with specific ID. # Get Widget Manifest Source: https://docs.fortepayments.io/api-reference/endpoint/widget-manifest api-reference/openapi-payments.json GET /payments/v1/widget/manifest Response payload includes widget version and integrity digest (SHA-384) # Compliance Events Source: https://docs.fortepayments.io/compliance/compliance-events Client-side events emitted by the Forte Compliance widget. Use these to update your UI after verification completes or the widget is closed. The Forte Compliance widget emits client-side `CustomEvent`s that you can listen to on your page. Use these to handle post-verification UX — such as showing a confirmation message, unlocking a feature, or re-prompting the user if they abandoned the flow. Client-side events confirm user action in the widget UI — not on-chain state. For authoritative access level confirmation, query the [ACL contract](/acl_contract) directly. *** ## Event Types Emitted once the widget script has loaded and the widget is ready to be initialized. A common pattern is to wrap the initialization call inside this listener: ```javascript theme={null} window.addEventListener('FortePaymentsWidgetLoaded', () => { window.initFortePaymentsWidget({ containerId: 'forte-payments-widget-container', data }); }); ``` Emitted when the user closes the widget from the **success screen** after completing verification for the requested access level. Use this event to unlock features, display a confirmation message, or refresh on-chain state in your UI. Emitted when the user closes the widget from the **failure screen** after an unsuccessful verification attempt. Use this event to display a fallback message or prompt the user to try again later. Emitted when the user closes the widget **before completing verification** — from any screen that is not a terminal success or failure screen. Verification flows are resumable. Previously provided information is retained — the user will not need to re-enter it when they re-open the widget. *** ## Event Listener Snippet Add event listeners **before** the script that loads the Forte widget. ```javascript theme={null} window.addEventListener('FortePaymentsWidgetLoaded', () => { window.initFortePaymentsWidget({ containerId: 'forte-payments-widget-container', data }); }); window.addEventListener('ForteKycSuccess', function(event) { console.log('Verification complete'); // e.g. unlock feature, show "Access Granted" toast }); window.addEventListener('ForteKycFailure', function(event) { console.log('Verification failed'); // e.g. display retry prompt }); window.addEventListener('FortePaymentsWidgetClosed', function(event) { console.log('Widget closed before completion'); // e.g. re-show the verification prompt }); ``` *** ## Authoritative Access Level Confirmation Client-side events are UI signals, not on-chain state. Before gating features or granting access, query the ACL contract directly to confirm the wallet's verified level. Query on-chain access levels via the Forte ACL smart contract # Create KYC Intent Source: https://docs.fortepayments.io/compliance/initiate-kyc-request Initiate an identity verification request using the Forte Compliance widget. Once you have an [access token](/obtain-access-token), create a KYC intent for the target verification level. Forte returns the `widget_data` needed to initialize the Compliance widget. ## Endpoint ``` POST /compliance/v2/kyc Authorization: Bearer {{access_token}} ``` ## Request Body Pass the desired `level` (1–3) and the user's wallet. For a breakdown of what each level requires, see [Verification Flows](/access_levels_flows). ```json theme={null} { "action": { "type": "OCC_RULES_ENGINE", "level": 3 }, "customer": { "wallet": { "blockchain": "base_sepolia", "address": "0xF84D68F29664F907eE9d03E534105b7aA6045A" }, "external_id": "0xF84D68F29664F907eE9d03E534105b7aA6045A" } } ``` ### Field Reference | Field | Required | Description | | ---------------------------- | ----------- | ------------------------------------------- | | `action.type` | Yes | Always `OCC_RULES_ENGINE` | | `action.level` | Yes | Target access level: `1`, `2`, or `3` | | `customer.wallet.blockchain` | Yes | Blockchain identifier (e.g. `base_sepolia`) | | `customer.wallet.address` | Yes | User's wallet address | | `customer.external_id` | Recommended | Your internal identifier for the user | If a user needs Level 3 but has only completed Level 1, the widget automatically walks them through all outstanding requirements in sequence — no additional API calls are needed. ## Response ```json theme={null} { "data": { "flow": "KYC_OCC", "error_code": null, "widget_data": "eyJhY2Nlc3NfdG9rZW4i..." } } ``` Pass the full `data` object to `initFortePaymentsWidget` — see [Integrate Widget](/integrate-widget). ## Next Steps Embed and initialize the Forte widget with the response data # 🚀 Get Started Source: https://docs.fortepayments.io/index Integrate Forte Payments and Forte Compliance into your application. Forte provides two complementary SDKs for digital asset platforms - payments and compliance - each available as a fully-managed drop-in widget. ## Forte Payments Accept fiat and crypto payments for Virtual Digital Goods. Built-in AML screening, 3DS, crypto wallet support, and graduated KYC - no sensitive data touches your servers. Transaction types and integration overview Start integrating in three steps ## Forte Compliance Trigger graduated identity verification for your users - from email verification to full document KYC - using a drop-in widget. Access levels are optionally published on-chain. Access levels, rules engine, and on-chain signals Preview widget UI screens for each level *** Full endpoint reference for all Forte APIs Define and enforce on-chain compliance rules # Integrate Widget Source: https://docs.fortepayments.io/integrate-widget Embed and initialize the Forte widget in your frontend. This step is the same for both Forte Payments and Forte Compliance. After receiving a `data` object from either a [payment intent](/payments/initiate-payment-request) or a [KYC request](/compliance/initiate-kyc-request), embed and initialize the widget in your page. *** ## Step 1 — Embed the Widget Script Add a container `
` and a ` ``` | Environment | CDN URL | | ----------- | -------------------------------------------------------------- | | Sandbox | `https://payments.sandbox.lemmax.com/forte-payments-widget.js` | | Production | `https://payments.prod.lemmax.com/forte-payments-widget.js` | ### SRI Compliance (Recommended) If your application enforces a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) with Subresource Integrity, fetch the versioned filename and integrity digest from the manifest endpoint first. **Fetch the manifest** ```text theme={null} GET /payments/v1/widget/manifest Authorization: Bearer {{access_token}} ``` Response: ```json theme={null} { "data": { "file_name": "forte-payments-widget-6.6.2.js", "integrity": { "type": "sha384", "digest": "sha384-+YZhTk4vMk5059Z1nwH7wn5uWhnILu7OGOnyxxV86ofK0WA0rpDci1dcxRjJdMe+" } } } ``` **Embed with versioned filename and integrity attribute** Use `data.file_name` to construct the `src` URL, and `data.integrity.digest` as the `integrity` attribute on the ` ``` Fetch the manifest on every new widget session. This ensures the `integrity` digest matches the version currently being served from the CDN. *** ## Step 2 — Initialize the Widget After embedding the widget script, initialize it by passing the full `data` object from your intent response. Add this ` ``` Always use a fresh `data` object from your most recent intent API response. Do not cache or reuse previous values. The widget session expires after **30 minutes** of inactivity. If the session expires, re-initiate the intent and re-initialize the widget. *** *** ## Next Steps Handle client-side events from the Payments widget Handle client-side events from the Compliance widget # Introduction Source: https://docs.fortepayments.io/introduction Hero Dark # Welcome to Forte Compliance Built for the complexities of web3 and gaming, Forte Compliance helps you integrate seamless, dynamic access requirements into your product using a developer-first API and flexible widget. Whether you’re launching an NFT drop, or onboarding wallets into your ecosystem Forte Compliance gives you the tools to determine the appropriate level of verification of contact details and identity required for your specific use case, enabling you to quickly build secure verification flows—without the need to create custom compliance logic or handle any sensitive data yourself. ## Why Use Forte Compliance? * **Instant on-chain compliance Widget** Drop-in, fully managed component for email verification, document checks, and progressive identity verification requirements. Embed in any frontend with a single script. * **No need to process sensitive data yourself** Get the comfort of engaging with verified users without having to process sensitive data yourself or worry about compliance with data protection laws. * **Dynamic Verification** Trigger access-level flows at the right time, with the right scope. Forte supports Level 1, 2, and 3 flows based on thresholds, regions, and asset types. * **Rules Engine Integration** Define custom compliance logic using Forte’s on-chain Rules Engine. Update flows without changing your backend code. * **Fast Time-to-Asset** Users verify only once and proceed instantly to buy tokens or NFTs. * **Managed Compliance Partners** Offset your data protection compliance and operational risk by relying on Forte’s professional partners every step of the way. ## Core Components * **Access Token Auth** – Standard OAuth2 authentication for secure integration * **Access Level Request API** – Simple POST call to trigger Forte’s access level verification logic * **Forte Compliance Widget** – A fully-managed, responsive widget that renders verification flows dynamically * **Rules Engine** – Use chain-based logic to enforce preconditions or signal outcomes ## How It All Connects Integration Guide for **Forte Compliance SDK** that unlocks the power to effortlessly create trust in your ecosystem by setting  level-based access requirements for your platform’s end-users - all while completely eliminating the need to handle any sensitive user data yourself. Integration Guide to set up compliance widget Learn more about Forte Compliance Preview widget user experience API endpoints for compliance integration ## Rules Engine Integration Guide for **Forte Rules Engine** that lets you define powerful, composable on-chain policies into your smart contracts. Define and enforce on-chain rules for compliance, risk management, economic safeguards, or app-specific logic with the Forte Rules Engine # Obtain Access Token Source: https://docs.fortepayments.io/obtain-access-token Authenticate with Forte using OAuth2 client credentials. Required before creating a payment intent or initiating a KYC request. All Forte API requests require a short-lived access token. Obtain one by calling the auth endpoint with your client credentials. Your `client_id` and `client_secret` are provided by your account manager. Store your `client_secret` securely on your backend — never expose it to the client. | Environment | Host URL | | ----------- | ---------------------------------------- | | Sandbox | `https://sandbox-api.sandbox.lemmax.com` | | Production | `https://api.prod.lemmax.com` | ## Endpoint ```text theme={null} POST /auth/v1/oauth2/token ``` ## Request ```json theme={null} { "client_id": "{{client_id}}", "client_secret": "{{client_secret}}" } ``` ## Response ```json theme={null} { "data": { "access_token": "eyJraWQi...", "expires_in": 3600, "token_type": "Bearer" } } ``` Include the token in the `Authorization` header of all subsequent requests: ```text theme={null} Authorization: Bearer {{access_token}} ``` Tokens are valid for **1 hour**. Implement token refresh in your backend to avoid interrupting active sessions. *** ## Next Steps Initiate a VDG purchase session Initiate an identity verification flow # Create Payment Intent Source: https://docs.fortepayments.io/payments/initiate-payment-request Initiate a VDG (Virtual Digital Goods) payment session using the Forte Payments widget or redirect mode. Once you have an [access token](/obtain-access-token), create a payment intent. Forte returns the data needed to initialize the payment widget or redirect the user to a hosted payment page. *** ## Widget Mode ### Endpoint Buyers are purchasing a non-tokenized (off-chain) asset that will be delivered by you (after receiving confirmation via Forte Webhook), directly into their game account. ```text theme={null} POST /payments/v2/intent Authorization: Bearer {{access_token}} ``` ### Request Body You must include either `buyer.email` or `buyer.wallet` (or both). Use the same identifier format consistently for a given user across all payment sessions. ```json theme={null} { "transaction_type": "BUY_VDA", "buyer": { "email": "user@example.com", "id": "your-internal-user-id" }, "currency": "USD", "idempotency_key": "unique-string-per-attempt", "reference": "your-internal-transaction-id", "items": [ { "id": "item-id-in-your-system", "title": "1,000 Gold Coins", "description": "Bundle of Gold", "amount": "9.99", "image_url": "https://yourgame.com/items/gold-coins.png" } ] } ``` Include a unique `idempotency_key` per payment attempt to prevent duplicate processing if a request is retried. The optional `reference` field accepts a string up to 100 characters (a UUID works well). Pass your own transaction or order ID here and Forte will echo it back in all webhooks for that payment, making reconciliation straightforward without needing to map Forte's `payment_intent_id` to your own records. ### Response A `201` response indicates the intent was created successfully. ```json theme={null} { "data": { "flow": "PAYMENT", "error_code": null, "payment_intent_id": "440b0e4e-ae70-4886-ad6b-1521cc22cbca", "widget_data": "eyJhY2Nlc3NfdG9rZW4i...", "notes": [] } } ``` **Handle both status codes** | Status | Meaning | | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `201` | Intent created. Use `payment_intent_id` to track webhooks. | | `200` | Request succeeded but no intent was created (e.g. user is banned or sanctioned). Initialize the widget anyway — it will display the appropriate message. No `payment_intent_id` is returned. | Pass the full `data` object to `initFortePaymentsWidget` — see [Integrate Widget](/integrate-widget). *** ## Redirect Mode Use redirect mode to send users to a Forte-hosted payment page instead of embedding the widget directly. ### Endpoint ```text theme={null} POST /payments/v2/intent/redirect Authorization: Bearer {{access_token}} ``` The request body is identical to widget mode above. ### Response ```json theme={null} { "data": { "redirect_url": "https://payments.forte.io/0f7660c1-a106-481f-acf9-1848e98a7230?redirect_token=eyJhbGci..." } } ``` Redirect the user's browser to the `redirect_url`. The redirect URL is valid for **30 seconds** and can only be accessed **once**. If the user revisits the same URL, they will see a `401` authentication error. Client-side widget events are not available in redirect mode. Use [webhooks](/payments/webhooks) to track payment status in both modes. *** ## Error Codes If `error_code` is non-null in the response, the widget will display the appropriate message to the user automatically. Common codes: | Code | Meaning | | ------- | ------------------------------------------------------------- | | `ERR01` | Customer is banned | | `ERR03` | Request originates from a sanctioned country | | `ERR06` | Customer must complete KYC — widget will display the KYC gate | | `ERR15` | Customer's wallet address is flagged (KYW check failed) | *** ## Next Steps Embed and initialize the Forte Payments widget with the response data # Introduction Source: https://docs.fortepayments.io/payments/introduction Forte Payments enables gaming and digital asset platforms to accept fiat and crypto payments for Virtual Digital Goods - with built-in compliance, no sensitive data on your servers. ## Overview Forte Payments is a regulatory-compliant payment solution for gaming and digital asset platforms. Your users can purchase Virtual Digital Goods using credit card or crypto with built-in AML screening, 3D Secure, graduated KYC enforcement, and fraud protection - all through a fully-managed drop-in widget. Your backend creates a payment intent. Your frontend embeds the widget. Forte handles everything in between. ## What are Virtual Digital Goods? Virtual Digital Goods (VDG) are off-chain items delivered within your game or application that include in-game currency, cosmetics, power-ups, or any item stored in your own database rather than on a blockchain. With the `BUY_VDA` transaction type, your user pays via the Forte Payments widget, and you deliver the item after receiving a `payment_approved` webhook. The API uses the field name `BUY_VDA` (Virtual Digital Asset). VDA and VDG are used interchangeably in this documentation. ## How It Works Call `POST /payments/v2/intent` with the item details and buyer information. Forte returns a `widget_data` object. Embed the Forte Payments widget and initialize it with `widget_data`. The widget handles the full payment UI — card entry, 3DS, crypto wallet connection, and KYC if required. Once payment settles, Forte sends a signed webhook to your endpoint. Deliver the item to the user on `payment_approved`. ## Integration Modes | Mode | Description | Best for | | ------------ | ------------------------------------------------- | ----------------------------------- | | **Widget** | Widget is embedded directly in your site | Seamless in-app purchase experience | | **Redirect** | User is redirected to a Forte-hosted payment page | Simpler frontend integration | ## Key Features * **Card payments** — Visa and Mastercard, with 3D Secure support * **Crypto payments** — Users pay from their connected Web3 wallet * **Built-in compliance** — AML screening, geo-blocking, and graduated KYC on high-value transactions * **Webhooks** — Signed payment status events delivered to your backend * **Saved Payment Instruments** — Seamless payment flow with saved payment methods ## Next Steps Start integrating Forte Payments Test cards, OTP codes, and sandbox details # Payment Events Source: https://docs.fortepayments.io/payments/payment-events Client-side events emitted by the Forte Payments widget. Use these to update your UI after a payment completes or the widget is closed. The Forte Payments widget emits client-side `CustomEvent`s that you can listen to on your page. Use these to trigger post-payment UX — such as showing a success message, refreshing inventory, or dismissing a modal — without polling. Client-side events are available in **widget mode only**. They are not emitted in redirect mode. For authoritative payment confirmation in both modes, use [webhooks](/payments/webhooks). *** ## Event Types Emitted when the user exits the widget from the **success screen** after completing a VDG purchase — whether they press "Done" or click the close button. **When to act:** This is a UI signal that the user has seen the success confirmation. For authoritative delivery confirmation — especially before delivering a Virtual Digital Good — wait for the `payment_approved` [webhook](/payments/webhooks). Emitted when the user closes the widget from **any screen that is not the success screen** — including mid-flow abandonment, after a payment decline, or after session expiry. Use this event to handle clean-up UX, such as re-showing a "Buy" button or dismissing a modal. *** ## Event Listener Snippet Add event listeners **before** the script that loads the Forte widget. ```javascript theme={null} window.addEventListener('FortePaymentsBuyVdaSuccess', function(event) { console.log('Payment intent ID:', event.detail.paymentIntentId); // e.g. show success toast, refresh inventory UI }); window.addEventListener('FortePaymentsWidgetClosed', function(event) { console.log('Widget closed. Intent ID:', event.detail.paymentIntentId); // e.g. re-show the Buy button, dismiss modal }); ``` Each event's `detail` object includes the `paymentIntentId` associated with the session. *** ## Authoritative Payment Confirmation Client-side events confirm that the user closed the widget — not that the payment has settled. For authoritative confirmation before delivering a Virtual Digital Good, always rely on the server-side `payment_approved` webhook. Set up server-side webhook subscriptions for payment status events # Testing Source: https://docs.fortepayments.io/payments/testing Test cards, sandbox OTP codes, and environment details for Forte Payments. Use the sandbox environment to test your integration without processing real payments. ## Sandbox Environment | Resource | Value | | ------------ | -------------------------------------------------------------- | | Widget CDN | `https://payments.sandbox.lemmax.com/forte-payments-widget.js` | | API Base URL | Provided by your Forte account manager | *** ## Test Credit Cards Do not use these test cards in the production environment. | Card Number | Network | 3DS | Result | | ------------------ | ---------- | ------- | -------- | | `5204247750001471` | Mastercard | Non-3DS | Approved | | `5200000000001104` | Mastercard | 3DS-1 | Approved | | `4111111111111111` | Visa | Non-3DS | Approved | | `4005562231212123` | Visa | 3DS-1 | Declined | | `4000020000000000` | Visa | 3DS-1 | Approved | | `5424180000000171` | Mastercard | Non-3DS | Declined | ### Additional Card Fields | Field | Value | | -------------------------- | ---------------------------------------------- | | Name | Any name | | CVV | Any 3 digits (e.g. `100`) | | Expiry | Any future date in MM/YY format (e.g. `01/99`) | | 3DS password (3DS-1 cards) | `1234` | ### About 3D Secure 3D Secure (3DS) is an additional authentication layer for online card payments. It is activated for transactions from most countries outside the United States and Canada, and is mandatory within the EU under Strong Customer Authentication (SCA) regulations. Forte supports: * **Visa Secure** (3DS v1 and v2) * **Mastercard Identity Check** (3DS v1 and v2) For 3DS-1, users are redirected to a separate browser window to complete a one-time passcode challenge. For 3DS-2, authentication happens silently in the background when supported by the issuing bank. *** ## Email Verification OTP In sandbox mode, emails are not sent. Use the following code to bypass email verification prompts in the widget: **OTP code: `947293`** *** ## Payment Status Reference The `POST /payments/v1/payments/statuses` endpoint returns the following statuses: | Status | Meaning | | ---------- | ------------------------------------ | | `Created` | Payment session initiated | | `Approved` | Payment completed — deliver the item | | `Declined` | Payment failed — do not deliver | | `Expired` | Session timed out — do not deliver | *** ## Common Issues Verify that the user has completed the 3DS challenge. For 3DS-1 cards, the challenge appears in a browser pop-up. If the user's browser blocks pop-ups, they will not see the prompt and the payment will be declined. To allow pop-ups: * **Widget mode** — allow pop-ups for your hosting domain * **Redirect mode** — allow pop-ups for `payments.forte.io` Common causes: * **Insufficient gas** — Users who manually override gas parameters may see delayed or failed transactions. Advise users not to modify wallet-suggested gas values. * **User exited before approving** — After clicking "Pay", the user has **10 minutes** to approve the transfer in their wallet. Exiting or refreshing the page before approval will fail the transaction. * **Payment window expired** — The 10-minute window has passed. The user must restart the payment flow. If your site enforces a Content Security Policy, allowlist the following: | URL | CSP Directive | | --------------------- | ---------------------------------------- | | `*.lemmax.com` | `script-src`, `image-src` | | `*.forte.io` | `script-src`, `connect-src`, `frame-src` | | `*.instana.io` | `connect-src` | | `*.ipify.org` | `connect-src` | | `*.walletconnect.org` | `connect-src`, `frame-src` | | `*.web3modal.com` | `connect-src`, `image-src` | Also allowlist any wallet extension sources (e.g. MetaMask, Coinbase Wallet) for crypto payment flows. # Pay with Card Source: https://docs.fortepayments.io/payments/vdg-pay-with-card End-to-end widget flow for a Virtual Digital Good purchase using a credit or debit card. ## Flow / Screen Examples This scenario assumes the user chooses to pay with a credit or debit card. *** **Marketplace / Game** The user views items for sale in the marketplace or game. Clicking to purchase opens the Forte Payments widget. *** **Screen 1 — Email Verification** The user is presented with an email verification screen. They must verify that they own the email address associated with their marketplace or game account. If `email` is not passed in the payment intent request (and `wallet` is passed instead), the user will be prompted to enter and verify their email on their first payment. That email is then linked for all future sessions. *** **Screen 2 — OTP Entry** The user enters the verification code sent to their email to complete email verification. *** **Screen 3 — Purchase Summary** The user is presented with a purchase summary showing transaction details. They select **Pay with card** to continue. *** **Screen 4 — Card Entry** The user enters their credit or debit card details and billing information, then clicks **Continue**. *** **Screen 5 — Transaction Review** The user sees a complete breakdown of the transaction before confirming: * **Buying** — the item being purchased * **Sending to** — the destination account receiving the asset * **Paying with** — the selected payment method The user can click **Paying with** to switch to a different card before confirming. *** **Screen 6 — Confirmation** Once the payment is processed, the user sees a confirmation screen. The user also receives an email receipt. **Success** — Payment processed. The user is shown a success message and Order ID. **Failure** — Payment could not be processed. The user is shown a failure message. # Webhooks Source: https://docs.fortepayments.io/payments/webhooks Subscribe to server-side payment status events from Forte. Webhooks are the authoritative signal for delivering Virtual Digital Goods. Forte delivers signed webhook events to your backend endpoint whenever a payment status changes. For VDG purchases, deliver the item to the user only after receiving a `payment_approved` event. ## Setup Overview Obtain an [access token](/obtain-access-token) using your client credentials. Register your webhook endpoint with Forte via `POST /notifications/v1/subscriptions`. Forte will POST signed events to your endpoint. Verify the signature using your `signing_key` and respond with `200`. *** ## Managing Subscriptions ### Create a Subscription ```bash theme={null} POST /notifications/v1/subscriptions Authorization: Bearer {{access_token}} Content-Type: application/json { "endpoint": "https://yourdomain.com/webhooks/forte", "description": "Production payment events" } ``` Response: ```json theme={null} { "data": { "id": "c83ff48a-cd80-4f02-b63f-2155a27682cd", "endpoint": "https://yourdomain.com/webhooks/forte", "description": "Production payment events", "signing_key": "43dec7af-275d-4c5f-b502-c9772858daab", "status": "enabled", "version": "1" } } ``` Store the `signing_key` securely. It is used to verify the authenticity of all incoming webhook events. **Subscription limits** | Environment | Max enabled subscriptions | | --------------------- | ------------------------- | | Production | 1 | | Sandbox / Development | 3 | Attempting to enable a subscription beyond this limit returns `429`. ### List Subscriptions ```bash theme={null} GET /notifications/v1/subscriptions Authorization: Bearer {{access_token}} ``` ### Enable / Disable a Subscription ```bash theme={null} # Enable POST /notifications/v1/subscriptions/{id}/enable Authorization: Bearer {{access_token}} # Disable POST /notifications/v1/subscriptions/{id}/disable Authorization: Bearer {{access_token}} ``` ### Update a Subscription ```bash theme={null} PATCH /notifications/v1/subscriptions/{id} Authorization: Bearer {{access_token}} Content-Type: application/json { "endpoint": "https://yourdomain.com/webhooks/forte-v2", "description": "Updated description" } ``` ### Delete a Subscription ```bash theme={null} DELETE /notifications/v1/subscriptions/{id} Authorization: Bearer {{access_token}} ``` Subscription deletion is irreversible. *** ## Test a Subscription Send a test event to confirm your endpoint is reachable: ```bash theme={null} POST /notifications/v1/subscriptions/{id}/test Authorization: Bearer {{access_token}} ``` Returns `200` if your endpoint responds with `200`, otherwise `502`. *** ## Receiving Webhooks Your endpoint must respond with HTTP `200` for every webhook received. If Forte does not receive a `200`, it will **retry 3 times** before marking the webhook as failed. Subscriptions that repeatedly fail may be automatically disabled. ### Verifying Signatures Forte signs all webhooks with **HMAC-SHA256** using the `signing_key` returned when you created the subscription. The signature is included in the `X-Forte-Payments-Webhook-Signature` request header. Always verify this signature before processing any payload. ### Replaying Webhooks If your endpoint was unavailable during a period, replay all webhooks from that window: ```bash theme={null} POST /notifications/v1/webhooks/replay?start_timestamp={{utc_timestamp}}&end_timestamp={{utc_timestamp}} Authorization: Bearer {{access_token}} ``` *** ## Webhook Payloads ### Payment Status Flow ``` Created → Approved (deliver item) Created → Declined (do not deliver) Created → Expired (do not deliver) ``` `Approved`, `Declined`, and `Expired` are terminal states. No further events follow. *** ### Payment Created Sent when the user initiates a payment session. ```json theme={null} { "event_type": "payment_created", "timestamp": "1725481536", "version": 1, "event_payload": { "payment_intent_id": "eda5d152-b75c-4f44-bb5e-65308518def9", "payment_status": "Created", "payment_method": "credit", "amount": "2.99", "currency": "EUR", "item_title": "Bundle of Gold", "item_desc": "1,000 Gold Coins", "customer_id": "3bd8c4f5-d25e-4260-b401-c5ce4e8bcc9a" } } ``` ### Payment Approved Sent when payment settles. **Deliver the item to the user upon receiving this event.** ```json theme={null} { "event_type": "payment_approved", "timestamp": "1725481561", "version": 1, "event_payload": { "payment_intent_id": "eda5d152-b75c-4f44-bb5e-65308518def9", "payment_status": "Approved", "payment_method": "credit", "amount": "2.99", "currency": "EUR", "item_title": "Bundle of Gold", "item_desc": "1,000 Gold Coins", "customer_id": "3bd8c4f5-d25e-4260-b401-c5ce4e8bcc9a" } } ``` ### Payment Declined Sent when the card is declined or a crypto transaction fails. ```json theme={null} { "event_type": "payment_declined", "timestamp": "1725481601", "version": 1, "event_payload": { "payment_intent_id": "b3e6a9bb-54f8-4fb5-8732-c3b334ec1a39", "payment_status": "Declined", "payment_method": "credit", "amount": "2.99", "currency": "EUR", "item_title": "Bundle of Gold", "item_desc": "1,000 Gold Coins", "customer_id": "3bd8c4f5-d25e-4260-b401-c5ce4e8bcc9a" } } ``` If a card is declined before a 3DS challenge is presented, `payment_declined` fires immediately with no preceding `payment_created` event. ### Payment Expired Sent when the user is inactive for 30 minutes and the session times out. ```json theme={null} { "event_type": "payment_intent_expired", "timestamp": "1725481601", "version": 1, "event_payload": { "payment_intent_id": "afb168c8-b951-429f-9ccf-83bf3e03f6e3", "payment_status": "Expired", "payment_method": null, "amount": "2.99", "currency": "EUR", "item_title": "Bundle of Gold", "item_desc": "1,000 Gold Coins", "customer_id": "3bd8c4f5-d25e-4260-b401-c5ce4e8bcc9a" } } ```