all_paystack_payments 1.1.0
all_paystack_payments: ^1.1.0 copied to clipboard
A Flutter plugin for integrating Paystack payment services, supporting card payments, bank transfers, and mobile money.
All Paystack Payments #
A comprehensive Flutter plugin for integrating Paystack payment services, supporting card payments, bank transfers, and mobile money transactions across multiple platforms including Android, iOS, Web, Windows, Linux, and macOS.
Screenshots #
Payment Method Selection #
Example app showing available payment methods
Card Payment Form #
Secure card payment form with validation
Bank Transfer Details #
Bank account details for transfer payments
Table of Contents #
- Quick Start
- Features
- Supported Platforms
- Installation & Setup
- Payment Methods
- Payment Management
- Error Handling
- Testing & Quality Assurance
- Best Practices & Security
- Troubleshooting
- Migration Guide
- API Reference
- Example App
- Contributing
- Support
Quick Start #
New to Paystack? Follow our step-by-step setup guide with screenshots to get started in minutes.
import 'package:all_paystack_payments/all_paystack_payments.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize with your Paystack public key
await AllPaystackPayments.initialize('pk_test_your_public_key_here');
runApp(MyApp());
}
// Process a payment
Future<void> processPayment() async {
final response = await AllPaystackPayments.initializeCardPayment(
amount: 50000, // β¦500.00 in kobo
email: 'customer@example.com',
cardNumber: '4084084084084081',
expiryMonth: '12',
expiryYear: '25',
cvv: '408',
cardHolderName: 'John Doe',
);
if (response.isSuccessful) {
print('Payment successful: ${response.reference}');
} else {
print('Payment failed: ${response.gatewayResponse}');
}
}
Features #
- π Secure Payment Processing: PCI DSS compliant card tokenization and secure payment flows (see SECURITY.md)
- π³ Multiple Payment Methods: Support for debit/credit cards, bank transfers, and mobile money
- π Multi-Currency Support: NGN, USD, GHS, ZAR, KES
- π± Cross-Platform: Works seamlessly on Android, iOS, Web, Windows, Linux, and macOS
- π Transaction Verification: Built-in methods for real-time payment verification and status checking
- β‘ Type-Safe APIs: Strongly typed Dart APIs with comprehensive enums
- π‘οΈ Error Handling: Robust error handling with detailed error messages and custom exceptions
- π― Easy Integration: Simple, intuitive API design for quick implementation
- π Payment Management: Cancel payments and check payment status programmatically
- π Comprehensive Logging: Debug mode support for troubleshooting
- π§ͺ Enterprise-Grade Testing: 80%+ test coverage with automated CI/CD, performance benchmarks, and comprehensive error scenario testing
- π Production Ready: Extensive validation, security testing, and cross-platform compatibility verification
Supported Platforms #
Platform | Minimum Version | Status | Setup Required |
---|---|---|---|
Android | API 21 (Android 5.0) | β Fully Supported | Internet permission |
iOS | 11.0 | β Fully Supported | App Transport Security |
Web | Modern browsers | β Fully Supported | None |
Windows | Windows 10+ | β Fully Supported | None |
Linux | Ubuntu 18.04+ | β Fully Supported | None |
macOS | 10.14+ | β Fully Supported | Network entitlements |
Installation & Setup #
Step 1: Add the Dependency #
Add all_paystack_payments
to your pubspec.yaml
file:
dependencies:
all_paystack_payments: ^1.0.0
Then run:
flutter pub get
Step 2: Get Your Paystack Keys #
- Sign up/Login to your Paystack Dashboard
- Navigate to Settings β API Keys & Webhooks
- Copy your public key:
- Test Mode:
pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- Live Mode:
pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- Test Mode:
β οΈ Security Note: Never commit API keys to version control. Use environment variables or secure storage.
Step 3: Initialize the Plugin #
Initialize the plugin in your main.dart
before using any payment methods:
import 'package:all_paystack_payments/all_paystack_payments.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize with your Paystack public key
await AllPaystackPayments.initialize('pk_test_your_public_key_here');
runApp(MyApp());
}
Step 4: Platform-Specific Setup #
Android Setup
Add internet permission to your android/app/src/main/AndroidManifest.xml
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- ... other permissions -->
</manifest>
Screenshot: Android Manifest Setup
π± [Screenshot showing AndroidManifest.xml with internet permission added]
iOS Setup
Add the following to your ios/Runner/Info.plist
:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Screenshot: iOS Info.plist Setup
π± [Screenshot showing Info.plist with NSAppTransportSecurity configuration]
Web Setup
No additional setup required. The plugin automatically handles web integration using Paystack's secure inline.js library.
Windows/Linux/macOS Setup
No additional configuration needed. These platforms have built-in network access.
Step 5: Test Your Setup #
Run the example app to verify everything works:
cd example
flutter run
Screenshot: Example App Running
π± [Screenshot of the example app showing payment options]
Payment Methods #
Card Payments #
Accept debit and credit card payments with secure tokenization.
Basic Implementation
Future<void> processCardPayment() async {
try {
final response = await AllPaystackPayments.initializeCardPayment(
amount: 50000, // Amount in kobo (β¦500.00)
email: 'customer@example.com',
cardNumber: '4084084084084081', // Test card
expiryMonth: '12',
expiryYear: '25',
cvv: '408',
cardHolderName: 'John Doe',
);
if (response.isSuccessful) {
print('Payment successful: ${response.reference}');
// Handle success - update UI, navigate to success screen
} else {
print('Payment failed: ${response.gatewayResponse}');
// Handle failure - show error message
}
} catch (e) {
print('Error: $e');
// Handle error
}
}
Advanced Implementation with Validation
class CardPaymentForm extends StatefulWidget {
@override
_CardPaymentFormState createState() => _CardPaymentFormState();
}
class _CardPaymentFormState extends State<CardPaymentForm> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _cardNumberController = TextEditingController();
final _expiryMonthController = TextEditingController();
final _expiryYearController = TextEditingController();
final _cvvController = TextEditingController();
final _cardHolderController = TextEditingController();
final _amountController = TextEditingController();
Future<void> _processPayment() async {
if (!_formKey.currentState!.validate()) return;
setState(() => _isProcessing = true);
try {
final response = await AllPaystackPayments.initializeCardPayment(
amount: int.parse(_amountController.text),
email: _emailController.text,
cardNumber: _cardNumberController.text.replaceAll(' ', ''),
expiryMonth: _expiryMonthController.text,
expiryYear: _expiryYearController.text,
cvv: _cvvController.text,
cardHolderName: _cardHolderController.text,
reference: 'card_${DateTime.now().millisecondsSinceEpoch}',
metadata: {
'order_id': '12345',
'customer_type': 'premium',
},
);
if (response.isSuccessful) {
_showSuccessDialog(response.reference!);
} else {
_showErrorDialog(response.gatewayResponse ?? 'Payment failed');
}
} on PaystackError catch (e) {
_showErrorDialog(e.message);
} catch (e) {
_showErrorDialog('An unexpected error occurred');
} finally {
setState(() => _isProcessing = false);
}
}
String? _validateCardNumber(String? value) {
if (value?.isEmpty ?? true) return 'Card number is required';
if (!ValidationUtils.isValidCardNumber(value!)) {
return 'Invalid card number';
}
return null;
}
String? _validateExpiry(String? value) {
if (value?.isEmpty ?? true) return 'Required';
if (!ValidationUtils.isValidExpiryDate(
_expiryMonthController.text,
_expiryYearController.text,
)) {
return 'Invalid expiry date';
}
return null;
}
String? _validateCvv(String? value) {
if (value?.isEmpty ?? true) return 'CVV is required';
if (!ValidationUtils.isValidCvv(value!)) {
return 'Invalid CVV (3-4 digits)';
}
return null;
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
validator: (value) =>
value?.isEmpty ?? true ? 'Email is required' : null,
),
TextFormField(
controller: _amountController,
decoration: InputDecoration(labelText: 'Amount (kobo)'),
keyboardType: TextInputType.number,
validator: (value) =>
value?.isEmpty ?? true ? 'Amount is required' : null,
),
TextFormField(
controller: _cardNumberController,
decoration: InputDecoration(labelText: 'Card Number'),
keyboardType: TextInputType.number,
validator: _validateCardNumber,
),
Row(
children: [
Expanded(
child: TextFormField(
controller: _expiryMonthController,
decoration: InputDecoration(labelText: 'MM'),
validator: _validateExpiry,
),
),
SizedBox(width: 8),
Expanded(
child: TextFormField(
controller: _expiryYearController,
decoration: InputDecoration(labelText: 'YY'),
validator: _validateExpiry,
),
),
],
),
TextFormField(
controller: _cvvController,
decoration: InputDecoration(labelText: 'CVV'),
validator: _validateCvv,
),
TextFormField(
controller: _cardHolderController,
decoration: InputDecoration(labelText: 'Card Holder Name'),
validator: (value) =>
value?.isEmpty ?? true ? 'Name is required' : null,
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _isProcessing ? null : _processPayment,
child: _isProcessing
? CircularProgressIndicator()
: Text('Pay Now'),
),
],
),
);
}
}
Test Card Numbers:
- Success:
4084084084084081
- Declined:
4084084084084082
- Insufficient Funds:
4084084084084083
Bank Transfer Payments #
Generate account details for customers to transfer money directly from their bank accounts.
Basic Implementation
Future<void> processBankTransfer() async {
try {
final response = await AllPaystackPayments.initializeBankTransfer(
amount: 100000, // β¦1,000.00 in kobo
email: 'customer@example.com',
);
if (response.isSuccessful) {
print('Transfer initiated: ${response.reference}');
// Display bank account details to customer
_showBankDetails(response);
} else {
print('Transfer failed: ${response.gatewayResponse}');
}
} catch (e) {
print('Error: $e');
}
}
void _showBankDetails(PaymentResponse response) {
final rawData = response.rawResponse;
if (rawData != null && rawData['data'] != null) {
final data = rawData['data'];
final bankName = data['bank_name'];
final accountNumber = data['account_number'];
final accountName = data['account_name'];
// Display these details to the user
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Bank Transfer Details'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Bank: $bankName'),
Text('Account Name: $accountName'),
Text('Account Number: $accountNumber'),
Text('Amount: β¦${response.amount / 100}'),
SizedBox(height: 16),
Text('Please transfer the exact amount within 30 minutes.'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
),
],
),
);
}
}
Advanced Implementation with Status Polling
class BankTransferPayment extends StatefulWidget {
@override
_BankTransferPaymentState createState() => _BankTransferPaymentState();
}
class _BankTransferPaymentState extends State<BankTransferPayment> {
PaymentResponse? _paymentResponse;
Timer? _statusTimer;
Future<void> _initiateTransfer() async {
final response = await AllPaystackPayments.initializeBankTransfer(
amount: 50000,
email: 'customer@example.com',
reference: 'bank_transfer_${DateTime.now().millisecondsSinceEpoch}',
);
if (response.isSuccessful) {
setState(() => _paymentResponse = response);
_startStatusPolling(response.reference!);
_showBankDetailsDialog(response);
}
}
void _startStatusPolling(String reference) {
_statusTimer = Timer.periodic(Duration(seconds: 30), (timer) async {
try {
final statusResponse = await AllPaystackPayments.verifyPayment(reference);
if (statusResponse.isSuccessful) {
_statusTimer?.cancel();
_showSuccessDialog();
} else if (statusResponse.status == PaymentStatus.failed) {
_statusTimer?.cancel();
_showFailureDialog();
}
} catch (e) {
print('Status check failed: $e');
}
});
}
void _showBankDetailsDialog(PaymentResponse response) {
// Implementation as above
}
@override
void dispose() {
_statusTimer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: _initiateTransfer,
child: Text('Pay with Bank Transfer'),
),
if (_paymentResponse != null)
Padding(
padding: EdgeInsets.all(16),
child: Text('Transfer initiated. Please complete the transfer.'),
),
],
);
}
}
Mobile Money Payments #
Accept payments from mobile money wallets (M-Pesa, Airtel Money, etc.).
Basic Implementation
Future<void> processMobileMoneyPayment() async {
try {
final response = await AllPaystackPayments.initializeMobileMoney(
amount: 25000, // β¦250.00 in kobo
email: 'customer@example.com',
provider: MobileMoneyProvider.mpesa,
phoneNumber: '+254712345678',
);
if (response.isSuccessful) {
print('Mobile money payment initiated: ${response.reference}');
// Customer will receive a prompt on their mobile device
_showMobileMoneyInstructions();
} else {
print('Mobile money payment failed: ${response.gatewayResponse}');
}
} catch (e) {
print('Error: $e');
}
}
void _showMobileMoneyInstructions() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Mobile Money Payment'),
content: Text(
'1. Check your phone for the payment prompt\n'
'2. Enter your mobile money PIN\n'
'3. Confirm the payment\n'
'4. Payment will be verified automatically'
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
),
],
),
);
}
Supported Providers and Countries
Provider | Countries | Currency | Phone Format | Notes |
---|---|---|---|---|
M-Pesa | Kenya | KES | +254XXXXXXXXX | Most popular in Kenya |
Airtel Money | Kenya, Tanzania, Uganda | KES, TZS, UGX | +254/+255/+256XXXXXXXXX | Regional coverage |
Vodafone Cash | Ghana | GHS | +233XXXXXXXXX | Ghana only |
Tigo Cash | Ghana | GHS | +233XXXXXXXXX | Ghana only |
Payment Management #
Payment Verification #
Always verify payment status after initiation to confirm completion:
Future<void> verifyPayment(String reference) async {
try {
final response = await AllPaystackPayments.verifyPayment(reference);
switch (response.status) {
case PaymentStatus.success:
print('Payment verified successfully');
// Update order status, deliver product, etc.
await handleSuccessfulPayment(response);
break;
case PaymentStatus.failed:
print('Payment verification failed');
await handleFailedPayment(response);
break;
case PaymentStatus.pending:
print('Payment is still pending');
// Implement retry logic or polling
await scheduleVerificationRetry(reference);
break;
case PaymentStatus.cancelled:
print('Payment was cancelled');
await handleCancelledPayment(response);
break;
}
} catch (e) {
print('Verification error: $e');
}
}
Payment Status Checking #
Check the current status without full verification:
Future<void> checkPaymentStatus(String reference) async {
try {
final response = await AllPaystackPayments.getPaymentStatus(reference);
print('Current status: ${response.status}');
print('Amount: ${response.amount}');
print('Currency: ${response.currency}');
if (response.isPending) {
// Payment is still processing
} else if (response.isSuccessful) {
// Payment completed
}
} catch (e) {
print('Error checking status: $e');
}
}
Payment Cancellation #
Cancel pending payments:
Future<void> cancelPendingPayment(String reference) async {
try {
final success = await AllPaystackPayments.cancelPayment(reference);
if (success) {
print('Payment cancelled successfully');
} else {
print('Failed to cancel payment');
}
} catch (e) {
print('Error cancelling payment: $e');
}
}
Testing & Quality Assurance #
This plugin includes a comprehensive testing suite to ensure reliability and developer confidence. All tests are automatically run in CI/CD pipelines.
π§ͺ Test Coverage #
- Unit Tests: Core business logic, validation, and utilities
- Integration Tests: Platform channel communication and API interactions
- Widget Tests: UI components and user interaction flows
- Performance Tests: Load testing and performance benchmarks
- Error Scenario Tests: Comprehensive failure case coverage
- Edge Case Tests: Boundary values and special character handling
π Quality Metrics #
Metric | Value | Description |
---|---|---|
Test Coverage | 80%+ | Minimum coverage threshold enforced |
Platforms Tested | 6 | Android, iOS, Web, Windows, Linux, macOS |
Test Categories | 8 | Unit, Integration, Widget, Performance, Error, Edge, Security, E2E |
CI/CD Status | β | Automated testing on every commit |
π οΈ Running Tests #
# Run all tests
flutter test
# Run with coverage
flutter test --coverage
# Run specific test groups
flutter test --tags integration # Integration tests only
flutter test --exclude-tags slow # Exclude slow tests
# Run example app tests
cd example && flutter test
π Test Categories #
Unit Tests
// Core validation testing
test('card number validation', () {
expect(ValidationUtils.isValidCardNumber('4111111111111111'), true);
expect(ValidationUtils.isValidCardNumber('invalid'), false);
});
// API method testing with mocks
test('card payment initialization', () async {
when(mockPlatform.getCheckoutUrl(any)).thenAnswer((_) async => checkoutUrl);
when(mockWebViewHandler.processPayment(any)).thenAnswer((_) async => mockResponse);
final result = await AllPaystackPayments.initializeCardPayment(...);
expect(result.isSuccessful, true);
});
Integration Tests
// Platform channel testing
test('method channel communication', () async {
final platform = MethodChannelAllPaystackPayments();
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
if (methodCall.method == 'initializePayment') {
return {'status': 'success', 'reference': 'test_ref'};
}
return null;
});
final response = await platform.initializePayment(request);
expect(response.status, PaymentStatus.success);
});
Widget Tests
// UI interaction testing
testWidgets('payment form validation', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(home: PaymentForm()));
// Enter invalid data
await tester.enterText(find.byType(TextFormField).first, 'invalid-email');
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
// Verify error message appears
expect(find.text('Please enter a valid email'), findsOneWidget);
});
Performance Tests
// Load testing
test('bulk validation performance', () {
final stopwatch = Stopwatch()..start();
for (int i = 0; i < 1000; i++) {
ValidationUtils.isValidCardNumber('4111111111111111');
}
stopwatch.stop();
final timePerValidation = stopwatch.elapsedMicroseconds / 1000;
expect(timePerValidation, lessThan(1000)); // < 1ms per validation
});
Error Scenario Tests
// Comprehensive error handling
test('handles all payment failure scenarios', () async {
// Test card declined
when(mockWebViewHandler.processPayment(any))
.thenThrow(PaystackError(message: 'Card declined', code: 'card_declined'));
expect(
() => AllPaystackPayments.initializeCardPayment(...),
throwsA(isA<PaystackError>()),
);
// Test network timeout
when(mockWebViewHandler.processPayment(any))
.thenThrow(TimeoutException('Payment timed out'));
expect(
() => AllPaystackPayments.initializeCardPayment(...),
throwsA(isA<TimeoutException>()),
);
});
π Security Testing #
// Input sanitization testing
test('prevents XSS attacks', () {
final maliciousInput = '<script>alert("xss")</script>';
final sanitized = ValidationUtils.sanitizeString(maliciousInput);
expect(sanitized, doesNotContain('<script>'));
});
// SQL injection prevention
test('handles malicious SQL input', () {
final sqlInjection = "'; DROP TABLE users; --";
final sanitized = ValidationUtils.sanitizeString(sqlInjection);
expect(ValidationUtils.isValidEmail(sanitized), false);
});
π CI/CD Integration #
The plugin uses GitHub Actions for automated testing:
# .github/workflows/ci.yml
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.24.0'
- run: flutter test --coverage
- run: dart tool/check_coverage.dart # Custom coverage checker
π Coverage Reporting #
Test coverage is automatically calculated and reported:
flutter test --coverage
# Generates coverage/lcov.info
# View HTML report
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html
π Debugging Test Failures #
Enable detailed logging for test debugging:
import 'dart:developer';
void main() {
// Enable test logging
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
log('${record.level.name}: ${record.time}: ${record.message}');
});
// Run your tests
test('debug test', () async {
log('Starting test...');
// Test code here
});
}
π Best Testing Practices #
- Mock External Dependencies: Always mock platform interfaces and network calls
- Test Edge Cases: Include boundary values, null inputs, and special characters
- Performance Benchmarks: Set performance expectations and monitor regressions
- Cross-Platform Testing: Ensure tests run on all supported platforms
- Error Coverage: Test all error paths and exception handling
- Security Testing: Validate input sanitization and injection prevention
Error Handling #
The plugin provides comprehensive error handling through the PaystackError
class:
Future<void> processPaymentWithErrorHandling() async {
try {
final response = await AllPaystackPayments.initializeCardPayment(
amount: 50000,
email: 'customer@example.com',
cardNumber: '4084084084084081',
expiryMonth: '12',
expiryYear: '25',
cvv: '408',
cardHolderName: 'John Doe',
);
// Handle response
await handlePaymentResponse(response);
} on PaystackError catch (e) {
// Paystack API errors
await handlePaystackError(e);
} on ArgumentError catch (e) {
// Validation errors
await handleValidationError(e);
} catch (e) {
// Network, timeout, or unexpected errors
await handleGeneralError(e);
}
}
Future<void> handlePaystackError(PaystackError e) async {
String userMessage;
switch (e.code) {
case 'insufficient_funds':
userMessage = 'Insufficient funds. Please check your account balance.';
break;
case 'card_declined':
userMessage = 'Card was declined. Please try a different card.';
break;
case 'invalid_card':
userMessage = 'Invalid card details. Please check and try again.';
break;
case 'expired_card':
userMessage = 'Card has expired. Please use a valid card.';
break;
default:
userMessage = e.message;
}
await showErrorDialog(userMessage);
}
Best Practices & Security #
Security Best Practices #
-
Key Management
// Use environment variables const publicKey = String.fromEnvironment('PAYSTACK_PUBLIC_KEY'); // Or secure storage final publicKey = await SecureStorage.getPaystackKey();
-
Input Validation
// Always validate user input if (!ValidationUtils.isValidEmail(email)) { throw ArgumentError('Invalid email format'); }
-
Error Logging
// Log errors without sensitive data catch (e) { log('Payment error: ${e.toString()}'); // Never log: card numbers, CVV, PIN, etc. }
Performance Best Practices #
-
Async Handling
Future<void> processPayment() async { showLoadingSpinner(); try { final response = await AllPaystackPayments.initializeCardPayment(...); hideLoadingSpinner(); handleResponse(response); } catch (e) { hideLoadingSpinner(); handleError(e); } }
-
Timeout Handling
final response = await AllPaystackPayments.initializeCardPayment(...) .timeout(Duration(seconds: 30));
User Experience Best Practices #
-
Loading States
setState(() => _isProcessing = true); try { final response = await processPayment(); } finally { setState(() => _isProcessing = false); }
-
Clear Messaging
if (response.isPending) { showMessage('Payment initiated. Please complete the transaction.'); } else if (response.isSuccessful) { showMessage('Payment successful! Thank you.'); }
Troubleshooting #
Common Issues #
"Invalid public key" error
- Ensure you're using a valid Paystack public key
- Check that the key matches your environment (test/live)
- Verify the key format (starts with
pk_test_
orpk_live_
)
Card payment fails with "Invalid card details"
- Verify card number format and length
- Check expiry date format (MM/YY)
- Ensure CVV is 3-4 digits
- Test with Paystack test cards
Mobile money payment not working
- Verify phone number includes country code
- Check that the provider is supported in the customer's country
- Ensure customer has sufficient balance
Bank transfer account not generated
- Check your Paystack dashboard for account generation settings
- Ensure bank transfers are enabled for your account
Verification returns pending status
- Some payment methods take time to process
- Implement polling or webhooks for real-time updates
- Check Paystack dashboard for transaction status
Debug Mode #
Enable detailed logging for debugging:
import 'dart:developer';
Future<void> debugPaymentProcess() async {
try {
final response = await AllPaystackPayments.initializeCardPayment(...);
log('Response: ${response.toString()}');
log('Raw response: ${response.rawResponse}');
} catch (e) {
log('Error: $e');
}
}
Platform-Specific Issues #
Android: Ensure internet permission in AndroidManifest.xml
iOS: Configure App Transport Security in Info.plist
Web: No additional setup required
Desktop: Ensure network access and proper TLS support
Migration Guide #
Migrating from flutter_paystack #
If you're migrating from the flutter_paystack
plugin:
-
Update Dependencies
dependencies: # Remove this # flutter_paystack: ^1.0.0 # Add this all_paystack_payments: ^1.0.0
-
Update Imports
// Old import 'package:flutter_paystack/flutter_paystack.dart'; // New import 'package:all_paystack_payments/all_paystack_payments.dart';
-
Update Initialization
// Old PaystackPlugin.initialize(publicKey: 'pk_test_...'); // New await AllPaystackPayments.initialize('pk_test_...');
-
Update Card Payment
// Old Charge charge = Charge() ..amount = 50000 ..email = 'customer@example.com' ..card = PaymentCard( number: '4084084084084081', cvc: '408', expiryMonth: 12, expiryYear: 25, ); // New final response = await AllPaystackPayments.initializeCardPayment( amount: 50000, email: 'customer@example.com', cardNumber: '4084084084084081', expiryMonth: '12', expiryYear: '25', cvv: '408', cardHolderName: 'John Doe', );
-
Update Response Handling
// Old if (charge.status == 'success') { // Handle success } // New if (response.isSuccessful) { // Handle success } else { // Handle failure }
Migrating from paystack_manager #
-
Update Dependencies
dependencies: all_paystack_payments: ^1.0.0
-
Update Method Calls
// Old PaystackManager.paystackTransaction(...) // New AllPaystackPayments.initializeCardPayment(...)
Key Differences #
- Unified API: Single plugin for all payment methods
- Better Error Handling: Detailed error messages and codes
- Type Safety: Strongly typed enums and responses
- Cross-Platform: Supports all Flutter platforms
- Modern Async: Uses async/await throughout
API Reference #
For detailed API documentation with comprehensive examples, see USAGE_README.md.
Key Classes #
AllPaystackPayments
- Main plugin class with all payment methodsPaymentResponse
- Payment response model with status and detailsPaystackError
- Error handling class with detailed error informationPaymentRequest
- Base payment request classCardPaymentRequest
- Card payment request modelBankTransferRequest
- Bank transfer request modelMobileMoneyRequest
- Mobile money request model
Main Methods #
initialize(String publicKey)
- Initialize the plugin with Paystack public keyinitializeCardPayment(...)
- Process card paymentsinitializeBankTransfer(...)
- Initiate bank transfer paymentsinitializeMobileMoney(...)
- Process mobile money paymentsinitializePayment(PaymentRequest)
- Process custom payment requestsverifyPayment(String reference)
- Verify payment completiongetPaymentStatus(String reference)
- Check current payment statuscancelPayment(String reference)
- Cancel pending payments
Enums #
Currency
- Supported currencies (NGN, USD, GHS, ZAR, KES)PaymentMethod
- Available payment methods (card, bankTransfer, mobileMoney)PaymentStatus
- Payment status values (pending, success, failed, cancelled)MobileMoneyProvider
- Mobile money providers (mpesa, airtel, vodafone, tigo)BankTransferType
- Bank transfer types (account, otp)
Example App #
Check out the example app for a complete implementation with UI forms for all payment methods.
To run the example:
cd example
flutter run
Screenshot: Example App Payment Options
π± [Screenshot showing the example app with different payment method buttons]
Contributing #
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Support #
- π Documentation
- π Issues
- π¬ Discussions
- π§ Contact: support@xeplas.com
License #
This project is licensed under the MIT License, which allows free use, modification, and distribution with proper attribution. See the LICENSE file for details.
Changelog #
See CHANGELOG.md for version history.
Made with β€οΈ for the Flutter community