PayFast Flutter SDK
π Pakistanβs First PayFast Flutter SDK
Pakistanβs first Flutter SDK designed to simplify PayFast payment gateway integration for modern Flutter apps on Android, iOS, and Web.
π― Features
- Production-Ready: Fully tested and production-ready SDK
- Clean API: Simple, Stripe-like interface for easy integration
- Secure Token Authentication: SHA256 signature generation for secure transactions
- Cross-Platform Checkout: WebView on Android/iOS and HTML form POST checkout on Flutter Web
- Customizable Configuration: Full control over payment parameters
- Error Handling: Comprehensive error handling and user feedback
- Support for Multiple Currencies: Flexible currency support (default: PKR)
- Redirect Monitoring: Success/failure URL monitoring with callback support
- Nullable Safety: Fully null-safe Dart code
π Requirements
- Flutter SDK:
>=3.10.0 - Dart SDK:
>=3.0.0 <4.0.0 - Active PayFast merchant account
π¦ Installation
Add this to your pubspec.yaml:
dependencies:
payfast_flutter:
path: ../ # For local development
Then run:
flutter pub get
π Quick Start
Basic Implementation (3 steps)
import 'package:flutter/material.dart';
import 'package:payfast_flutter/payfast_flutter.dart';
class CheckoutPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Checkout')),
body: Center(
child: ElevatedButton(
onPressed: () => _startPayment(context),
child: Text('Pay 100 PKR'),
),
),
);
}
void _startPayment(BuildContext context) {
final config = PayFastConfig(
merchantId: "XXXX",
securedKey: "XXXX",
basketId: "ORDER123",
amount: "100",
successUrl: "https://example.com/success",
failureUrl: "https://example.com/failure",
checkoutUrl: "https://example.com/ipn",
);
PayFast.startPayment(
context: context,
config: config,
onResult: (result) {
if (result.success) {
print('β Payment successful!');
print('Transaction ID: ${result.transactionId}');
} else {
print('β Payment failed: ${result.message}');
}
},
);
}
}
π API Reference
PayFastConfig
Configuration class for payment parameters.
Required Parameters
PayFastConfig(
merchantId: "241665", // Your PayFast merchant ID
securedKey: "XXXX", // Your secure key
basketId: "ORDER123", // Unique order ID
amount: "100.00", // Amount in decimal format
successUrl: "https://...", // Redirect on success
failureUrl: "https://...", // Redirect on failure
checkoutUrl: "https://...", // IPN endpoint
)
Optional Parameters
PayFastConfig(
// ... required params ...
currency: "PKR", // Default: "PKR"
txnDesc: "Product Purchase", // Default: "Payment Transaction"
customerEmail: "user@example.com",
customerMobile: "03001234567",
environment: "sandbox", // Default: "sandbox"
additionalDescription: "Order details", // Default: "Flutter Payment"
)
Available Properties
| Property | Type | Default | Description |
|---|---|---|---|
merchantId |
String | Required | PayFast merchant ID |
securedKey |
String | Required | Secure key for authentication |
basketId |
String | Required | Unique transaction ID |
amount |
String | Required | Payment amount (decimal) |
successUrl |
String | Required | Success redirect URL |
failureUrl |
String | Required | Failure redirect URL |
checkoutUrl |
String | Required | IPN checkout URL |
currency |
String | "PKR" | Currency code |
txnDesc |
String | "Payment Transaction" | Transaction description |
customerEmail |
String? | null | Customer email (optional) |
customerMobile |
String? | null | Customer phone (optional) |
environment |
String | "sandbox" | "sandbox" or "live" |
additionalDescription |
String | "Flutter Payment" | Additional description |
PayFast
Main SDK class for initiating payments.
startPayment()
Initiates a complete payment flow.
PayFast.startPayment({
required BuildContext context,
required PayFastConfig config,
required Function(PayFastResult result) onResult,
});
Parameters:
context: BuildContext for navigationconfig: PayFastConfig instance with payment detailsonResult: Callback function that receives PayFastResult
Flow:
- β Requests access token from PayFast API
- β Opens WebView on Android/iOS or a secure popup checkout on Web
- β Auto-submits form to PayFast
- β Monitors for success/failure redirects
- β Returns result via callback
PayFastResult
Result model returned from payment callback.
class PayFastResult {
final bool success; // Payment successful?
final String message; // Status message
final String? transactionId; // Transaction ID (if successful)
}
Example:
(result) {
if (result.success) {
print('Transaction: ${result.transactionId}');
print('Message: ${result.message}');
}
}
PayFastService
Low-level service for API communications.
getAccessToken()
Requests an access token from PayFast API.
final token = await PayFastService.getAccessToken(
config: payFastConfig,
);
PayFastSignature
Utility for generating SHA256 signatures.
final signature = PayFastSignature.generate(
securedKey: "XXXX",
basketId: "ORDER123",
amount: "100.00",
);
π‘ Advanced Usage
Complete Example
import 'package:flutter/material.dart';
import 'package:payfast_flutter/payfast_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PayFast SDK Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: PaymentDemo(),
);
}
}
class PaymentDemo extends StatefulWidget {
@override
State<PaymentDemo> createState() => _PaymentDemoState();
}
class _PaymentDemoState extends State<PaymentDemo> {
String _paymentStatus = "Ready";
PayFastResult? _lastResult;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('PayFast Payment Demo')),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Status Display
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text('Payment Status', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Text(_paymentStatus),
if (_lastResult != null) ...[
SizedBox(height: 16),
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: _lastResult!.success ? Colors.green[100] : Colors.red[100],
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
_lastResult!.success ? 'β Success' : 'β Failed',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _lastResult!.success ? Colors.green : Colors.red,
),
),
SizedBox(height: 8),
Text(_lastResult!.message),
if (_lastResult!.transactionId != null) ...[
SizedBox(height: 8),
Text('ID: ${_lastResult!.transactionId}', style: TextStyle(fontSize: 12)),
]
],
),
),
]
],
),
),
),
SizedBox(height: 24),
// Payment Button
ElevatedButton.icon(
onPressed: () => _initiatePayment(),
icon: Icon(Icons.payment),
label: Text('Start Payment'),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16),
),
),
],
),
),
);
}
void _initiatePayment() {
final config = PayFastConfig(
merchantId: "241665",
securedKey: "XXXX",
basketId: "ORD-${DateTime.now().millisecondsSinceEpoch}",
amount: "100.00",
successUrl: "https://example.com/success",
failureUrl: "https://example.com/failure",
checkoutUrl: "https://example.com/ipn",
currency: "PKR",
txnDesc: "Product Purchase",
customerEmail: "customer@example.com",
customerMobile: "03001234567",
);
setState(() => _paymentStatus = "Processing...");
PayFast.startPayment(
context: context,
config: config,
onResult: (result) {
setState(() {
_lastResult = result;
_paymentStatus = result.success ? "Completed" : "Failed";
});
// Show snackbar
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(result.message),
backgroundColor: result.success ? Colors.green : Colors.red,
),
);
},
);
}
}
Dynamic Amount Calculation
String calculateAmount(List<Item> items) {
double total = items.fold(0, (sum, item) => sum + item.price);
return total.toStringAsFixed(2);
}
final config = PayFastConfig(
// ... other params ...
amount: calculateAmount(cartItems),
);
Custom Error Handling
PayFast.startPayment(
context: context,
config: config,
onResult: (result) {
if (!result.success) {
// Show custom error dialog
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Payment Failed'),
content: Text(result.message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Retry'),
),
],
),
);
}
},
);
π Flutter Web Notes
Flutter Web is supported through a real HTML form submission to PayFast:
- The SDK opens a popup window on user interaction.
- It creates a hidden HTML form and
POSTs the payment fields to PayFast. - The popup is polled until PayFast redirects back to your app.
- The PayFast access token should be generated on your backend and supplied through
accessTokenProvider.
Important web requirements
For Flutter Web:
successUrlandfailureUrlmust use the same origin as your web app.- Use
accessTokenProviderto fetch the PayFast access token from your backend. - Do not expose your PayFast
securedKeydirectly in browser requests in production.
Example when running locally:
final successUrl = Uri.base.resolve('/payfast-success').toString();
final failureUrl = Uri.base.resolve('/payfast-failure').toString();
final checkoutUrl = Uri.base.resolve('/payfast-ipn').toString();
PayFast.startPayment(
context: context,
config: config,
accessTokenProvider: (config) async {
final response = await http.post(
Uri.parse('http://localhost:8080/api/payfast/access-token'),
headers: const {'Content-Type': 'application/json'},
body: jsonEncode({
'merchantId': config.merchantId,
'securedKey': config.securedKey,
'basketId': config.basketId,
'amount': config.amount,
'currencyCode': config.currency,
}),
);
final data = jsonDecode(response.body) as Map<String, dynamic>;
return data['accessToken'] as String?;
},
onResult: (result) {
// handle result
},
);
If you use a different redirect domain on web, the browser will block the SDK from safely detecting the final redirect.
π Security
- β SHA256 signature generation for request verification
- β Token-based authentication
- β Support for sandbox and live environments
- β HTTPS-only communication
- β No sensitive data stored locally
- β Null-safe Dart code
π Supported Currencies
Currently supported currencies:
- PKR (Pakistani Rupee) - Default
More currencies can be added via configuration.
π± Platform Support
- β Android
- β iOS
- β Web
- β οΈ Windows: not implemented
- β οΈ macOS: not implemented
π Examples
A complete example app is available in the example folder.
For Flutter Web, start the local token proxy first:
cd example
dart run tool/payfast_token_server.dart
Then, in a second terminal:
cd example
flutter run -d chrome
For mobile:
cd example
flutter run
π Troubleshooting
Issue: "Failed to get access token"
Solution: Check that your credentials are correct:
- Verify
merchantId - Verify
securedKey - Ensure internet connectivity
- Test in sandbox mode first
- On Flutter Web, verify your backend
accessTokenProvideror token proxy is running and returningACCESS_TOKEN
Issue: Payment page doesn't load
Solution:
- On Android/iOS, check WebView initialization and connectivity
- On Web, make sure the browser allowed the payment popup
- Verify the PayFast endpoint and token are valid
- Check the browser console or app logs for errors
Issue: Success/Failure redirect not triggered
Solution:
- Verify
successUrlandfailureUrlare correct - On Flutter Web, ensure both URLs use the same origin as
Uri.base - Keep the redirect routes reachable by your app
- Ensure proper URL encoding
π Dependencies
flutter: sdkhttp: ^1.2.0webview_flutter: ^4.7.0crypto: ^3.0.3
π API Endpoints
| Purpose | Endpoint |
|---|---|
| Get Token | https://ipg1.apps.net.pk/Ecommerce/api/Transaction/GetAccessToken |
| Process Payment | https://ipg1.apps.net.pk/Ecommerce/api/Transaction/PostTransaction |
π License
MIT License - See LICENSE file for details
Copyright (c) 2026 Abdullah Ghaffar
π€ Contributing
Contributions are welcome! Please feel free to submit pull requests.
π¬ Support
If this package helps you and you'd like to say thanks, or if you want to work with us on a custom project, here are the best ways to connect:
- π GitHub Issues: https://github.com/abdullahghaffar4445/payfast_flutter/issues
- β Buy me a coffee / support this package:
YOUR_COFFEE_URL - πΌ Project inquiries on WhatsApp:
https://wa.me/923271774145 - π³ JazzCash:
03271774145 - π¬ GitHub Discussions: https://github.com/abdullahghaffar4445/payfast_flutter/discussions
Replace
YOUR_COFFEE_URLwith your real public support link before publishing.
π Learn More
Built with β€οΈ for Flutter developers