Guide
Flutter guide for running the YKNPG transaction flow from a mobile app (development demo).
Flutter guide for running the YKNPG transaction flow from a mobile app (development demo).
Security note
- Store the bearer token securely (e.g., remote config or a backend proxy). Using
.envin-app is for local testing.
Prerequisites
- Flutter SDK, Dart 3+.
- Create app
flutter create demo_yknpg && cd demo_yknpg
- Add dependencies
flutter pub add http flutter_dotenv
- Add environment file
- File:
.env
YKNPG_API_TOKEN=replace-with-your-token
YKNPG_BASE_URL=https://yknpg.ngoul.com/api/v1
- Update
pubspec.yamlto include the file:
flutter:
assets:
- .env
- Create API helper
- File:
lib/yknpg_api.dart
import 'dart:convert';
import 'dart:io';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:http/http.dart' as http;
final _baseUrl = dotenv.env['YKNPG_BASE_URL'] ?? 'https://yknpg.ngoul.com/api/v1';
final _headers = {
HttpHeaders.authorizationHeader: 'Bearer ${dotenv.env['YKNPG_API_TOKEN']}',
HttpHeaders.contentTypeHeader: 'application/json',
};
Future<Map<String, dynamic>> startTransaction(int amount, String phone) async {
final payload = {
'amount': amount,
'provider': 'ORANGE_MONEY',
'user_infos': {'number': phone},
'callback_url': 'http://example.com',
'callback_method': 'POST',
'your_message': 'pay this',
'your_order_ref': 'demo-1',
'provider_fees_on_customer': true,
};
final create = await http.post(Uri.parse("$_baseUrl/transactions/"),
headers: _headers, body: jsonEncode(payload));
if (create.statusCode >= 400) throw create.body;
final created = jsonDecode(create.body) as Map<String, dynamic>;
final pay = await http.post(
Uri.parse("$_baseUrl/transactions/${created['uuid']}/pay/"),
headers: _headers);
if (pay.statusCode >= 400) throw pay.body;
final paid = jsonDecode(pay.body) as Map<String, dynamic>;
var status = paid['status'] as String;
while (status == 'new' || status == 'paying') {
await Future.delayed(const Duration(seconds: 5));
final poll = await http.get(
Uri.parse("$_baseUrl/transactions/${created['uuid']}/"),
headers: _headers);
if (poll.statusCode >= 400) throw poll.body;
status = (jsonDecode(poll.body) as Map<String, dynamic>)['status'] as String;
}
return {
'created': created,
'paid': paid,
'finalStatus': status,
};
}
- Update UI
- File:
lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'yknpg_api.dart';
Future<void> main() async {
await dotenv.load(fileName: ".env");
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: PaymentPage(),
);
}
}
class PaymentPage extends StatefulWidget {
@override
State<PaymentPage> createState() => _PaymentPageState();
}
class _PaymentPageState extends State<PaymentPage> {
final _phoneCtrl = TextEditingController();
final _amountCtrl = TextEditingController(text: '1000');
String _status = '';
bool _loading = false;
Future<void> _submit() async {
setState(() {
_loading = true;
_status = '';
});
try {
final result = await startTransaction(
int.parse(_amountCtrl.text), _phoneCtrl.text);
setState(() {
_status =
"Gateway: ${result['created']['instructions']}\nPost-pay: ${result['paid']['instructions']}\nFinal status: ${result['finalStatus']}";
});
} catch (e) {
setState(() {
_status = 'Error: $e';
});
} finally {
setState(() {
_loading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Start payment')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(controller: _phoneCtrl, decoration: const InputDecoration(labelText: 'Phone')),
TextField(
controller: _amountCtrl,
decoration: const InputDecoration(labelText: 'Amount'),
keyboardType: TextInputType.number,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loading ? null : _submit,
child: Text(_loading ? 'Processing...' : 'Pay'),
),
const SizedBox(height: 16),
Text(_status),
],
),
),
);
}
}
- Run
flutter run
Notes
- Move token to a secure backend or key management for production.