glomopay_sdk 2.0.0 copy "glomopay_sdk: ^2.0.0" to clipboard
glomopay_sdk: ^2.0.0 copied to clipboard

Official GlomoPay Flutter SDK for integrating LRS Checkout.

GlomoPay Flutter SDK #

pub package license

The GlomoPay Flutter SDK provides a seamless, secure, and customizable payment checkout experience for your Flutter applications. It features a robust dual-WebView architecture for handling complex payment flows like 3DS auth or bank redirects, built-in security compliance checks, and comprehensive analytics tracking.

See the pub.flutter-io.cn page for the current published version, and the CHANGELOG for release notes.

Features #

  • Dual-WebView Architecture: Handles standard checkout flows and overlay redirects (3DS, bank pages) seamlessly.
  • Robust Security: Built-in jailbreak and root detection to ensure transactions happen on secure devices.
  • Deep Analytics: Automatic Segment analytics and Sentry error reporting for monitoring checkout performance. Error reporting runs over a self-contained HTTP client: no global error handlers are installed and no native crash handler is touched, so your app's own crash reporting is unaffected.
  • Comprehensive Error Handling: Built to handle connection drops, DNS issues, HTTP errors, and validation mistakes gracefully.
  • Native Support: Full support for Android and iOS native features like the camera and file pickers required by certain payment methods.
  • Mock Mode: Easy testing with test keys (test_..., mock_...).

Installation #

1. Depend on it #

Add the glomopay_sdk to your pubspec.yaml dependencies:

dependencies:
  flutter:
    sdk: flutter
  glomopay_sdk: ^2.0.0

Alternatively, run this command in your terminal:

flutter pub add glomopay_sdk

This will automatically add the latest version of the SDK to your project and run an implicit flutter pub get.

Versioning #

This package follows semantic versioning:

  • Versions 1.0.3 and below are deprecated, and 1.0.4 or later versions are available.
  • Use patch releases for bug fixes
  • Use minor releases for backward-compatible features
  • Use major releases for breaking changes

Upgrading from 1.11.x? See MIGRATION.md. The 2.0.0 release introduces new callbacks and some breaking changes in callback behaviors.

2. Import it #

Now in your Dart code, you can use:

import 'package:glomopay_sdk/glomopay_sdk.dart';

Platform Setup #

Android Permissions & Setup #

Add the required permissions in your android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Required for the checkout flow -->
    <uses-permission android:name="android.permission.INTERNET"/>
    
    <!-- Required if you need users to upload images/documents during checkout -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
</manifest>

iOS Permissions & Setup #

Add the keys to your ios/Runner/Info.plist:

<dict>
    <!-- Required for camera/file uploads during the checkout process -->
    <key>NSCameraUsageDescription</key>
    <string>This app requires access to the camera to upload documents required for payment verification.</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>This app requires access to the photo library to select documents for payment verification.</string>
</dict>

Quick Start #

Import the library and display the GlomoPayCheckout widget.

import 'package:flutter/material.dart';
import 'package:glomopay_sdk/glomopay_sdk.dart';

class PaymentScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Complete Payment')),
      body: GlomoPayCheckout(
        config: const GlomoPayConfig(
          publicKey: 'test_pk_12345',
          orderId: 'order_abc123',
        ),
        onPaymentSuccess: (GlomoPayPayload payload) {
          print('Payment succeeded! Payment ID: ${payload.paymentId}');
          // Navigate to success screen
        },
        onPaymentFailure: (GlomoPayPayload payload) {
          // A payment the backend confirms failed - prompt for another card
          // or bank. A checkout that could not load arrives on onSdkError or
          // onConnectionError instead.
          print('Payment failed with Order ID: ${payload.orderId}');
        },
        onUserJourneyCompleted: (GlomoPayUserJourneyPayload journey) {
          // The second happy path: the user made a payment via bank transfer
          // This payment mode is async so paymentId or signature
          // are not available and settlement confirmation will be done
          // via wehbooks
          print('Journey completed: ${journey.journeyType.value} '
              'for order ${journey.orderId}');
        },
        onSdkError: (List<SdkError> errors) {
          print('SDK Error: ${errors.first.message}');
        },
        onConnectionError: (ConnectionError error) {
          print('Connection error: ${error.message}');
        },
        onPaymentTerminate: (TerminationSource source) {
          // Fires whenever the checkout ends without a payment result: the
          // user dismissed it, hit back, exited one of the SDK's error
          // screens, or a connection error closed it. The SDK closes itself,
          // so this is where your own cleanup goes - not the closing.
          print('Checkout ended without a payment: $source');
        },
      ),
    );
  }
}

API Reference #

GlomoPayCheckout Props #

Prop Type Required Description
config GlomoPayConfig Yes The configuration details for the checkout session.
onPaymentSuccess Function(GlomoPayPayload) Yes Called when the payment is completed successfully.
onPaymentFailure Function(GlomoPayPayload) Yes Called when the transaction gets declined or fails. Reports only failed payment attempts, not checkouts that dropped midway due to network connectivity issues.
onUserJourneyCompleted Function(GlomoPayUserJourneyPayload) Yes Called when the user completes an asynchronous journey that ends the checkout without a confirmed payment - today, only bankTransferSubmitted. The payload has no paymentId or signature, so reconcile the order rather than treating it as a payment. These journeys reached onPaymentSuccess before 2.0.0 and no longer do.
onSdkError Function(List<SdkError>) Yes Called when an SDK-level error occurs (e.g. validation or attempts to use on non-compliant devices).
onConnectionError Function(ConnectionError) Yes Called if there's an internet drop, DNS failure, severe HTTP error, or page timeout.
onPaymentTerminate Function(TerminationSource)? No Called when the checkout ends without a payment result: the user dismissed it or exited one of the SDK's error screens (userDismiss), hit back (backButton), or a connection error closed it (connectionError). This is a notification, not a handover - the SDK closes the checkout itself whether or not this callback was supplied, so handling a source to keep the user's exit working will not be required. The SDK will not undergo double close if attempting to close from here.
onEvent Function(String, Map)? No Deprecated. A generic listener for handling deep telemetry events.
onUserRefusedDevicePermissions VoidCallback? No Called when the user denies a required device permission (e.g. camera/media gallery/storage) during file upload in the checkout flow.
autoCloseOnConnectionError bool No Whether a critical connection error auto-closes the checkout and reports onPaymentTerminate(connectionError). The SDK pops its own route on this path, so the user is returned to your app without your callback having to close anything. Defaults to true.

GlomoPayConfig #

Property Type Default Description
publicKey String - Your GlomoPay public key (e.g., live_..., test_...).
orderId String - The unique tracking ID generated for this transaction on your server.
subscriptionId String - The subscription checkout ID. Use this instead of orderId for subscription payments.
server String? null A custom LRS checkout URL (leave blank to use the official env URL).

Payload Types #

GlomoPayPayload

Returned via onPaymentSuccess and onPaymentFailure:

  • orderId (String): The system ID of the order.
  • paymentId (String?): The transaction reference, if generated.
  • signature (String?): The validation signature hash for backend verification.

Error Models #

SdkError

  • type (SdkErrorType): validationError, deviceForbidden, networkError, or unknown.
  • message (String): A human-readable description of the constraint that failed.
  • field (String?): The field name (e.g., "orderId") that caused the validationError.

ConnectionError

  • type (ConnectionErrorType): noInternet, timeout, dnsFailure, sslError, httpClientError, httpServerError, webResourceError, unknown.
  • message (String): Extracted error description or HTTP status phrase.
  • errorCode (int?): The internal WebKit/Android error code.
  • statusCode (int?): HTTP status code, if applicable.
  • isRecoverable (bool): Suggests if it is safe to offer a 'Retry' button.

GlomoPayUserJourneyPayload

  • journeyType (GlomoPayUserJourneyType): which journey ended. bankTransferSubmitted is the only member today.
  • orderId (String): the order the journey belongs to. Always present.
  • senderAccountNumber (String?), transactionReference (String?), status (String?): the journey's own details, when the checkout page reports them.
  • rawResponse (Map<String, dynamic>?): the page's message exactly as it arrived.

There is deliberately no paymentId or 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.

Failure/Error callbacks

  • onPaymentFailure - a payment the checkout page reports as failed. Prompt the user to try another card or bank. The SDK no longer routes error related failures here (a checkout that could not load, a connection failure or an internal error all reach onSdkError or onConnectionError), and a user who abandoned a checkout reaches onPaymentTerminate.
  • onSdkError - the SDK or the order could not proceed. Surface it to the user, and prefer something you can dismiss over a blocking screen: the SDK does not currently signal when a reported failure clears, and a transient one can be followed by the checkout rendering normally.
  • onConnectionError - connectivity failed, or the checkout timed out.

One journey produces one callback. A submitted bank transfer is an ending in its own right: it reports through onUserJourneyCompleted and never also through onPaymentSuccess, and a user who closes what is left of the checkout afterwards does not additionally arrive as onPaymentTerminate.

One failure produces one callback. A dropped connection, a DNS failure or an SSL error - whether it happens while the SDK is fetching your order or while the checkout page is loading - arrives once, through onConnectionError. A rejected or unreadable order, a failed validation or a blocked device arrives once, through onSdkError. Neither is ever reported as a payment failure.

A load-timeout onConnectionError (ConnectionErrorType.timeout) is advisory: the page may still be in flight and can recover. The SDK does not close itself on it - shouldAutoClose is false, so autoCloseOnConnectionError does not apply - and if the page does render, the SDK withdraws its own error screen and the checkout continues normally. Because it can clear itself, prefer not to tear the checkout down on this one. Log it, or show something non-blocking in the meantime.

The SDK's default error screens

Your callback always fires first, as soon as the condition is detected, so you can present your own UX instead. The screens below are a safety net for the case where you do not - the user is never left with nothing to press.

Which one appears depends on whether retrying is meaningful:

Condition Screen Controls
The checkout page failed to load (dropped connection, DNS, SSL) Connection error screen Retry (when isRecoverable) and Cancel. Retry reloads the page.
The SDK could not reach GlomoPay to fetch your order Error dialog Exit
The page did not render inside its load timeout Error dialog Exit. Withdrawn automatically if the page renders after all.
The SDK or the order could not proceed (onSdkError) Error dialog Exit

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. Exit closes the checkout and reports onPaymentTerminate with TerminationSource.userDismiss.

Checkout Lifecycle States (CheckoutStatus) #

These are internal to the SDK and are not part of the integration contract - the callbacks above are. Listed for context when reading logs:

  1. validating: Input keys and devices are securely checked.
  2. ready: Verified; loading the UI.
  3. paymentInProgress: The user is currently typing card details or authorizing.
  4. paymentSuccessful: Payment clear.
  5. paymentFailed: Processing declined.
  6. paymentCancelled: The user bounced.
  7. bankTransferSubmitted: The user submitted bank transfer details. The checkout has ended but no payment is confirmed - this used to be reported as paymentSuccessful, which was never true of it.
  8. error: The checkout could not proceed and no payment was attempted - the order or configuration was unusable, the backend was unreachable, or the user exited one of the SDK's error screens. These used to be reported as paymentFailed.

Platform-Specific Behavior #

  • iOS Swiping: The SDK successfully intercepts iOS downward swipe gestures to dismiss the payment sheet and accurately triggers onPaymentTerminate with TerminationSource.userDismiss.
  • Android Back Button: Automatically overrides standard pop. First checks if the Flow WebView overlay (e.g. 3DS bank page) can go back. Does so accordingly until the modal is closed, returning TerminationSource.backButton.

Mock Mode Testing #

Using a public key that starts with test_ or mock_ shifts the SDK into "mock mode." In this mode:

  • Analytics events attach a mock flag.
  • Connection heuristics allow mock traffic.
  • You can simulate fake transactions without real money movement.

Troubleshooting #

  • Invalid Order ID format: Ensure orderId starts with "order_" and has appropriate length.
  • Invalid Subscription ID format: Ensure subscriptionId starts with "sub_" and has appropriate length.
  • Mutually exclusive identifiers: Provide either orderId or subscriptionId, not both.
  • SDK crashes immediately on open: Ensure config.publicKey is correctly set and starts with the expected prefix (test_ or live_).
  • File upload buttons do nothing: Run flutter clean and ensure Camera/Storage permissions were explicitly granted in the native AndroidManifest and Info.plist layers.
  • Device Forbidden (Error): Rooted or jailbroken devices immediately trigger onSdkError on live checkouts. Test on an unmodified device or an emulator.

Publishing #

If you are maintaining this package, publish a new release with:

flutter pub publish --dry-run
flutter pub publish

Before publishing, make sure:

  • pubspec.yaml version is updated
  • lib/src/utils/package_version.dart sdkVersion matches that version
  • CHANGELOG.md includes the release notes
  • dart analyze passes
  • git status is clean or the changes are intentional