glomopay_sdk 2.0.0
glomopay_sdk: ^2.0.0 copied to clipboard
Official GlomoPay Flutter SDK for integrating LRS Checkout.
GlomoPay Flutter SDK #
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.3and below are deprecated, and1.0.4or 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, orunknown.message(String): A human-readable description of the constraint that failed.field(String?): The field name (e.g.,"orderId") that caused thevalidationError.
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.bankTransferSubmittedis 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 reachonSdkErrororonConnectionError), and a user who abandoned a checkout reachesonPaymentTerminate.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:
validating: Input keys and devices are securely checked.ready: Verified; loading the UI.paymentInProgress: The user is currently typing card details or authorizing.paymentSuccessful: Payment clear.paymentFailed: Processing declined.paymentCancelled: The user bounced.bankTransferSubmitted: The user submitted bank transfer details. The checkout has ended but no payment is confirmed - this used to be reported aspaymentSuccessful, which was never true of it.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 aspaymentFailed.
Platform-Specific Behavior #
- iOS Swiping: The SDK successfully intercepts iOS downward swipe gestures to dismiss the payment sheet and accurately triggers
onPaymentTerminatewithTerminationSource.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
orderIdstarts with"order_"and has appropriate length. - Invalid Subscription ID format: Ensure
subscriptionIdstarts with"sub_"and has appropriate length. - Mutually exclusive identifiers: Provide either
orderIdorsubscriptionId, not both. - SDK crashes immediately on open: Ensure
config.publicKeyis correctly set and starts with the expected prefix (test_orlive_). - File upload buttons do nothing: Run
flutter cleanand ensure Camera/Storage permissions were explicitly granted in the native AndroidManifest and Info.plist layers. - Device Forbidden (Error): Rooted or jailbroken devices immediately trigger
onSdkErroron 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.yamlversion is updatedlib/src/utils/package_version.dartsdkVersionmatches that versionCHANGELOG.mdincludes the release notesdart analyzepassesgit statusis clean or the changes are intentional