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
Libraries
- all_paystack_payments
- all_paystack_payments_method_channel
- all_paystack_payments_platform_interface
- all_paystack_payments_web
- bank_transfer_request
- card_payment_request
- enums
- mobile_money_request
- payment_request
- payment_response
- paystack_error
- validation_utils
- webview/android_webview_payment_handler
- webview/ios_webview_payment_handler
- webview/linux_webview_payment_handler
- webview/macos_webview_payment_handler
- webview/web_webview_payment_handler
- webview/windows_webview_payment_handler
- webview_payment_handler