ecocash 0.0.1
ecocash: ^0.0.1 copied to clipboard
A comprehensive Dart SDK for integrating Ecocash payment services. Supports instant payments, transaction lookup, and refund processing with functional error handling.
Ecocash Payments SDK #
A comprehensive Dart SDK for integrating Ecocash payment services into your Flutter and Dart applications. This SDK provides seamless access to Ecocash's Customer-to-Business (C2B) instant payments, transaction lookups, and refund processing with functional error handling.
Note: This package is developed in personal capacity and is not officially affiliated with Econet or Ecocash. For official documentation and API access, visit developers.ecocash.co.zw.
Features π #
- π³ Instant Payments: Real-time C2B payment processing
- π Transaction Lookup: Check transaction status and details
- π° Refund Processing: Handle payment refunds seamlessly
- ποΈ Dual Environment: Sandbox and live environment support
- π‘οΈ Type Safety: Built with strong typing and null safety
- π Functional Error Handling: Result types for predictable error handling
- π¦ JSON Serialization: Automatic request/response handling with Freezed
- π Secure: API key authentication with best practices
- π Well Documented: Comprehensive documentation and examples
- π― Flutter Ready: Works seamlessly with Flutter apps
Installation π» #
β In order to start using Ecocash SDK you must have the Dart SDK installed on your machine.
Add this to your package's pubspec.yaml file:
dependencies:
ecocash: ^0.1.0
Then run:
dart pub get
Or install it directly via:
dart pub add ecocash
Getting Started πββοΈ #
1. Get Your API Credentials #
- Visit developers.ecocash.co.zw
- Sign up for a developer account
- Create a new application to get your API key
- Use sandbox mode for testing, live mode for production
2. Initialize the SDK #
import 'package:ecocash/ecocash.dart';
// For development/testing
final ecocash = Ecocash.initialize(
apiKey: 'your-sandbox-api-key',
sandboxMode: true,
);
// For production
final ecocash = Ecocash.initialize(
apiKey: 'your-live-api-key',
sandboxMode: false,
);
Usage Examples π #
Making a Payment #
import 'package:ecocash/ecocash.dart';
Future<void> processPayment() async {
final ecocash = Ecocash.initialize(
apiKey: 'your-api-key',
sandboxMode: true,
);
final request = PaymentRequest(
mobileNumber: '263771234567',
amount: 50.00,
reason: 'Payment for premium subscription',
currency: 'USD',
reference: 'TXN-${DateTime.now().millisecondsSinceEpoch}',
);
final result = await ecocash.makePayment(request);
switch (result) {
case Success(:final data):
print('Payment successful: $data');
// Process successful payment
break;
case Error(:final error):
print('Payment failed: ${error.message}');
print('Status Code: ${error.statusCode}');
// Handle error appropriately
break;
}
}
Transaction Lookup #
Future<void> checkTransactionStatus() async {
final request = LookupRequest(
mobileNumber: '263774222475',
reference: 'TXN-1693574400000',
);
final result = await ecocash.lookupTransaction(request);
switch (result) {
case Success(:final data):
print('Transaction Status: ${data.status}');
print('Amount: ${data.amount.amount} ${data.amount.currency}');
print('Ecocash Reference: ${data.ecocashReference}');
print('Transaction Date: ${data.transactionDateTime}');
break;
case Error(:final error):
print('Lookup failed: ${error.message}');
break;
}
}
Processing a Refund #
Future<void> processRefund() async {
final request = RefundRequest(
originalEcocashTransactionReference: '581af738-f459-4629-a72e-8388e0acdb5e',
refundCorrelator: 'REF-${DateTime.now().millisecondsSinceEpoch}',
sourceMobileNumber: '263774222475',
amount: 25.00,
clientName: 'My Application',
currency: 'USD',
reasonForRefund: 'Customer requested refund',
);
final result = await ecocash.processRefund(request);
switch (result) {
case Success(:final data):
print('Refund Status: ${data.transactionStatus}');
print('Refund Reference: ${data.ecocashReference}');
break;
case Error(:final error):
print('Refund failed: ${error.message}');
break;
}
}
Advanced Usage π§ #
Working with Result Types #
The SDK uses functional error handling with Result<T> types instead of throwing exceptions:
// Pattern matching with switch expressions
final result = await ecocash.makePayment(request);
final message = switch (result) {
Success() => 'Payment completed successfully',
Error(:final error) => 'Payment failed: ${error.message}',
};
// Using helper methods
if (result.isSuccess) {
final success = result.dataOrNull; // Returns data or null
print('Success: $success');
}
if (result.isError) {
final error = result.errorOrNull; // Returns error or null
print('Error: ${error?.message}');
}
// Transform results
final transformedResult = result.map((success) => success.toString());
// Chain operations
final chainedResult = result.flatMap((success) => performAnotherOperation());
// Execute callbacks
result.when(
success: (data) => print('Success: $data'),
error: (error) => print('Error: ${error.message}'),
);
Error Handling #
The SDK provides structured error handling with EcocashException:
final result = await ecocash.makePayment(request);
switch (result) {
case Success(:final data):
// Handle success
break;
case Error(:final error):
switch (error.statusCode) {
case 400:
print('Bad Request: ${error.message}');
// Handle validation errors
break;
case 401:
print('Unauthorized: ${error.message}');
// Handle authentication errors
break;
case 500:
print('Server Error: ${error.message}');
// Handle server errors
break;
default:
print('Unknown Error: ${error.message}');
// Handle other errors
break;
}
break;
}
API Reference π #
Core Classes #
Ecocash
Main SDK class for payment operations.
Factory Constructor:
Ecocash.initialize({required String apiKey, bool sandboxMode = false})
Methods:
Future<Result<bool>> makePayment(PaymentRequest request)Future<Result<LookupResponse>> lookupTransaction(LookupRequest request)Future<Result<RefundResponse>> processRefund(RefundRequest request)
PaymentRequest
PaymentRequest({
required String mobileNumber, // Customer mobile number (e.g., '263771234567')
required double amount, // Payment amount (e.g., 10.50)
required String reason, // Payment description
required String currency, // Currency code ('USD', 'ZWL', 'ZiG')
required String reference, // Unique transaction reference
})
LookupRequest
LookupRequest({
required String mobileNumber, // Source mobile number
required String reference, // Transaction reference to lookup
})
RefundRequest
RefundRequest({
required String originalEcocashTransactionReference, // Original transaction ID
required String refundCorrelator, // Unique refund ID
required String sourceMobileNumber, // Source mobile number
required double amount, // Refund amount
required String clientName, // Your app name
required String currency, // Currency code
required String reasonForRefund, // Reason for refund
})
Result<T>
Functional error handling type with two variants:
Success<T>(T data)- Contains successful resultError<T>(EcocashException error)- Contains error information
Methods:
bool get isSuccessbool get isErrorT? get dataOrNullEcocashException? get errorOrNullResult<R> map<R>(R Function(T) transform)Result<R> flatMap<R>(Result<R> Function(T) transform)R when<R>({required R Function(T data) success, required R Function(EcocashException error) error})
Testing π§ͺ #
Unit Testing #
import 'package:ecocash/ecocash.dart';
import 'package:test/test.dart';
void main() {
group('Ecocash SDK', () {
late Ecocash ecocash;
setUp(() {
ecocash = Ecocash.initialize(
apiKey: 'test-api-key',
sandboxMode: true,
);
});
test('should initialize correctly', () {
expect(ecocash.apiKey, equals('test-api-key'));
expect(ecocash.sandboxMode, isTrue);
});
test('should create payment request', () {
final request = PaymentRequest(
mobileNumber: '263771234567',
amount: 10.00,
reason: 'Test payment',
currency: 'USD',
reference: 'TEST-123',
);
expect(request.mobileNumber, equals('263771234567'));
expect(request.amount, equals(10.00));
});
});
}
Integration Testing #
Use sandbox mode for integration testing:
void main() {
group('Ecocash Integration Tests', () {
final ecocash = Ecocash.initialize(
apiKey: 'your-sandbox-api-key',
sandboxMode: true,
);
test('should process payment successfully', () async {
final request = PaymentRequest(
mobileNumber: '263771234567',
amount: 1.00,
reason: 'Integration test',
currency: 'USD',
reference: 'TEST-${DateTime.now().millisecondsSinceEpoch}',
);
final result = await ecocash.makePayment(request);
expect(result.isSuccess, isTrue);
});
});
}
Best Practices π‘ #
Security #
- Never expose API keys in client-side code or version control
- Use environment variables for API key storage
- Validate inputs before sending requests
- Use HTTPS in production environments
Error Handling #
- Always handle both success and error cases when using Result types
- Log errors appropriately for debugging and monitoring
- Provide meaningful error messages to users
- Implement retry logic for network failures
Performance #
- Cache SDK instances instead of creating new ones
- Use connection pooling for multiple requests
- Implement request timeouts to prevent hanging requests
- Monitor API usage to stay within rate limits
Example: Secure Implementation #
class PaymentService {
late final Ecocash _ecocash;
PaymentService({required String apiKey, bool sandboxMode = false}) {
_ecocash = Ecocash.initialize(
apiKey: apiKey,
sandboxMode: sandboxMode,
);
}
Future<PaymentResult> processPayment({
required String mobileNumber,
required double amount,
required String reason,
required String currency,
}) async {
// Validate inputs
if (amount <= 0) {
return PaymentResult.failure('Invalid amount');
}
if (!isValidMobileNumber(mobileNumber)) {
return PaymentResult.failure('Invalid mobile number');
}
// Generate unique reference
final reference = 'TXN-${DateTime.now().millisecondsSinceEpoch}';
final request = PaymentRequest(
mobileNumber: mobileNumber,
amount: amount,
reason: reason,
currency: currency,
reference: reference,
);
final result = await _ecocash.makePayment(request);
return switch (result) {
Success(:final data) => PaymentResult.success(reference, data),
Error(:final error) => PaymentResult.failure(error.message),
};
}
bool isValidMobileNumber(String number) {
// Implement mobile number validation
return RegExp(r'^263\d{9}$').hasMatch(number);
}
}
Supported Currencies π° #
- USD - United States Dollar
- ZWL - Zimbabwe Dollar
- ZiG - Zimbabwe Gold
Environment Configuration π #
Sandbox Environment #
- Purpose: Testing and development
- Base URL:
https://developers.ecocash.co.zw/api/ecocash_pay - Use sandbox API keys obtained from the developer portal
Live Environment #
- Purpose: Production transactions
- Base URL:
https://developers.ecocash.co.zw/api/ecocash_pay - Use live API keys obtained from the developer portal
- Real money transactions
Contributing π€ #
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please make sure to update tests as appropriate and follow the existing code style.
License π #
This project is licensed under the MIT License - see the LICENSE file for details.
Support π #
- Developer Portal: developers.ecocash.co.zw
- Issues: GitHub Issues
- Documentation: This README and inline code documentation
Changelog π #
See CHANGELOG.md for a detailed list of changes and versions.
Disclaimer β οΈ #
This package is developed in personal capacity and is not officially affiliated with, endorsed by, or connected to Econet Wireless Zimbabwe or Ecocash. It is an independent implementation based on publicly available APIs and documentation.
For official support, terms of service, and API documentation, please visit developers.ecocash.co.zw.
Made with β€οΈ for the Zimbabwean developer community