PayFast Flutter SDK

πŸš€ Pakistan’s First PayFast Flutter SDK

Pakistan’s first Flutter SDK designed to simplify PayFast payment gateway integration for modern mobile applications.

🎯 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
  • WebView-Based Payments: Secure payment processing via WebView
  • Customizable Configuration: Full control over payment parameters
  • Error Handling: Comprehensive error handling and user feedback
  • Support for Multiple Currencies: Flexible currency support (default: PKR)
  • Deep Linking: Success/failure URL monitoring with callback support
  • Nullable Safety: Fully null-safe Dart code

πŸ“‹ Requirements

  • Flutter SDK: >=3.0.0 <4.0.0
  • Dart SDK: >=3.10.8
  • 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 navigation
  • config: PayFastConfig instance with payment details
  • onResult: Callback function that receives PayFastResult

Flow:

  1. βœ“ Requests access token from PayFast API
  2. βœ“ Opens WebView with payment form
  3. βœ“ Auto-submits form to PayFast
  4. βœ“ Monitors for success/failure redirects
  5. βœ“ 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'),
            ),
          ],
        ),
      );
    }
  },
);

πŸ”’ 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 (requires WebView configuration)
  • ⚠️ Windows (requires WebView configuration)
  • ⚠️ macOS (requires WebView configuration)

πŸ“– Examples

A complete example app is available in the example folder:

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

Issue: Payment page doesn't load

Solution:

  • Check WebView is properly initialized
  • Verify URLs are correct
  • Ensure JavaScript is enabled in WebView
  • Check console for errors

Issue: Success/Failure redirect not triggered

Solution:

  • Verify successUrl and failureUrl are correct
  • Check URLs contain "success" or "failure" keywords
  • Ensure proper URL encoding

πŸ“ Dependencies

  • flutter: sdk
  • http: ^1.2.0
  • webview_flutter: ^4.7.0
  • crypto: ^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

For issues, questions, or suggestions:

πŸŽ“ Learn More


Built with ❀️ for Flutter developers

Libraries

payfast_flutter