{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-platform/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Glomo Flutter SDK v2","siteUrl":"https://docs.glomo.one","llmstxt":{"title":"Glomo developer documentation","description":"Cross-border payments APIs, organised by product. Each product section carries both the explanation of its objects and the job-shaped integration guides that set them up.","details":{"content":"How to use these docs:\n\n- Get started is the front door. It has one section per integration\n  (what you're building), each with the API calls in order and the guide that builds it, plus\n  how to get API keys.\n- The product sections - Payin, Payout, Multi-currency accounts,\n  Verification, Request for Information (RFI) - each hold two kinds of page.\n  Pages titled as a noun (\"Subscriptions\", \"Bank Transfer\") explain what\n  an object is. Pages titled as a job (\"Set up recurring subscriptions\",\n  \"Collect LRS remittances from India\") are complete integration flows\n  and live one level under the object they set up. Match a user's goal\n  against the job-shaped titles first.\n- Reports and Identity and Access Management are the two subjects with no\n  API counterpart. There are no endpoints for reports, users, roles or\n  invites; both pages document the merchant dashboard. A dashboard\n  procedure for something the API also does (a refund, a payout) is NOT\n  here - it stays in the product that owns it.\n- API reference is generated from OpenAPI and carries endpoint detail.\n- The OpenAPI description itself is published as a single self-contained\n  document at /openapi.yaml, and the same document as JSON at\n  /openapi.json. Every $ref is resolved into it, so one fetch is the\n  whole contract: 61 paths, 81 operations, 68 schemas - the same\n  endpoints the API reference pages render. Fetch that instead of\n  reading the 129 pages under /api-reference/.\n- Every page below, in full, is one document at /llms-full.txt. Fetch that\n  instead of the pages one by one. It leaves out the API reference;\n  /openapi.yaml is that.\n- Developer resources covers what every integration touches and no single\n  product owns: auth, errors, pagination, rate limits, webhooks,\n  versioning, supported countries, quotes, the MCP server for AI\n  agents, and the client SDKs.\n"},"sections":[{"title":"Get started","description":"The front door. The ordered API calls for each integration and the guide that delivers it, how to obtain API keys, and the platform glossary.","includeFiles":["**/get-started/**"]},{"title":"Payin","description":"Collecting money: customers, orders, checkout, payment links, payment methods (cards, bank transfer, pay via bank), refunds, disputes, purpose codes, fee models, custom fields, subscriptions, and resident India remittance under LRS. Includes the flows that set each up - bank transfers, subscriptions, LRS remittances, and server-to-server card payments.","includeFiles":["**/payin/**"]},{"title":"Payout","description":"Sending money: the payout life cycle, queued payouts, creating and cancelling a payout, rails, beneficiaries, purpose codes, and the action required state. Includes the flow that sends a payout over the API.","includeFiles":["**/payout/**"]},{"title":"Multi-currency accounts","description":"Holding money: balances, balance conversion, adding balance, and withdrawing balance.","includeFiles":["**/multi-currency-account/**"]},{"title":"Verification","description":"Proving who someone is and that an account is theirs: KYC, bank account validation, and third-party verification (TPV) with the flow that verifies a payer's bank account.","includeFiles":["**/verification/**"]},{"title":"Request for Information (RFI)","description":"Responding to a compliance request for information, per object - payment link, order, payment, payout - plus compliance reviews and RFIs raised on already-successful payments.","includeFiles":["**/request-for-information/**"]},{"title":"Nivo (Capital markets)","description":"Nivo, Glomo's capital markets suite (Nivo for AMCs, Nivo for Distributors, and overseas investing for brokers and wealth platforms). Mutual fund investments run as payment sessions: the fund, distributor, scheme, plan, ARN and folio roles; the distributor's relationship with a fund and what it fixes; and the flow that opens a session as a distributor.","includeFiles":["**/capital-markets/**"]},{"title":"Reports","description":"The downloadable and scheduled reports a merchant pulls from the dashboard for reconciliation, compliance and treasury: payment, subscription, settlement breakup, balance statement and KYC reports, plus scheduling one for recurring email delivery and customizing the fields and column order of a report. Dashboard only - there is no reports API.","includeFiles":["**/reports/**"]},{"title":"Identity and Access Management","description":"Administering who can use a merchant account: one set of credentials across multiple MIDs, switching between them without logging out, the Admin / Member / Sales Partner roles and what each may see and do, and inviting a new member. Dashboard only - there is no users, roles or invites API.","includeFiles":["**/access-management/**"]},{"title":"Settlement holidays","description":"The settlement holiday calendar for 2026 - the Indian and US bank and FX holidays on which payin, payout and settlement to the merchant's bank account may be delayed.","includeFiles":["**/settlement-holidays/**"]},{"title":"API reference","description":"Endpoint reference generated from the Glomo OpenAPI description.","includeFiles":["**/api-reference/**"]},{"title":"Developer resources","description":"What every integration touches and no single product owns: auth, errors, pagination, rate limits, webhooks, versioning, API validations, security, supported countries, quotes, connecting an AI agent to the Glomo MCP server, and the client SDKs - React Native, Flutter, Android and the Unified Web SDK, with per-version references and changelogs. Reports and Identity and Access Management used to be listed here and are now their own sections.","includeFiles":["**/platform/**"]}]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"glomo-flutter-sdk-v2","__idx":0},"children":["Glomo Flutter SDK v2"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Official Flutter SDK for integrating Glomo payment checkout flows into your mobile applications."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Current version: ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["v2.2.0"]},". Upgrading from v2.1.x is a version bump and one optional prop - no API changed and no existing code needs to move. v2.2.0 adds ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#dark-mode"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["appColorScheme"]}]},". If your app or your network restricts outbound hosts, there is one allowlist change to make - see the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/migration#v21x-to-v220"},"children":["Migration Guide"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Upgrading from v1.11.x? See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/migration"},"children":["Migration Guide"]},". Every integration needs at least one change, and the compiler points at it."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Full ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/changelog"},"children":["Changelog"]}," is also available."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"prerequisites","__idx":1},"children":["Prerequisites"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before using this SDK, you need:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["API credentials (Public Key) from your Glomo dashboard"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["An order ID created via the Glomo API (or a subscription ID for subscription payments)"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"system-requirements","__idx":2},"children":["System Requirements"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Requirement"},"children":["Requirement"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Version"},"children":["Version"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Flutter"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[">= 3.7.0"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Dart SDK"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[">= 3.0.0"]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"installation","__idx":3},"children":["Installation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Add the dependency to your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pubspec.yaml"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"dependencies:\n  flutter:\n    sdk: flutter\n  glomopay_sdk: ^2.2.0\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Or install via the CLI:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"flutter pub add glomopay_sdk\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Then import the package:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"import 'package:glomopay_sdk/glomopay_sdk.dart';\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"platform-setup","__idx":4},"children":["Platform Setup"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"android","__idx":5},"children":["Android"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Add the following permissions to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["android/app/src/main/AndroidManifest.xml"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"xml","header":{"controls":{"copy":{}}},"source":"<uses-permission android:name=\"android.permission.INTERNET\"/>\n<uses-permission android:name=\"android.permission.CAMERA\" />\n<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" android:maxSdkVersion=\"32\" />\n<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" android:maxSdkVersion=\"29\" />\n","lang":"xml"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"ios","__idx":6},"children":["iOS"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Add the following keys to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ios/Runner/Info.plist"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"xml","header":{"controls":{"copy":{}}},"source":"<key>NSCameraUsageDescription</key>\n<string>This app requires access to the camera to upload documents required for payment verification.</string>\n<key>NSPhotoLibraryUsageDescription</key>\n<string>This app requires access to the photo library to select documents for payment verification.</string>\n","lang":"xml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Document downloads (",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#document-downloads"},"children":["below"]},") need no additional permission or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Info.plist"]}," key on either platform - the SDK writes only to the destination the user picks in the system save dialog."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"quick-start","__idx":7},"children":["Quick Start"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"import 'package:flutter/material.dart';\nimport 'package:glomopay_sdk/glomopay_sdk.dart';\n\nclass PaymentScreen extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Complete Payment')),\n      body: GlomoPayCheckout(\n        config: const GlomoPayConfig(\n          publicKey: 'test_pk_12345',\n          orderId: 'order_abc123',\n        ),\n        onPaymentSuccess: (GlomoPayPayload payload) {\n          // A payment the backend confirms succeeded. Verify\n          // payload.signature on your server before fulfilling.\n          print('Payment succeeded! Payment ID: ${payload.paymentId}');\n        },\n        onPaymentFailure: (GlomoPayPayload payload) {\n          // A payment the backend confirms failed - prompt for another card\n          // or bank. A checkout that could not load arrives on onSdkError or\n          // onConnectionError instead, not here.\n          print('Payment failed with Order ID: ${payload.orderId}');\n        },\n        onUserJourneyCompleted: (GlomoPayUserJourneyPayload journey) {\n          // The second happy path: the user submitted bank transfer details.\n          // A settlement may not have happened\n          // and there is no paymentId or signature, so\n          // reconcile journey.orderId on your backend rather than treating\n          // this as paid. Settlement is confirmed via webhooks.\n          print('Journey completed: ${journey.journeyType.value} '\n              'for order ${journey.orderId}');\n        },\n        onSdkError: (List<SdkError> errors) {\n          print('SDK Error: ${errors.first.message}');\n        },\n        onConnectionError: (ConnectionError error) {\n          print('Connection error: ${error.message}');\n        },\n        onPaymentTerminate: (TerminationSource source) {\n          // Fires when the checkout ends without a payment result: the user\n          // dismissed it, hit back, exited one of the SDK's error screens, or\n          // a connection error closed it. The SDK closes itself, so this is\n          // where your own cleanup goes - not the closing.\n          print('Checkout ended without a payment: $source');\n        },\n      ),\n    );\n  }\n}\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"subscriptions-checkout","__idx":8},"children":["Subscriptions Checkout"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To process subscription payments, pass a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["subscriptionId"]}," instead of an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["orderId"]}," in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GlomoPayConfig"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"GlomoPayCheckout(\n  config: const GlomoPayConfig(\n    publicKey: 'live_pk_abc123',\n    subscriptionId: 'sub_xyz789',\n  ),\n  onPaymentSuccess: (GlomoPayPayload payload) {\n    print('Subscription payment success: ${payload.paymentId}');\n  },\n  onPaymentFailure: (GlomoPayPayload payload) {\n    print('Subscription payment failed: ${payload.orderId}');\n  },\n  onUserJourneyCompleted: (GlomoPayUserJourneyPayload journey) {\n    // Never fires for subscriptions today, but the callback is required.\n  },\n  onSdkError: (List<SdkError> errors) {\n    errors.forEach((e) => print('${e.type}: ${e.message}'));\n  },\n  onConnectionError: (ConnectionError error) {\n    print('Connection error: ${error.message}');\n  },\n)\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["subscriptionId"]}," is provided:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The SDK skips order detection API calls"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["subscriptionId"]}," must start with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sub_"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["LRS-specific UI elements and flow logic are not applied"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Do not pass both ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["orderId"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["subscriptionId"]}," - the SDK will fire ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Existing order-based integrations remain backward compatible"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"asynchronous-payment-journeys","__idx":9},"children":["Asynchronous Payment Journeys"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Some checkout flows end without a confirmed payment. The user has done everything asked of them, but a settlement may not have happened yet payment confirmation happens async. These arrive through ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onUserJourneyCompleted"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Today there is exactly one such journey: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bankTransferSubmitted"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"onUserJourneyCompleted: (GlomoPayUserJourneyPayload journey) {\n  switch (journey.journeyType) {\n    case GlomoPayUserJourneyType.bankTransferSubmitted:\n      // The user submitted their transfer details. Mark the order as\n      // awaiting funds and reconcile it on your backend; settlement is\n      // confirmed by webhook, not by this callback.\n      print('Transfer reference: ${journey.transactionReference}');\n      break;\n  }\n},\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before v2.0.0, a submitted bank transfer was delivered to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentSuccess"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentId"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["signature"]}," both ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}," - a payment reported as received that your backend had nothing to verify against. It now reports here instead, and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["no longer reaches ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentSuccess"]}]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["One journey produces one callback. A submitted bank transfer is an ending in its own right: it reports through ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onUserJourneyCompleted"]}," and never also through ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentSuccess"]},", and a user who closes what remains of the checkout afterwards does not additionally arrive as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentTerminate"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"features","__idx":10},"children":["Features"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Glomo Payment Stack Support"]}," - handles standard checkout flows and overlay redirects (3DS, bank pages)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Robust Security"]}," - built-in jailbreak and root detection"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Self-contained error reporting"]}," - diagnostics run over the SDK's own HTTP client. No global Flutter or platform error handlers are installed and no native crash handler is touched, so your app's own crash reporting is unaffected and your error-tracking dependencies are unconstrained."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Comprehensive Error Handling"]}," - gracefully manages connection drops, DNS issues, HTTP errors, validation mistakes"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Native Support"]}," - full Android/iOS native features (camera, file pickers)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Document downloads"]}," - bank flows that offer a form or agreement download hand it to the system save dialog, so the user gets the file on their device. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.1.0"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Origin policy"]}," - the checkout WebView stays on Glomo's own pages, and payment results are accepted only from them. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.1.2"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Dark mode"]}," - ask the checkout page for a light or dark scheme, or let it follow your app's own theme. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.2.0"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Mock Mode"]}," - easy testing with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["test_"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mock_"]}," prefixed keys"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"api-reference","__idx":11},"children":["API Reference"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"glomopaycheckout-widget","__idx":12},"children":["GlomoPayCheckout Widget"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The checkout widget. Place it in your widget tree to render the payment checkout."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"GlomoPayCheckout(\n  config: config,\n  onPaymentSuccess: ...,\n  onPaymentFailure: ...,\n  onUserJourneyCompleted: ...,\n  onSdkError: ...,\n  onConnectionError: ...,\n)\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"props","__idx":13},"children":["Props"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Prop"},"children":["Prop"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Type"},"children":["Type"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Required"},"children":["Required"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["config"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GlomoPayConfig"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Configuration details for the checkout session"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentSuccess"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Function(GlomoPayPayload)"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Called when a payment the backend confirms succeeded"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentFailure"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Function(GlomoPayPayload)"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Called when the backend confirms a payment failed. Reports failed payment attempts only - not checkouts that could not load or dropped midway"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onUserJourneyCompleted"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Function(GlomoPayUserJourneyPayload)"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Called when an asynchronous journey ends the checkout without a confirmed payment - today only ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bankTransferSubmitted"]},". The payload has no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentId"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["signature"]},", so reconcile the order. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New and required in v2.0.0"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Function(List<SdkError>)"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Called when the SDK or the order could not proceed (validation, forbidden device, unusable order, unreachable backend)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onConnectionError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Function(ConnectionError)"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Called on internet drop, DNS failure, HTTP error, or page load timeout"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentTerminate"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Function(TerminationSource)?"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Called when the checkout ends without a payment result. A notification, not a handover - the SDK closes the checkout itself whether or not you supply this, and will not close twice if you also close from here"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onUserRefusedDevicePermissions"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Function()?"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Called when the user denies camera/storage permissions during file/document uploads or selfie verification requests"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onEvent"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Function(String, Map)?"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Deprecated in v2.0.0."]}," Carries internal debugging events that are subject to change and are not part of the integration contract. Scheduled for removal in a future major version"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["autoCloseOnConnectionError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bool"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Whether a critical connection error auto-closes the checkout and reports ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentTerminate(connectionError)"]},". The SDK pops its own route on this path. Does not apply to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ConnectionErrorType.timeout"]},". Default: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["controller"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GlomoPayController?"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Supply your own controller instead of letting the SDK create one"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["appColorScheme"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GlomoColorScheme"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The colour scheme to request from the checkout page: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auto"]}," (default), ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["light"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dark"]},". A request, not an instruction - see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#dark-mode"},"children":["Dark mode"]},". ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.2.0"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"glomopayconfig","__idx":14},"children":["GlomoPayConfig"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Property"},"children":["Property"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Type"},"children":["Type"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Default"},"children":["Default"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["publicKey"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["String"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["-"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your Glomo public key (must start with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["live_"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mock_"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["test_"]},")"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["orderId"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["String?"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unique tracking ID generated for the transaction on your server"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["subscriptionId"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["String?"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Subscription checkout ID; use instead of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["orderId"]}," for subscriptions"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Exactly one of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["orderId"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["subscriptionId"]}," must be provided. If both or neither are set, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," fires."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"glomopaypayload","__idx":15},"children":["GlomoPayPayload"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Delivered by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentSuccess"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentFailure"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"class GlomoPayPayload {\n  final String orderId;                     // System ID of the order\n  final String? paymentId;                  // Transaction reference, if generated\n  final String? signature;                  // Validation signature hash for backend verification\n  final Map<String, dynamic>? rawResponse;  // The page's message exactly as it arrived\n}\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Verify ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["signature"]}," on your backend before fulfilling. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["rawResponse"]}," carries the checkout page's own detail untouched, including the thinner shape the page emits for its own setup failures."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"glomopayuserjourneypayload","__idx":16},"children":["GlomoPayUserJourneyPayload"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Delivered by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onUserJourneyCompleted"]},". ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.0.0."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"class GlomoPayUserJourneyPayload {\n  final GlomoPayUserJourneyType journeyType;  // Which journey ended\n  final String orderId;                       // The order it belongs to. Always present\n  final String? senderAccountNumber;          // Reported by the page, when present\n  final String? transactionReference;         // Reported by the page, when present\n  final String? status;                       // The page's own status string, when present\n  final Map<String, dynamic>? rawResponse;    // The page's message exactly as it arrived\n}\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["There is deliberately no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentId"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["signature"]},". The user has submitted their transfer details and the money has not moved, so there is nothing to verify a payment against - reconcile the order on your backend instead."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"glomopayuserjourneytype","__idx":17},"children":["GlomoPayUserJourneyType"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Wire value"},"children":["Wire value"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bankTransferSubmitted"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bank_transfer_submitted"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The user submitted their bank transfer details. No payment is confirmed yet"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is the only member today. It is an enum rather than a bare string so you can switch on it exhaustively and so a second asynchronous flow can be added without changing the callback's shape."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"sdkerror","__idx":18},"children":["SdkError"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"class SdkError {\n  final SdkErrorType type;   // Error category\n  final String message;      // Human-readable description\n  final String? field;       // Field name (e.g. \"orderId\") causing validationError\n}\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"sdkerrortype","__idx":19},"children":["SdkErrorType"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validationError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Input validation failed"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["deviceForbidden"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Rooted or jailbroken device detected"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["networkError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Network-level error"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["unknown"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Uncategorized error"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field"]}," names what produced the error - ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"orderId\""]}," for a malformed order id, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"file.save\""]}," for a ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#document-downloads"},"children":["document download"]}," that failed. A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["file.save"]}," validation error is non-terminal: the checkout continues."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"connectionerror","__idx":20},"children":["ConnectionError"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"class ConnectionError {\n  final ConnectionErrorType type;  // Error category\n  final String message;            // Extracted error description or HTTP status phrase\n  final int? errorCode;            // Internal WebKit/Android error code\n  final int? statusCode;           // HTTP status code, if applicable\n  final bool isRecoverable;        // Suggests if safe to offer a \"Retry\" button\n}\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"connectionerrortype","__idx":21},"children":["ConnectionErrorType"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["noInternet"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Device is offline"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dnsFailure"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["DNS resolution failed"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["timeout"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Request timed out"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sslError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["SSL/TLS certificate error"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["httpClientError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["HTTP 4xx response"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["httpServerError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["HTTP 5xx response"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webResourceError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["WebView resource loading error"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["unknown"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Uncategorized connection error"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["isRecoverable"]}," to decide whether to show a \"Retry\" button to the user."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"terminationsource","__idx":22},"children":["TerminationSource"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["userDismiss"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["User dismissed the checkout, including by exiting an SDK error screen"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["backButton"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["User pressed the hardware or software back button"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["programmatic"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The checkout was closed programmatically"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["connectionError"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A connection error closed the checkout"]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"callback-routing","__idx":23},"children":["Callback Routing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["One failure produces one callback, and each failure reports its actual semantic."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"What happened"},"children":["What happened"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Callback"},"children":["Callback"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The backend confirmed a payment succeeded"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentSuccess"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The backend confirmed a payment failed"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentFailure"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The user submitted a bank transfer"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onUserJourneyCompleted"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The SDK or the order could not proceed"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Connectivity failed, or the page did not load in time"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onConnectionError"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The user left, including from an SDK error screen"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentTerminate(TerminationSource.userDismiss)"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A document download failed"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validationError"]},", field ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["file.save"]},")"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Notes on the routing:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentFailure"]}," means a real decline."]}," A checkout that could not load, a connection failure, or an internal fault all reach ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onConnectionError"]}," instead. A user who abandoned a checkout reaches ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentTerminate"]},". In v1.11.x these were reported as payment failures."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A single load failure fires one callback."]}," A main-frame load failure delivers ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onConnectionError"]}," alone. It used to deliver ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," as well."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," is worth surfacing non-blockingly."]}," The SDK does not currently signal when a reported failure clears, and a transient one can be followed by the checkout rendering normally. Prefer something dismissible over a blocking screen."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A load timeout is advisory."]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ConnectionErrorType.timeout"]}," means the page may still be in flight. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["shouldAutoClose"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["false"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["autoCloseOnConnectionError"]}," does not apply, and if the page does render the SDK withdraws its own error screen and the checkout continues. Prefer not to tear the checkout down on this one - log it, or show something non-blocking."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Your callback always fires first"]},", as soon as the condition is detected, independently of whether the SDK's default dialog is ever dismissed."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A failed document download is not an ending."]}," It reaches ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," as a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validationError"]}," on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field: 'file.save'"]},", and the checkout carries on. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#document-downloads"},"children":["Document Downloads"]},"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"the-sdks-default-error-screens","__idx":24},"children":["The SDK's default error screens"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["These are a safety net for the case where you do not present your own UX - the user is never left with nothing to press. Which screen appears depends on whether retrying is meaningful."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Condition"},"children":["Condition"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Screen"},"children":["Screen"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Controls"},"children":["Controls"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The checkout page failed to load (dropped connection, DNS, SSL)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Connection error screen"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Retry"]}," (when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["isRecoverable"]},") and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Cancel"]},". Retry reloads the page"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The SDK could not reach Glomo to fetch your order"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Error dialog"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Exit"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The page did not render inside its load timeout"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Error dialog"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Exit"]},". Withdrawn automatically if the page renders after all"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The SDK or the order could not proceed (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]},")"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Error dialog"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Exit"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The last three offer no Retry deliberately: a page that never rendered and a backend the SDK could not reach are both conditions where something is genuinely wrong, and failing visibly gets it reported rather than retried into silence. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Exit"]}," closes the checkout and reports ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentTerminate"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TerminationSource.userDismiss"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK does not draw its own error screen when the checkout page reports a failure it handles itself - the page already renders one."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"checkout-status","__idx":25},"children":["Checkout Status"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CheckoutStatus"]}," represents the current state of the checkout flow. These are internal to the SDK and are not part of the integration contract - the callbacks above are. Listed for context when reading logs."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Status"},"children":["Status"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ready"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ready to start"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validating"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Input keys and device securely checked"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentInProgress"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["User typing card details or authorizing"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentSuccessful"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Payment clear"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentFailed"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Processing declined"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentCancelled"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["User bounced"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bankTransferSubmitted"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.0.0."]}," User submitted bank transfer details; the checkout ended but no payment is confirmed. Previously reported as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentSuccessful"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.0.0."]}," The checkout could not proceed and no payment was attempted. Previously reported as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentFailed"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Both new members are ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["appended"]},", so the existing members keep the indices they shipped with. Anything persisting a raw ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status.index"]}," keeps reading what it wrote; only an exhaustive ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["switch"]}," over ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CheckoutStatus"]}," needs new arms."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"platform-specific-behavior","__idx":26},"children":["Platform-Specific Behavior"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["iOS"]},": the SDK intercepts downward swipes to dismiss the payment sheet and triggers ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentTerminate"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TerminationSource.userDismiss"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Android"]},": the SDK overrides the standard pop and checks whether the Flow WebView overlay can go back first. The back button is no longer locked while a payment is in progress. Depending on the flow, leaving from inside a bank page may still take two presses, so an accidental press cannot end a payment."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"dark-mode","__idx":27},"children":["Dark mode"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.2.0."]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["appColorScheme"]}," tells the Glomo checkout page which scheme to present:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"GlomoPayCheckout(\n  config: config,\n  appColorScheme: GlomoColorScheme.auto, // auto | light | dark\n  // ...\n)\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["It requests a scheme, it does not force one."]}," The page decides, against your merchant dashboard settings. Setting ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dark"]}," does not guarantee a dark checkout, and dark mode is enabled per account - until it is enabled for yours, checkout stays light whatever you pass. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/dark-mode"},"children":["Dark mode across the SDKs"]}," for what that gating means and how the other Glomo SDKs resolve the same property."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["What ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auto"]}," resolves to."]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Theme.of(context).brightness"]}," where the checkout is mounted - your app's own appearance, not the device's. A light-only app on a dark phone gets a light checkout, and an in-app theme toggle is followed with no work on your side. An app on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ThemeMode.system"]}," gets the device setting, because that is what its theme already reports."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Pass ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["light"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dark"]}," to override that. Those two values mean the same thing on every Glomo SDK; ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auto"]}," does not, so read the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/dark-mode#what-the-automatic-value-resolves-to"},"children":["cross-SDK table"]}," before assuming parity with your web or native integration."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Changing it mid-checkout is best-effort."]}," Pass a new value and the SDK hands it to the live page immediately, without reloading, so a payment in flight always survives the change. Whether the page acts on it is the page's call, and a checkout still loading when the change lands may miss it. Any later load the SDK performs - a retry, the error screen's Reload - carries the current scheme. Nothing errors and the payment is never interrupted: the SDK will not reload the checkout to force a scheme through."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For a guaranteed scheme, set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["appColorScheme"]}," before the checkout mounts."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Do not remount the checkout to change the scheme"]}," - that reloads the WebView and loses the payment."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["On Android"]},", the WebView may still apply its own darkening to the checkout on hosts targeting SDK 32 or below with a dark app theme; from targetSdk 33 the platform leaves the page alone unless asked. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["glomopay_sdk"]}," is pure Dart and the underlying Android WebView plugin exposes no control over the setting, so the SDK cannot disable it."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Also on Android"]},", keep ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["uiMode"]}," in your activity's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["android:configChanges"]},", as Flutter's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MainActivity"]}," template already does. Without it, an OS dark-mode toggle destroys and recreates your activity mid-checkout and loses the payment in flight. This is pre-existing behaviour, but it matters more now that users have a reason to toggle."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"file-uploads","__idx":28},"children":["File Uploads"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Bank flows that ask for documents open the device picker. As of v2.0.0 the SDK does not filter the picker by file type - whatever the bank's page asks for can be selected, including formats the picker previously greyed out. The bank's page is the authority on what it accepts."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If the user denies camera or storage permission, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onUserRefusedDevicePermissions"]}," fires."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"document-downloads","__idx":29},"children":["Document Downloads"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.1.0."]}," Some bank flows offer a form or a signed agreement to download - the Kotak LRS agreements step is the first. Inside a WebView the page cannot put a file on the device by itself - its browser download path is an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["anchor[download]"]}," on a blob URL, which a WebView drops. Before v2.1.0 the control silently did nothing: the tap produced no file and no error."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK now handles those downloads. When the checkout page asks it to save a document:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The SDK fetches the document over its own HTTPS client, with a 30-second timeout."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["It opens the platform save dialog with the filename pre-filled - ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ACTION_CREATE_DOCUMENT"]}," on Android, the document picker in export mode on iOS. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The user chooses the destination"]},", so where the file lands is their choice, not the SDK's."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The bytes are written to what they picked."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["There is nothing to integrate. No callback, no configuration, no permission and no new dependency - ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["file_picker"]},", already in the graph for uploads, provides the dialog on both platforms. The checkout page only offers the download to the SDK once the SDK advertises it, so an older SDK build keeps whatever behaviour it had rather than swallowing the user's tap."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"when-a-download-fails","__idx":30},"children":["When a download fails"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A failed download is reported three ways and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["never ends the checkout"]}," - the payment session is untouched and the user can carry on:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The SDK shows a dismissible dialog naming what went wrong, with a single ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["OK"]},". It dims the checkout rather than covering it."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," fires with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SdkErrorType.validationError"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field"]}," set to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["'file.save'"]},". Filter on that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field"]}," to tell a failed download apart from any other validation error."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The failure is recorded in the SDK's own diagnostics."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"onSdkError: (List<SdkError> errors) {\n  for (final error in errors) {\n    if (error.field == 'file.save') {\n      // A document download failed. The checkout is still alive and the\n      // user has already been told - log it, don't tear anything down.\n      continue;\n    }\n    // Everything else.\n  }\n},\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A user who opens the save dialog and cancels it is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["not"]}," a failure: no dialog, no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]},", nothing to handle."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["What can fail, and what the user is told:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Condition"},"children":["Condition"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"User sees"},"children":["User sees"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The document could not be fetched - timeout, transport, non-200"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["\"Couldn't download the document. Please try again.\""]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The document URL is not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["\"Couldn't download the document. Please try again.\""]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The document is larger than 100 MB"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["\"Downloads above 100 MB are not supported.\""]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The platform refused the save dialog, or the write failed"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["\"Couldn't save the document to your device. Please try again.\""]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The 100 MB ceiling is a memory bound, not a policy about document sizes: the save dialog takes the bytes rather than a path, so the whole document is held in memory to hand over, and something larger would take the host app down with it. Oversized responses are refused mid-download and never buffered whole."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK's dialog is suppressed while a connection error screen or a bank page overlay is on screen, so nothing ever paints over the user's only recovery control. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," still fires in those cases."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"origin-policy","__idx":31},"children":["Origin Policy"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["New in v2.1.2."]}," The SDK now decides for itself which pages belong to Glomo, and treats everything else as untrusted. Nothing to integrate - no callback, no configuration and no new dependency - but two behaviours are worth knowing about, because some of what they refuse reaches ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"navigation-is-restricted","__idx":32},"children":["Navigation is restricted"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The checkout WebView will only open Glomo's own pages: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https"]}," on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["glomopay.com"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["glomo.one"]}," (including subdomains) on the default port. A top-level navigation anywhere else is stopped before it loads and reported to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SdkErrorType.unknown"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field: 'navigation'"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This does not touch the bank flow. Bank pages, 3DS challenges and card frames run in the flow WebView and in iframes, which the policy leaves alone - a card payment or a bank redirect behaves exactly as it did in v2.1.1. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mailto:"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tel:"]}," links still hand off to the mail and phone apps."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"payment-results-are-accepted-only-from-glomos-checkout-page","__idx":33},"children":["Payment results are accepted only from Glomo's checkout page"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentSuccess"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPaymentFailure"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onUserJourneyCompleted"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onEvent"]}," now fire only for messages the checkout page itself posts. The two untrusted sources are handled differently, so expect different signals:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A third-party frame inside the checkout page."]}," The message is checked against the page's own origin and dropped before it reaches the bridge. No callback fires and nothing reaches your app - the drop is recorded in the SDK's own diagnostics only."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A bank or 3DS page in the flow WebView."]}," A message shaped like a payment result is dropped and reported to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SdkErrorType.unknown"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field: 'flow'"]},". Anything else such a page posts is ignored quietly. A ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#document-downloads"},"children":["document download"]}," it requests is refused and reported as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SdkErrorType.validationError"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field: 'file.save'"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Neither of these ends the checkout. As before, confirm every payment on your server by verifying its signature - the origin check narrows what can reach your callbacks, it does not replace verification."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"mock-mode","__idx":34},"children":["Mock Mode"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK infers mock mode from the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["publicKey"]}," prefix:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Prefix"},"children":["Prefix"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Mode"},"children":["Mode"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Environment"},"children":["Environment"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["live_"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Live"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Production"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["test_"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Mock"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Test/sandbox"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mock_"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Mock"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Test/sandbox"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Mock mode keys route to the sandbox backend. No real transactions are created."]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Connection heuristics allow mock traffic"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Simulate transactions without real money movement"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"troubleshooting","__idx":35},"children":["Troubleshooting"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"build-fails-with-the-named-parameter-onuserjourneycompleted-is-required","__idx":36},"children":["Build fails with \"The named parameter 'onUserJourneyCompleted' is required\""]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Expected on upgrade from v1.11.x. Add the callback. If you do not take bank transfers, an empty body is a complete migration - it simply never fires. See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/migration"},"children":["Migration Guide"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"invalid-order-id-format","__idx":37},"children":["Invalid Order ID format"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ensure ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["orderId"]}," starts with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["order_"]}," and has an appropriate length."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"invalid-subscription-id-format","__idx":38},"children":["Invalid Subscription ID format"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ensure ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["subscriptionId"]}," starts with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sub_"]}," and has an appropriate length."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"mutually-exclusive-identifiers","__idx":39},"children":["Mutually exclusive identifiers"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Provide either ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["orderId"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["subscriptionId"]},", not both. The SDK fires ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," if both are set."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"sdk-crashes-immediately-on-open","__idx":40},"children":["SDK crashes immediately on open"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ensure ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["config.publicKey"]}," is correctly set with an expected prefix (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["live_"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mock_"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["test_"]},")."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"file-upload-buttons-do-nothing","__idx":41},"children":["File upload buttons do nothing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flutter clean"]}," and rebuild. Ensure Camera and Storage permissions are declared in the platform config."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"download-buttons-do-nothing","__idx":42},"children":["Download buttons do nothing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Check the SDK version first: document downloads need v2.1.0 or later. On an older build the checkout page falls back to its browser download path, which cannot write a file from inside a WebView, so the tap produces nothing. Upgrading is the whole fix - there is nothing to wire up."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you are on v2.1.0 or later and a download still produces nothing, the checkout page may be serving that flow without a download control. Send the order id to the Glomo mobile team."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"downloads-report-couldnt-download-the-document","__idx":43},"children":["Downloads report \"Couldn't download the document\""]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK fetched the document and the fetch failed - an expired signed link, a timeout, or a non-200. The document URL must also be ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https"]},"; a plain-",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["http"]}," link is refused without a request being made. The failure reaches ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field: 'file.save'"]},", and the message names which of these it was in the SDK's diagnostics."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onsdkerror-reports-field-navigation-mid-checkout","__idx":44},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," reports ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field: 'navigation'"]}," mid-checkout"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The checkout page tried to send the user somewhere outside Glomo and v2.1.2 stopped it. This is the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#origin-policy"},"children":["origin policy"]}," doing its job; the checkout keeps running. If a legitimate step of your flow is being blocked, send the order id to the Glomo mobile team."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onsdkerror-reports-field-flow","__idx":45},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," reports ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["field: 'flow'"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A page inside the bank flow posted something shaped like a payment result and v2.1.2 refused it. No callback fired for it, deliberately. Reconcile the order against your backend as usual. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#origin-policy"},"children":["Origin Policy"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"device-forbidden-error","__idx":46},"children":["Device Forbidden Error"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Rooted or jailbroken devices trigger ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSdkError"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SdkErrorType.deviceForbidden"]}," on live checkouts. Test on an unmodified device or an emulator."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"my-app-lost-its-error-handlers-after-upgrading","__idx":47},"children":["My app lost its error handlers after upgrading"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["v2.0.0 no longer installs ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FlutterError.onError"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PlatformDispatcher.instance.onError"]},", or native crash handlers inside your app. If your app was inheriting SDK-installed handlers, install your own. In exchange, the SDK no longer constrains versions for error-tracking packages."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"security","__idx":48},"children":["Security"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK loads checkout pages in an isolated WebView. No PAN, CVV, or sensitive payment data passes through the Flutter bridge - all sensitive input is handled within the WebView's sandboxed context."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Built-in jailbreak and root detection prevents checkout on compromised devices."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["SDK diagnostics are spooled to the application support directory and flushed on the next initialisation, so a checkout interrupted by process death is still reported. The SDK writes only its own diagnostics there, and no payment data."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Document downloads are fetched over ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https"]}," only (the sole exception is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["localhost"]},", for the mock server). Download URLs are signed links, so they are treated as credentials: neither the URL nor the filename is ever written to diagnostics or analytics - failures are reported by outcome, file extension and byte count."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["As of v2.1.2 the checkout WebView stays on Glomo's own origins, and payment results and checkout events are accepted only from Glomo's checkout page - see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#origin-policy"},"children":["Origin Policy"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To report a security vulnerability, email security@glomopay.com. Do not open a public GitHub issue for security reports."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"dependency-notes","__idx":49},"children":["Dependency Notes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["path_provider"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["^2.0.0"]},") is a direct dependency as of v2.0.0. It was already resolved transitively, so no new package enters your dependency graph, but the constraint is now the SDK's own and may affect resolution in a pinned host app."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["v2.1.0 adds no dependency. ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#document-downloads"},"children":["Document downloads"]}," use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["file_picker"]},", which the SDK already depended on for uploads. v2.1.1, v2.1.2 and v2.1.3 add none either."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["v2.2.0 ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["removes"]}," one: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["segment_analytics"]}," is no longer a dependency of the SDK. Nothing enters your graph, and one package leaves it. If your app depends on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["segment_analytics"]}," itself, it keeps whatever constraint it declares - the SDK no longer has an opinion on the version."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"exports","__idx":50},"children":["Exports"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"dart","header":{"controls":{"copy":{}}},"source":"/**\n * All public exports from package:glomopay_sdk/glomopay_sdk.dart\n */\n\n// Widget\nGlomoPayCheckout\n\n// Controller\nGlomoPayController\n\n// Configuration\nGlomoPayConfig\n\n// Payloads\nGlomoPayPayload\nGlomoPayUserJourneyPayload\n\n// Errors\nSdkError\nSdkErrorType\nConnectionError\nConnectionErrorType\n\n// Enums\nGlomoPayUserJourneyType\nTerminationSource\nCheckoutStatus\nGlomoColorScheme\n","lang":"dart"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related","__idx":51},"children":["Related"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/migration"},"children":["Migration Guide (v1.11.x to v2.0.0)"]}," - step-by-step upgrade instructions with before/after code"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/changelog"},"children":["Changelog"]}," - all release notes"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/dark-mode"},"children":["Dark mode across the SDKs"]}," - what ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auto"]}," resolves to on each Glomo SDK, and the per-account gating"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/v1"},"children":["Flutter SDK v1 (Archived)"]}," - previous version documentation"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/v0.0.15"},"children":["Flutter SDK v0.0.15 (Deprecated)"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/v0.0.13"},"children":["Flutter SDK v0.0.13 (Deprecated)"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/flutter-sdk/v0.0.3"},"children":["Flutter SDK v0.0.3 (Deprecated)"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/react-native-sdk"},"children":["React Native SDK"]}," - Glomo SDK for React Native apps"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/android-sdk"},"children":["Android SDK"]}," - Glomo SDK for native Android apps"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/platform/sdk/unified-sdk"},"children":["Unified SDK (Web)"]}," - Glomo SDK for web apps"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/payin/checkout"},"children":["Checkout overview"]}," - server-side checkout integration and signature verification"]}]}]},"headings":[{"value":"Glomo Flutter SDK v2","id":"glomo-flutter-sdk-v2","depth":1},{"value":"Prerequisites","id":"prerequisites","depth":2},{"value":"System Requirements","id":"system-requirements","depth":2},{"value":"Installation","id":"installation","depth":2},{"value":"Platform Setup","id":"platform-setup","depth":2},{"value":"Android","id":"android","depth":3},{"value":"iOS","id":"ios","depth":3},{"value":"Quick Start","id":"quick-start","depth":2},{"value":"Subscriptions Checkout","id":"subscriptions-checkout","depth":2},{"value":"Asynchronous Payment Journeys","id":"asynchronous-payment-journeys","depth":2},{"value":"Features","id":"features","depth":2},{"value":"API Reference","id":"api-reference","depth":2},{"value":"GlomoPayCheckout Widget","id":"glomopaycheckout-widget","depth":3},{"value":"Props","id":"props","depth":4},{"value":"GlomoPayConfig","id":"glomopayconfig","depth":3},{"value":"GlomoPayPayload","id":"glomopaypayload","depth":3},{"value":"GlomoPayUserJourneyPayload","id":"glomopayuserjourneypayload","depth":3},{"value":"GlomoPayUserJourneyType","id":"glomopayuserjourneytype","depth":4},{"value":"SdkError","id":"sdkerror","depth":3},{"value":"SdkErrorType","id":"sdkerrortype","depth":4},{"value":"ConnectionError","id":"connectionerror","depth":3},{"value":"ConnectionErrorType","id":"connectionerrortype","depth":4},{"value":"TerminationSource","id":"terminationsource","depth":3},{"value":"Callback Routing","id":"callback-routing","depth":2},{"value":"The SDK's default error screens","id":"the-sdks-default-error-screens","depth":3},{"value":"Checkout Status","id":"checkout-status","depth":2},{"value":"Platform-Specific Behavior","id":"platform-specific-behavior","depth":2},{"value":"Dark mode","id":"dark-mode","depth":2},{"value":"File Uploads","id":"file-uploads","depth":2},{"value":"Document Downloads","id":"document-downloads","depth":2},{"value":"When a download fails","id":"when-a-download-fails","depth":3},{"value":"Origin Policy","id":"origin-policy","depth":2},{"value":"Navigation is restricted","id":"navigation-is-restricted","depth":3},{"value":"Payment results are accepted only from Glomo's checkout page","id":"payment-results-are-accepted-only-from-glomos-checkout-page","depth":3},{"value":"Mock Mode","id":"mock-mode","depth":2},{"value":"Troubleshooting","id":"troubleshooting","depth":2},{"value":"Build fails with \"The named parameter 'onUserJourneyCompleted' is required\"","id":"build-fails-with-the-named-parameter-onuserjourneycompleted-is-required","depth":3},{"value":"Invalid Order ID format","id":"invalid-order-id-format","depth":3},{"value":"Invalid Subscription ID format","id":"invalid-subscription-id-format","depth":3},{"value":"Mutually exclusive identifiers","id":"mutually-exclusive-identifiers","depth":3},{"value":"SDK crashes immediately on open","id":"sdk-crashes-immediately-on-open","depth":3},{"value":"File upload buttons do nothing","id":"file-upload-buttons-do-nothing","depth":3},{"value":"Download buttons do nothing","id":"download-buttons-do-nothing","depth":3},{"value":"Downloads report \"Couldn't download the document\"","id":"downloads-report-couldnt-download-the-document","depth":3},{"value":"onSdkError reports field: 'navigation' mid-checkout","id":"onsdkerror-reports-field-navigation-mid-checkout","depth":3},{"value":"onSdkError reports field: 'flow'","id":"onsdkerror-reports-field-flow","depth":3},{"value":"Device Forbidden Error","id":"device-forbidden-error","depth":3},{"value":"My app lost its error handlers after upgrading","id":"my-app-lost-its-error-handlers-after-upgrading","depth":3},{"value":"Security","id":"security","depth":2},{"value":"Dependency Notes","id":"dependency-notes","depth":2},{"value":"Exports","id":"exports","depth":2},{"value":"Related","id":"related","depth":2}],"frontmatter":{"title":"Flutter SDK v2","seo":{"title":"Glomo Flutter SDK v2"}},"lastModified":"2026-11-06T11:06:03.697Z","pagePropGetterError":{"message":"","name":""}},"slug":"/platform/sdk/flutter-sdk/v2","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}