A production-ready Dart Frog server showcasing best practices for using the MindPaystack SDK. This implementation leverages the SDK's excellent built-in features like automatic mapping, comprehensive error handling, and structured validation.
- Structured Options: Uses
InitializeTransactionOptions,CreateChargeOptions, etc. - Automatic Validation: SDK handles all input validation internally
- Built-in Serialization: Uses
.toJson()methods for consistent responses - Comprehensive Error Handling: Leverages
MindExceptionwith detailed error information
- No Manual Validation: SDK's options classes handle validation
- Consistent Error Responses: MindException provides standardized error format
- Type Safety: SDK's structured classes ensure type correctness
- Minimal Boilerplate: Let the SDK do the heavy lifting
- π Secure Configuration: SDK initialization from environment variables only
- π SDK-First Approach: Uses all SDK built-in features (validation, serialization, error handling)
- π‘οΈ Comprehensive Error Handling: Leverages MindException's rich error details
- π All Payment Methods: Cards, bank transfers, USSD, mobile money, saved cards
- π Complete Transaction Management: Initialize, verify, list, timeline, export
- π Type Safety: Uses SDK's structured options classes
Create .env file:
# Required
PAYSTACK_PUBLIC_KEY=pk_test_your_public_key_here
PAYSTACK_SECRET_KEY=sk_test_your_secret_key_here
# Optional
PAYSTACK_ENVIRONMENT=test
PAYSTACK_LOG_LEVEL=infodart pub get
dart_frog devThe server uses the SDK's chargeCard convenience method:
curl -X POST http://localhost:8080/charge \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"amount": "50000",
"payment_method": "card",
"card": {
"number": "4084084084084081",
"cvv": "123",
"expiry_month": "12",
"expiry_year": "2025"
},
"pin": "1234"
}'Uses SDK's chargeBankTransfer method:
curl -X POST http://localhost:8080/charge \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"amount": "50000",
"payment_method": "bank_transfer"
}'Uses SDK's chargeSavedCard method:
curl -X POST http://localhost:8080/charge \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"amount": "50000",
"payment_method": "saved_card",
"authorization_code": "AUTH_abc123def"
}'Uses structured SubmitPinOptions:
curl -X POST http://localhost:8080/charge/submit/pin \
-H "Content-Type: application/json" \
-d '{
"reference": "transaction_reference",
"pin": "1234"
}'Uses InitializeTransactionOptions with full validation:
curl -X POST http://localhost:8080/transaction \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"amount": "50000",
"callback_url": "https://your-site.com/callback",
"channels": ["card", "bank", "ussd"],
"metadata": {
"order_id": "12345"
}
}'Simple and clean with SDK handling everything:
curl http://localhost:8080/transaction/verify/your_transaction_reference// DON'T DO THIS
Future<Response> _createCharge(RequestContext context) async {
final body = await context.request.json() as Map<String, dynamic>;
// Manual validation (unnecessary!)
final email = body['email'] as String?;
if (email == null || !email.contains('@')) {
return Response.json(statusCode: 400, body: {'error': 'Invalid email'});
}
// Manual error handling (reinventing the wheel!)
try {
final card = Card(/*...*/);
final result = await sdk.charge.chargeCard(/*...*/);
return Response.json(body: {
'status': result.status,
'message': result.message,
'data': result.data?.toJson(), // Manual serialization
});
} catch (e) {
return Response.json(statusCode: 500, body: {'error': e.toString()});
}
}// DO THIS - Clean, simple, and leverages all SDK features!
Future<Response> _createCharge(RequestContext context) async {
try {
final body = await context.request.json() as Map<String, dynamic>;
// Use SDK's convenience methods - they handle validation!
final email = body['email'] as String;
final amount = body['amount'] as String;
final card = Card(/*...*/);
final result = await MindPaystack.instance.charge.chargeCard(
email: email,
amount: amount,
card: card,
);
// SDK's built-in serialization handles everything!
return Response.json(
statusCode: result.status ? 200 : 400,
body: result.toJson(), // β¨ Perfect serialization
);
} on MindException catch (e) {
// SDK's comprehensive error handling!
return Response.json(
statusCode: 400,
body: e.toJson(), // β¨ Complete error details
);
}
}All responses use the SDK's built-in serialization:
{
"status": true,
"message": "Charge created successfully",
"data": {
"id": 123456789,
"reference": "tx_abc123",
"amount": 50000,
"currency": "NGN",
"status": "success",
// ... complete transaction details from SDK
}
}{
"message": "Invalid card number",
"code": "invalid_card_number",
"category": "validation",
"severity": "error",
"technicalMessage": "Card number fails Luhn algorithm check",
"validationErrors": [
{
"field": "card.number",
"message": "Invalid card number format"
}
]
}Instead of manual charge creation, use:
sdk.charge.chargeCard()sdk.charge.chargeBankTransfer()sdk.charge.chargeSavedCard()
Use SDK's option classes:
InitializeTransactionOptionsSubmitPinOptionsListTransactionsOptions
MindException provides:
- Structured error codes
- Validation details
- Technical messages
- User-friendly messages
Always use .toJson() methods:
result.toJson()for responsesexception.toJson()for errors
- Less Code: SDK handles validation, serialization, error formatting
- Better Errors: Rich error details from MindException
- Type Safety: SDK's structured classes prevent runtime errors
- Consistency: All responses use SDK's standardized format
- Maintainability: Changes in SDK automatically benefit your server
- Testing: SDK's structured approach makes testing easier
The server is designed for production with:
- Environment-based configuration
- Comprehensive error handling via SDK
- Type-safe request handling
- Consistent response formatting
- Zero hardcoded credentials
This implementation demonstrates:
- Best practices for SDK usage
- Production-ready patterns
- Clean architecture leveraging existing SDK features
- Comprehensive examples for all payment methods
The key insight: Don't reinvent what the SDK already does perfectly! π