PayWidget
Overview
PayWidget is a drop-in Flutter widget that embeds the FedaPay payment page. It handles transaction creation (optional), URL construction, and payment callbacks automatically. It works across Mobile (WebView) and Web/Desktop (Browser redirect).
Usage
Simple Usage (Token-based)
If you already have a transaction token from your backend:
PayWidget(
transactionToken: 'ts_token_xxx',
onPaymentSuccess: () => print('Success'),
onPaymentFailed: () => print('Failed'),
)
Advanced Usage (Client-side creation)
If you want the widget to create the transaction for you:
PayWidget(
instance: FedaFlutter.instance,
amount: 2000,
description: 'Order #123',
customer: {'email': 'test@example.com'},
onPaymentSuccess: () => print('Success'),
onPaymentFailed: () => print('Failed'),
)
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
onPaymentSuccess | VoidCallback | ✅ | Called on successful payment |
onPaymentFailed | VoidCallback | ✅ | Called on failed payment |
transactionToken | String? | ❌ | FedaPay transaction token (skips creation) |
instance | FedaFlutter? | ❌ | SDK instance (required for client-side creation) |
amount | int? | ❌ | Amount in local currency subunit |
currency | CurrencyIso? | ❌ | Currency (defaults to XOF) |
customer | Map<String, dynamic>? | ❌ | Customer data (email, firstname, etc.) |
- Mobile (Android/iOS): Opens the FedaPay checkout in a secure embedded WebView. It automatically detects redirection to
/status/successor/status/failure. - Web & Desktop: Displays a "Pay" button that opens the checkout in a new browser tab/window. To ensure the application is notified of the payment status, a polling mechanism is used.
!NOTE For polling to work on Desktop, you must provide the
instance(or use the client-side creation flow). If you only provide atransactionToken, the application won't be able to automatically poll for the status.
How it works
- If
transactionTokenis provided, it constructs the URL:https://checkout.fedapay.com/pay/{token}. - If not, it uses the provided
instanceto create a transaction via the API first. - On Mobile, it loads the URL in a
WebViewand monitors URL changes. - On Web/Desktop, it uses
url_launcherto redirect the user and starts polling the transaction status every 3 seconds until the payment is approved, canceled, or declined. - Once the status is confirmed by the API, the corresponding
onPaymentSuccessoronPaymentFailedcallback is triggered.
Cloud Proxy Example (Recommended for Production)
This is the most secure way to integrate FedaPay. Your secret API keys are safely stored on your backend (e.g. using ashgateway), and the mobile app communicates via a secure proxy.
import 'package:feda_flutter/feda_flutter.dart';
import 'package:flutter/material.dart';
void main() {
// Initialize with your public project key and your cloud proxy URL
FedaFlutter.applyCloudConfig(
projectKey: 'your_public_project_key',
cloudUrl: 'https://your-ashgateway-instance.com',
);
runApp(const MyApp());
}
Direct API Example (Testing & Prototyping)
Here is a concrete example of a checkout page using the simplified PayWidget. This example shows how to handle the payment state and show success/failure feedback using a SnackBar.
import 'package:feda_flutter/feda_flutter.dart';
import 'package:flutter/material.dart';
void main() {
FedaFlutter.applyConfig(
environment: ApiEnvironment.sandbox,
apiKey: 'test_api_key_1234567890abcdef',
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Checkout Demo',
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple)),
home: const MyHomePage(title: 'FedaPay Checkout'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _showPayment = false;
void _startPayment() {
setState(() {
_showPayment = true;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: _showPayment
? PayWidget(
instance: FedaFlutter.instance,
amount: 5000,
description: 'Demo payment from Lab App',
customer: const {
'email': 'customer@example.com',
'firstname': 'John',
'lastname': 'Doe',
},
onPaymentSuccess: () {
setState(() => _showPayment = false);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Paiement réussi !')),
);
},
onPaymentFailed: () {
setState(() => _showPayment = false);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Paiement échoué.')),
);
},
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.shopping_cart_outlined,
size: 100,
color: Colors.grey,
),
const SizedBox(height: 20),
const Text(
'Votre panier est prêt',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const Text('Total: 5000 FCFA'),
const SizedBox(height: 30),
ElevatedButton.icon(
onPressed: _startPayment,
icon: const Icon(Icons.payment),
label: const Text('Procéder au paiement'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
),
),
],
),
),
);
}
}