payfast_flutter 0.0.4 copy "payfast_flutter: ^0.0.4" to clipboard
payfast_flutter: ^0.0.4 copied to clipboard

Flutter SDK for PayFast payment gateway integration with Android, iOS, and Web support.

example/lib/main.dart

import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:payfast_flutter/payfast_flutter.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomeScreen(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  static final Uri _webTokenServerUri = Uri.parse(
    'http://localhost:8080/api/payfast/access-token',
  );

  PayFastResult? _lastResult;
  String _paymentStatus = 'Ready to pay';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('PayFast SDK Demo'),
        elevation: 0,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Card(
              elevation: 2,
              child: Padding(
                padding: const EdgeInsets.all(20.0),
                child: Column(
                  children: [
                    Text(
                      'Payment Status',
                      style: Theme.of(context).textTheme.titleLarge,
                    ),
                    const SizedBox(height: 12),
                    Text(
                      _paymentStatus,
                      style: Theme.of(context).textTheme.bodyMedium,
                      textAlign: TextAlign.center,
                    ),
                    if (_lastResult != null) ...[
                      const SizedBox(height: 16),
                      Container(
                        padding: const EdgeInsets.all(12),
                        decoration: BoxDecoration(
                          color: _lastResult!.success
                              ? Colors.green[100]
                              : Colors.red[100],
                          borderRadius: BorderRadius.circular(8),
                          border: Border.all(
                            color: _lastResult!.success
                                ? Colors.green
                                : Colors.red,
                          ),
                        ),
                        child: Column(
                          children: [
                            Text(
                              _lastResult!.success
                                  ? '✓ Payment Successful'
                                  : '✗ Payment Failed',
                              style: TextStyle(
                                fontSize: 16,
                                fontWeight: FontWeight.bold,
                                color: _lastResult!.success
                                    ? Colors.green[700]
                                    : Colors.red[700],
                              ),
                            ),
                            const SizedBox(height: 8),
                            Text(
                              _lastResult!.message,
                              textAlign: TextAlign.center,
                              style: const TextStyle(fontSize: 14),
                            ),
                            if (_lastResult!.transactionId != null) ...[
                              const SizedBox(height: 8),
                              Text(
                                'Transaction ID: ${_lastResult!.transactionId}',
                                style: const TextStyle(
                                  fontSize: 12,
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                            ],
                          ],
                        ),
                      ),
                    ],
                  ],
                ),
              ),
            ),
            const SizedBox(height: 32),
            ElevatedButton.icon(
              onPressed: () => _processPayment(
                context,
                amount: '10.00',
                description: 'Premium Package',
              ),
              icon: const Icon(Icons.payment),
              label: const Text('Pay 10 PKR'),
              style: ElevatedButton.styleFrom(
                padding: const EdgeInsets.symmetric(vertical: 16),
              ),
            ),
            const SizedBox(height: 12),
            ElevatedButton.icon(
              onPressed: () => _processPayment(
                context,
                amount: '500.00',
                description: 'Deluxe Package',
              ),
              icon: const Icon(Icons.payment),
              label: const Text('Pay 500 PKR'),
              style: ElevatedButton.styleFrom(
                padding: const EdgeInsets.symmetric(vertical: 16),
              ),
            ),
            const SizedBox(height: 12),
            ElevatedButton.icon(
              onPressed: () => _processPayment(
                context,
                amount: '1000.00',
                description: 'Enterprise Package',
              ),
              icon: const Icon(Icons.payment),
              label: const Text('Pay 1000 PKR'),
              style: ElevatedButton.styleFrom(
                padding: const EdgeInsets.symmetric(vertical: 16),
              ),
            ),
            const SizedBox(height: 32),
            Card(
              color: Colors.blue[50],
              child: Padding(
                padding: const EdgeInsets.all(12.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Demo Credentials',
                      style: Theme.of(context).textTheme.titleSmall,
                    ),
                    const SizedBox(height: 8),
                    Text(
                      kIsWeb
                          ? 'Merchant ID: YOUR_MERCHANT_ID\n'
                              'Mode: Sandbox\n'
                              'Web: run `dart run tool/payfast_token_server.dart` first.\n'
                              'Web: success/failure URLs must use the same origin as this app.\n'
                              'Token Proxy: $_webTokenServerUri\n'
                              'Replace the placeholders below with your own credentials.'
                          : 'Merchant ID: YOUR_MERCHANT_ID\n'
                              'Mode: Sandbox\n'
                              'Replace the placeholders below with your own credentials.',
                      style: const TextStyle(fontSize: 12),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Future<String?> _fetchWebAccessToken(PayFastConfig config) async {
    try {
      final response = await http.post(
        _webTokenServerUri,
        headers: const {
          'Content-Type': 'application/json',
        },
        body: jsonEncode({
          'merchantId': config.merchantId,
          'securedKey': config.securedKey,
          'basketId': config.basketId,
          'amount': config.amount,
          'currencyCode': config.currency,
        }),
      );

      if (response.statusCode != 200) {
        debugPrint(
          'Web token proxy failed: ${response.statusCode} ${response.body}',
        );
        return null;
      }

      final data = jsonDecode(response.body);
      if (data is! Map<String, dynamic>) {
        return null;
      }

      final token = data['accessToken'] ?? data['ACCESS_TOKEN'];
      return token is String && token.trim().isNotEmpty ? token.trim() : null;
    } catch (e) {
      debugPrint('Web token proxy error: $e');
      return null;
    }
  }

  void _processPayment(
    BuildContext context, {
    required String amount,
    required String description,
  }) {
    if (!context.mounted) {
      return;
    }

    final successUrl = kIsWeb
        ? Uri.base.resolve('/payfast-success').toString()
        : 'https://example.com/success';
    final failureUrl = kIsWeb
        ? Uri.base.resolve('/payfast-failure').toString()
        : 'https://example.com/failure';
    final checkoutUrl = kIsWeb
        ? Uri.base.resolve('/payfast-ipn').toString()
        : 'https://example.com/ipn';

    final config = PayFastConfig(
      merchantId: '241665',
      securedKey: 'tDy0TwuynmeiJP3FiVg-YAOC',
      basketId: 'ORD-${DateTime.now().millisecondsSinceEpoch}',
      amount: amount,
      currency: 'PKR',
      txnDesc: description,
      customerEmail: 'customer@example.com',
      customerMobile: '03001234567',
      successUrl: successUrl,
      failureUrl: failureUrl,
      checkoutUrl: checkoutUrl,
      environment: 'sandbox',
    );

    setState(() {
      _paymentStatus = kIsWeb
          ? 'Requesting secure web access token for $amount PKR...'
          : 'Processing payment for $amount PKR...';
    });

    PayFast.startPayment(
      context: context,
      config: config,
      accessTokenProvider: kIsWeb ? _fetchWebAccessToken : null,
      onResult: (result) {
        if (!mounted) {
          return;
        }

        setState(() {
          _lastResult = result;
          _paymentStatus =
              result.success ? 'Payment Completed' : 'Payment Failed';
        });

        debugPrint('Payment Result: ${result.success}');
        debugPrint('Message: ${result.message}');
        debugPrint('Transaction ID: ${result.transactionId}');
      },
    );
  }
}
6
likes
145
points
148
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter SDK for PayFast payment gateway integration with Android, iOS, and Web support.

Repository (GitHub)
View/report issues

Topics

#payfast #payment #flutter-payment #pakistan-payment

Documentation

API reference

License

unknown (license)

Dependencies

crypto, flutter, http, webview_flutter

More

Packages that depend on payfast_flutter