telebirr_inapp 0.1.0
telebirr_inapp: ^0.1.0 copied to clipboard
Flutter plugin for telebirr InApp SDK payments on Android and iOS, with an explicit Gradle switch between the testbed and production SDK.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:telebirr_inapp/telebirr_inapp.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'telebirr InApp example',
theme: ThemeData(colorSchemeSeed: Colors.green),
home: const CheckoutPage(),
);
}
}
class CheckoutPage extends StatefulWidget {
const CheckoutPage({super.key});
@override
State<CheckoutPage> createState() => _CheckoutPageState();
}
class _CheckoutPageState extends State<CheckoutPage> {
// Pre-fill the form at build time, e.g.
// flutter run --dart-define=TELEBIRR_APP_ID=... --dart-define=TELEBIRR_SHORT_CODE=...
// --dart-define=TELEBIRR_BACKEND_URL=...
final _backendUrl = TextEditingController(
text: const String.fromEnvironment('TELEBIRR_BACKEND_URL'),
);
final _appId = TextEditingController(
text: const String.fromEnvironment('TELEBIRR_APP_ID'),
);
final _shortCode = TextEditingController(
text: const String.fromEnvironment('TELEBIRR_SHORT_CODE'),
);
final _title = TextEditingController(text: 'Test order');
final _amount = TextEditingController(text: '1');
final _receiveCode = TextEditingController();
TelebirrEnvironment _environment = TelebirrEnvironment.test;
TelebirrEnvironment? _nativeEnvironment;
bool _busy = false;
String _status = '';
@override
void initState() {
super.initState();
_loadNativeEnvironment();
}
@override
void dispose() {
for (final c in [
_backendUrl,
_appId,
_shortCode,
_title,
_amount,
_receiveCode,
]) {
c.dispose();
}
super.dispose();
}
Future<void> _loadNativeEnvironment() async {
final native = await Telebirr.nativeEnvironment();
if (!mounted) return;
setState(() {
_nativeEnvironment = native;
if (native != null) _environment = native;
});
}
/// Asks your backend to create the order. The backend applies the fabric
/// token, calls telebirr's createOrder and answers with the receiveCode,
/// either as plain text or as JSON.
Future<String> _createOrder() async {
final response = await http.post(
Uri.parse(_backendUrl.text.trim()),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'title': _title.text.trim(),
'amount': _amount.text.trim(),
'environment': _environment.name,
}),
);
if (response.statusCode != 200) {
throw Exception(
'Backend answered ${response.statusCode}: ${response.body}',
);
}
final body = response.body.trim();
if (!body.startsWith('{')) return body;
final json = jsonDecode(body) as Map<String, dynamic>;
final bizContent = json['biz_content'];
final code =
json['receiveCode'] ??
(bizContent is Map ? bizContent['receiveCode'] : null);
if (code is! String || code.isEmpty) {
throw Exception('No receiveCode in the backend response: $body');
}
return code;
}
Future<void> _pay() async {
setState(() {
_busy = true;
_status = 'Starting…';
});
try {
await Telebirr.initialize(
appId: _appId.text,
shortCode: _shortCode.text,
environment: _environment,
);
var receiveCode = _receiveCode.text.trim();
if (receiveCode.isEmpty) {
receiveCode = await _createOrder();
}
final result = await Telebirr.pay(receiveCode: receiveCode);
_status = result.isSuccess
? 'telebirr reported success. Confirm it on your backend before delivering.'
: 'Not paid: ${result.status.name} (code ${result.code}) ${result.message}';
} catch (e) {
_status = '$e';
}
if (mounted) setState(() => _busy = false);
}
@override
Widget build(BuildContext context) {
final native = _nativeEnvironment;
return Scaffold(
appBar: AppBar(title: const Text('telebirr InApp example')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
Text(
native == null
? 'Native SDK: one binary for test and live'
: 'Native SDK in this build: ${native.name}',
),
const SizedBox(height: 12),
SegmentedButton<TelebirrEnvironment>(
segments: const [
ButtonSegment(
value: TelebirrEnvironment.test,
label: Text('Test'),
),
ButtonSegment(
value: TelebirrEnvironment.live,
label: Text('Live'),
),
],
selected: {_environment},
onSelectionChanged: (s) => setState(() => _environment = s.first),
),
_field(_appId, 'Merchant App ID'),
_field(_shortCode, 'Short code', number: true),
_field(_backendUrl, 'Backend create-order URL'),
_field(_title, 'Title'),
_field(_amount, 'Amount (ETB)', number: true),
_field(
_receiveCode,
'receiveCode (optional, skips the backend call)',
),
const SizedBox(height: 16),
FilledButton(
onPressed: _busy ? null : _pay,
child: Text(_busy ? 'Waiting for telebirr…' : 'Pay with telebirr'),
),
const SizedBox(height: 16),
SelectableText(_status),
],
),
);
}
Widget _field(
TextEditingController controller,
String label, {
bool number = false,
}) {
return Padding(
padding: const EdgeInsets.only(top: 12),
child: TextField(
controller: controller,
keyboardType: number ? TextInputType.number : TextInputType.text,
decoration: InputDecoration(
labelText: label,
border: const OutlineInputBorder(),
),
),
);
}
}