payfast 1.0.2-pre.1 copy "payfast: ^1.0.2-pre.1" to clipboard
payfast: ^1.0.2-pre.1 copied to clipboard

A Flutter package to integrate Payfast payments into your app.

example/lib/main.dart

import 'dart:math';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:payfast/payfast.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Payfast Widget Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
  }

  String _randomId() {
    var rng = Random();
    var randomNumber = rng.nextInt(900000) + 100000;

    return '$randomNumber';
  }

  void paymentCompleted(Map<String, dynamic> data) {
    print(data);

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
          content: Text('Payment Successful!'),
          behavior: SnackBarBehavior.floating),
    );

    Navigator.push(
      context,
      CupertinoPageRoute(builder: (context) => const MyApp()),
    );
  }

  void paymentCancelled() {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text('Payment Cancelled!'),
        behavior: SnackBarBehavior.floating,
        backgroundColor: Colors.red,
      ),
    );

    Navigator.push(
      context,
      CupertinoPageRoute(builder: (context) => const MyApp()),
    );
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.

    return Material(
        child: Scaffold(
      body: SafeArea(
        child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: PayFast(
              data: {
                'merchant_id': '0000000',
                'merchant_key': 'xxxxxxxxx',
                'name_first': 'Yung',
                'name_last': 'Cet',
                'email_address': '[email protected]',
                'm_payment_id':
                    DateTime.now().millisecondsSinceEpoch.toString(),
                'amount': '20',
                'item_name': 'Subscription',
              },
              passPhrase: 'xxxxxxx',
              useSandBox: true,
              onsiteActivationScriptUrl:
                  'https://youngcet.github.io/sandbox_payfast_onsite_payments/',
              onPaymentCancelled: paymentCancelled,
              onPaymentCompleted: paymentCompleted,
            )),
      ),
    ));
  }
}

class CustomPaymentSummary extends StatelessWidget {
  final Map<String, dynamic> data;
  final VoidCallback processPayment;

  const CustomPaymentSummary({
    Key? key,
    required this.data,
    required this.processPayment,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return SafeArea(
      bottom: false,
      child: Scaffold(
        backgroundColor: Colors.white,
        body: Column(
          children: [
            // Scrollable content
            Expanded(
              child: SingleChildScrollView(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: [
                    // Header
                    Row(
                      children: [
                        Container(
                          width: 44,
                          height: 44,
                          decoration: BoxDecoration(
                            shape: BoxShape.circle,
                            color: Colors.grey.shade100,
                          ),
                          child: const Icon(Icons.receipt_long, size: 20),
                        ),
                        const SizedBox(width: 12),
                        Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              'Order summary',
                              style: theme.textTheme.titleMedium
                                  ?.copyWith(fontWeight: FontWeight.w600),
                            ),
                            const SizedBox(height: 2),
                            Text(
                              'Review & confirm your purchase',
                              style: theme.textTheme.bodySmall,
                            ),
                          ],
                        )
                      ],
                    ),
                    const SizedBox(height: 18),

                    // Summary card
                    Container(
                      padding: const EdgeInsets.all(12),
                      decoration: BoxDecoration(
                        color: Colors.white,
                        border: Border.all(color: Colors.grey.shade200),
                        borderRadius: BorderRadius.circular(8),
                      ),
                      child: Column(
                        children: [
                          Row(
                            children: [
                              Expanded(
                                child: Text(
                                  data['item_name'],
                                  style: theme.textTheme.bodyMedium
                                      ?.copyWith(fontWeight: FontWeight.w500),
                                ),
                              ),
                              Text(
                                'R${data['amount']}',
                                style: theme.textTheme.bodyMedium
                                    ?.copyWith(fontWeight: FontWeight.w700),
                              ),
                            ],
                          ),
                          const SizedBox(height: 12),
                          Container(height: 1, color: Colors.grey.shade100),
                          const SizedBox(height: 12),
                          Row(
                            children: [
                              const Icon(Icons.person_outline, size: 16),
                              const SizedBox(width: 8),
                              Flexible(
                                child: Text(
                                  data['email_address'],
                                  style: theme.textTheme.bodySmall,
                                  overflow: TextOverflow.ellipsis,
                                ),
                              ),
                            ],
                          ),
                        ],
                      ),
                    ),
                    const SizedBox(height: 18),

                    // Optional extra content can go here
                  ],
                ),
              ),
            ),

            // Pay Now button + footer
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                mainAxisSize: MainAxisSize.min,
                children: [
                  ElevatedButton(
                    onPressed: processPayment,
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(8),
                      ),
                    ),
                    child:
                        const Text('Pay Now', style: TextStyle(fontSize: 16)),
                  ),
                  const SizedBox(height: 8),
                  Text(
                    'Secure payment processed by PayFast',
                    textAlign: TextAlign.center,
                    style: theme.textTheme.bodySmall,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}