moamalat_payment 2.0.0
moamalat_payment: ^2.0.0 copied to clipboard
Comprehensive Flutter package for Moamalat payment gateway integration with native Android SDK and WebView support, featuring intelligent auto-selection and Libya-focused currency handling.
🏦 Moamalat Payment Gateway #
The most comprehensive Flutter package for Moamalat payment integration
Seamlessly integrate Moamalat payment gateway into your Flutter applications with support for Android, iOS, Web, and WASM platforms.
🌟 Overview #
MoamalatPayment is a powerful, feature-rich Flutter package that provides seamless integration with Libya's leading payment gateway. Built specifically for the Libyan market, it offers comprehensive payment processing capabilities with support for both Native Android SDK and WebView implementations, featuring intelligent auto-selection for optimal user experience.
🎯 Why Choose MoamalatPayment? #
- 🚀 Dual Integration: Native Android SDK + WebView support for all platforms
- ⚡ Auto-Selection: Intelligently chooses the best payment method for each platform
- 💱 Currency Conversion: Built-in Dinar ↔ Dirham conversion utilities
- 🔒 Enterprise Security: HMAC-SHA256 encryption and secure transaction handling
- 🌐 Arabic Support: Full RTL and Arabic language support
- 📱 Responsive Design: Optimized for all screen sizes and orientations
- 🎨 Customizable UI: Beautiful loading indicators and error handling
- 🔧 Developer Friendly: Comprehensive documentation and examples
✨ Features #
🏗️ Core Functionality #
- ✅ Native SDK Integration - Direct Android SDK integration for optimal performance
- ✅ WebView Fallback - Universal WebView support for all platforms
- ✅ Smart Method Selection - Automatically chooses SDK or WebView based on platform
- ✅ Transaction Management - Comprehensive success/error handling with detailed callbacks
- ✅ Environment Support - Seamless switching between test and production modes
💰 Currency & Localization #
- ✅ Currency Conversion - Built-in Libyan Dinar to Dirham converter with validation
- ✅ Arabic Language Support - Full RTL support with customizable Arabic messages
- ✅ Amount Formatting - Smart formatting with thousands separators and currency symbols
- ✅ Input Validation - Comprehensive validation for all payment parameters
🔐 Security & Compliance #
- ✅ HMAC-SHA256 Encryption - Industry-standard transaction signing
- ✅ Secure Key Management - Protected merchant secret key handling
- ✅ Transaction Integrity - Tamper-proof payment verification
- ✅ PCI Compliance Ready - Secure payment processing standards
🎨 User Experience #
- ✅ Loading Indicators - Beautiful, customizable loading screens
- ✅ Smooth Navigation - Optimized back button and navigation handling
- ✅ Responsive Design - Perfect display on all screen sizes
- ✅ Error Handling - User-friendly error messages and recovery options
🛠️ Developer Experience #
- ✅ WASM Compatible - Works with WebAssembly for modern web deployment
- ✅ Comprehensive Documentation - Detailed guides, examples, and API reference
- ✅ Type Safety - Full Dart type safety with detailed model classes
- ✅ Easy Integration - Simple setup with minimal configuration required
🚀 Quick Start #
📦 Installation #
Add moamalat_payment
to your pubspec.yaml
:
dependencies:
moamalat_payment: ^1.0.1
Then run:
flutter pub get
📱 Platform Setup #
Android (Required for SDK Support)
- Add SDK dependencies to
android/app/build.gradle
:
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 19 // Minimum required
targetSdkVersion 34
multiDexEnabled true
}
}
dependencies {
implementation 'com.github.payskyCompany:NUMO-PayButton-SDK-android:1.0.12'
implementation 'androidx.multidex:multidex:2.0.1'
}
- Add Jitpack repository to
android/build.gradle
:
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' } // Add this line
}
}
iOS
Ensure your ios/Runner/Info.plist
includes:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Web
No additional setup required! Works out of the box with WASM support.
🔑 Getting Your Credentials #
- Contact Moamalat: Reach out to Moamalat to get your merchant account
- Receive Credentials: You'll get:
merchantId
- Your unique merchant identifierterminalId
- Your terminal identifiermerchantSecretKey
- Your secret key for transaction signing
- Test Environment: Request test credentials for development
⚡ Basic Implementation #
Option 1: Auto-Select Method (Recommended)
import 'package:flutter/material.dart';
import 'package:moamalat_payment/moamalat_payment.dart';
class PaymentScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Payment')),
body: MoamalatPaymentUnified(
// Auto-selects SDK on Android, WebView elsewhere
merchantId: "your_merchant_id",
merchantReference: "ORDER_${DateTime.now().millisecondsSinceEpoch}",
terminalId: "your_terminal_id",
amount: "1000", // 1 LYD in dirham
merchantSecretKey: "your_secret_key",
// Environment
isTest: true, // Set to false for production
// Localization
loadingMessage: "الرجاء الإنتظار جاري تحويلك لبوابة الدفع",
// Callbacks
onCompleteSucsses: (transaction) {
print('Payment successful: ${transaction.systemReference}');
// Navigate to success screen
},
onError: (error) {
print('Payment failed: ${error.error}');
// Handle error
},
),
);
}
}
Option 2: Force Specific Method
// Force SDK method (Android only)
MoamalatPaymentUnified(
paymentMethod: PaymentMethod.sdk,
// ... other parameters
)
// Force WebView method (all platforms)
MoamalatPaymentUnified(
paymentMethod: PaymentMethod.webview,
// ... other parameters
)
Option 3: Original WebView Implementation
// Use the original WebView-only widget
MoamalatPayment(
merchantId: "your_merchant_id",
merchantReference: "ORDER_${DateTime.now().millisecondsSinceEpoch}",
terminalId: "your_terminal_id",
amount: "1000", // 1 LYD in dirham
merchantSecretKey: "your_secret_key",
// Environment
isTest: true,
// Callbacks
onCompleteSucsses: (transaction) {
print('Payment failed: ${error.error}');
// Show error dialog
},
),
);
}
}
💱 Currency Conversion #
The package includes a powerful CurrencyConverter
utility for handling Libyan Dinar to Dirham conversions:
🔄 Basic Conversion #
import 'package:moamalat_payment/moamalat_payment.dart';
// Convert 10.5 LYD to dirham
String dirhamAmount = CurrencyConverter.dinarToDirham(10.5); // "10500"
// Use in payment
MoamalatPayment(
amount: dirhamAmount, // "10500" dirham
// ... other parameters
)
🎯 Advanced Usage #
// Convert from string input
String userInput = "25.750";
if (CurrencyConverter.isValidDinarAmount(userInput)) {
String dirham = CurrencyConverter.dinarStringToDirham(userInput); // "25750"
}
// Format for display
String formatted = CurrencyConverter.formatDinarAmount(10.5); // "10.50 LYD"
String dirhamFormatted = CurrencyConverter.formatDirhamAmount("10500"); // "10,500 dirham"
// Convert back for display
double dinarValue = CurrencyConverter.dirhamToDinar("10500"); // 10.5
📊 Conversion Reference #
Libyan Dinar (LYD) | Dirham | Usage |
---|---|---|
1.0 LYD | 1,000 dirham | CurrencyConverter.dinarToDirham(1.0) |
10.5 LYD | 10,500 dirham | CurrencyConverter.dinarToDirham(10.5) |
25.750 LYD | 25,750 dirham | CurrencyConverter.dinarToDirham(25.750) |
📚 Complete Usage Examples #
🏪 E-commerce Integration #
class CheckoutScreen extends StatefulWidget {
final double totalAmount; // Amount in LYD
final String orderId;
const CheckoutScreen({
required this.totalAmount,
required this.orderId,
});
@override
_CheckoutScreenState createState() => _CheckoutScreenState();
}
class _CheckoutScreenState extends State<CheckoutScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Complete Payment'),
backgroundColor: Colors.blue,
),
body: Column(
children: [
// Order summary
Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text('Order Total: ${CurrencyConverter.formatDinarAmount(widget.totalAmount)}'),
Text('Order ID: ${widget.orderId}'),
],
),
),
// Payment widget
Expanded(
child: MoamalatPayment(
merchantId: "your_merchant_id",
merchantReference: widget.orderId, // this will be used as your reference to the transaction you can manage this string by any format
terminalId: "your_terminal_id",
amount: CurrencyConverter.dinarToDirham(widget.totalAmount),
merchantSecretKey: "your_secret_key",
isTest: false, // Production mode
loadingMessage: "جاري معالجة الدفع...",
onCompleteSucsses: (transaction) {
// Payment successful
_handlePaymentSuccess(transaction);
},
onError: (error) {
// Payment failed
_handlePaymentError(error);
},
),
),
],
),
);
}
void _handlePaymentSuccess(TransactionSucsses transaction) {
// Save transaction to database
// Send confirmation email
// Navigate to success screen
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => PaymentSuccessScreen(
transaction: transaction,
orderAmount: widget.totalAmount,
),
),
);
}
void _handlePaymentError(PaymentError error) {
// Log error
// Show user-friendly error dialog
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Payment Failed'),
content: Text('Sorry, we couldn\'t process your payment. Please try again.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Try Again'),
),
],
),
);
}
}
🎮 Gaming/App Purchase #
class InAppPurchaseScreen extends StatelessWidget {
final String itemName;
final double itemPrice;
const InAppPurchaseScreen({
required this.itemName,
required this.itemPrice,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Purchase $itemName')),
body: MoamalatPayment(
merchantId: "your_merchant_id",
merchantReference: "ORDER_${DateTime.now().millisecondsSinceEpoch}", // this will be used as your reference to the transaction you can manage this string by any format
terminalId: "your_terminal_id",
amount: CurrencyConverter.dinarToDirham(itemPrice),
merchantSecretKey: "your_secret_key",
isTest: true, // Test mode for development
loadingMessage: "جاري تحضير عملية الشراء...",
onCompleteSucsses: (transaction) {
// Unlock premium features
_unlockPremiumFeatures();
// Show success message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Purchase successful! Enjoy your $itemName'),
backgroundColor: Colors.green,
),
);
},
onError: (error) {
// Handle purchase failure
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Purchase failed. Please try again.'),
backgroundColor: Colors.red,
),
);
},
),
);
}
void _unlockPremiumFeatures() {
// Your premium feature unlock logic
}
}
📖 API Reference #
🏗️ MoamalatPayment Widget #
Parameter | Type | Required | Description |
---|---|---|---|
merchantId |
String |
✅ | Your merchant ID from Moamalat |
merchantReference |
String |
✅ | Unique transaction reference |
terminalId |
String |
✅ | Your terminal ID from Moamalat |
amount |
String |
✅ | Amount in dirham (smallest unit) |
merchantSecretKey |
String |
✅ | Your secret key for signing |
onCompleteSucsses |
Function(TransactionSucsses) |
✅ | Success callback |
onError |
Function(PaymentError) |
✅ | Error callback |
isTest |
bool |
❌ | Test mode (default: false ) |
loadingMessage |
String? |
❌ | Custom loading message |
💱 CurrencyConverter Utility #
Method | Parameters | Returns | Description |
---|---|---|---|
dinarToDirham() |
double dinar |
String |
Convert dinar to dirham string |
dinarStringToDirham() |
String dinar |
String |
Convert dinar string to dirham |
dirhamToDinar() |
String dirham |
double |
Convert dirham to dinar |
isValidDinarAmount() |
String amount |
bool |
Validate dinar format |
isValidDirhamAmount() |
String amount |
bool |
Validate dirham format |
formatDinarAmount() |
double amount |
String |
Format dinar for display |
formatDirhamAmount() |
String amount |
String |
Format dirham for display |
📊 Response Models #
TransactionSucsses
class TransactionSucsses {
final String txnDate;
final String systemReference;
final String networkReference;
final String merchantReference;
final int amount;
final String currency;
final String paidThrough;
final String payerAccount;
final String payerName;
final String providerSchemeName;
final String secureHash;
final String displayData;
final String tokenCustomerId;
final String tokenCard;
}
PaymentError
class PaymentError {
final String error;
final String amount;
final String merchantReference;
final String dateTimeLocalTrxn;
final String secureHash;
}
🔧 Advanced Configuration #
🌐 Multi-Language Support #
// Arabic (RTL)
MoamalatPayment(
loadingMessage: "الرجاء الإنتظار جاري تحويلك لبوابة الدفع",
// ... other parameters
)
// English (LTR)
MoamalatPayment(
loadingMessage: "Please wait, redirecting to payment gateway...",
// ... other parameters
)
🔒 Security Best Practices #
// ✅ DO: Store secret key securely
class SecureConfig {
static const String merchantSecretKey = String.fromEnvironment('MOAMALAT_SECRET');
}
// ✅ DO: Generate unique references
String generateReference() {
return "YOUR_UNIQUE_REFERENCE"; // Generate unique reference for each transaction
}
// ❌ DON'T: Hardcode secret keys in source code
// const String secretKey = "your_actual_secret_key"; // Never do this!
⚠️ CRITICAL SECURITY WARNING #
NEVER commit production credentials to version control!
- ❌ DON'T: Include real
merchantId
,terminalId
, ormerchantSecretKey
in your code - ❌ DON'T: Push production credentials to GitHub or any public repository
- ✅ DO: Use environment variables or secure configuration files
- ✅ DO: Use placeholder values in examples and documentation
- ✅ DO: Keep production credentials in secure, encrypted storage
// ❌ WRONG - Never do this
merchantId: "10038160862", // Real production ID
merchantSecretKey: "real_secret_key_here", // Real secret
// ✅ CORRECT - Use placeholders in examples
merchantId: "YOUR_MERCHANT_ID", // Placeholder
merchantSecretKey: "YOUR_MERCHANT_SECRET_KEY", // Placeholder
🎨 Custom Error Handling #
void handlePaymentError(PaymentError error, BuildContext context) {
String userMessage;
switch (error.error.toLowerCase()) {
case 'insufficient funds':
userMessage = 'عذراً، الرصيد غير كافي';
break;
case 'card expired':
userMessage = 'البطاقة منتهية الصلاحية';
break;
case 'network error':
userMessage = 'خطأ في الاتصال، يرجى المحاولة مرة أخرى';
break;
default:
userMessage = 'حدث خطأ في عملية الدفع';
}
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('فشل في الدفع'),
content: Text(userMessage),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('حسناً'),
),
],
),
);
}
🌍 Platform Support #
Platform | Native SDK | WebView | Auto-Selection | Notes |
---|---|---|---|---|
Android | ✅ Available | ✅ Available | ✅ Prefers SDK | Requires minSdkVersion 19+ |
iOS | ❌ Not Available | ✅ Available | ✅ Uses WebView | Requires iOS 11.0+ |
Web | ❌ Not Available | ✅ Available | ✅ Uses WebView | Works with all browsers |
WASM | ❌ Not Available | ✅ Available | ✅ Uses WebView | Future-ready web deployment |
Desktop | ❌ Not Available | ⚠️ Limited | ❌ Error fallback | Shows platform not supported |
🚨 Important Notes #
💰 Amount Format & Payment Methods #
🎯 Unified Widget (MoamalatPaymentUnified) - Recommended
// ✅ ALWAYS use dirham format (string) regardless of payment method
MoamalatPaymentUnified(
amount: "1000", // 1 LYD in dirham - works for both SDK and WebView
// amount: CurrencyConverter.dinarToDirham(1.0), // Also correct
// ... other parameters
)
📱 Direct SDK Service (MoamalatSdkService)
// ✅ Use LYD amount (double) - SDK handles conversion internally
await MoamalatSdkService.startPayment(
amount: 1.0, // 1 LYD as double - SDK converts to dirham automatically
// ... other parameters
)
🌐 WebView Widget (MoamalatPayment)
// ✅ Use dirham format (string) - WebView expects dirham
MoamalatPayment(
amount: "1000", // 1 LYD in dirham
// amount: CurrencyConverter.dinarToDirham(1.0), // Also correct
// ... other parameters
)
❌ Common Mistakes
- ❌ Wrong:
"1.0"
(This is dinar, not dirham) - ❌ Wrong:
"1,000"
(Contains comma) - ❌ Wrong:
1000
(Should be string, not number for widgets) - ❌ Wrong: Using dirham string in SDK service (use LYD double instead)
🔐 Security Requirements #
- Store
merchantSecretKey
securely (environment variables, secure storage) - Use unique
merchantReference
for each transaction - Never expose secret keys in client-side code
- Always validate amounts before processing
🌐 Environment Setup #
- Use
isTest: true
during development - Switch to
isTest: false
for production - Test thoroughly in both environments
- Monitor transaction logs
🆘 Troubleshooting #
Common Issues #
❓ "Platform not supported" error
- Solution: Use Android, iOS, or Web. Desktop platforms show this message.
❓ Payment gateway not loading
- Solution: Check internet connection and ensure Moamalat URLs are accessible.
❓ Invalid hash error
- Solution: Verify your
merchantSecretKey
is correct and properly formatted.
❓ Amount validation failed
- Solution: Ensure amount is in dirham format (string of integers only).
Getting Help #
- 📧 Email: Contact Moamalat support
- 🐛 Issues: GitHub Issues
- 📖 Documentation: This comprehensive guide
- 💬 Community: Flutter Libya community
🤝 Contributing #
We welcome contributions! Please see our Contributing Guide for details.
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ for the Libyan developer community
If this package helped you, please consider giving it a ⭐ on GitHub!