Flutter API Integration
Direct API integration with YagoutPay in Flutter applications with complete implementation details.
Direct API integration processes payments without redirecting users to external pages. All payment processing happens within your Flutter application using YagoutPay's API with AES-256-CBC encryption.
Overview
Direct API integration uses YagoutPay's API to process payments directly in your Flutter application. The process involves encrypting payment data, making API calls, and handling encrypted responses.
Direct API Payment Flow
- Data Collection: Collect payment data from customer form
- Data Structure: Build complete payment structure with all required fields
- Encryption: Encrypt payment data using AES-256-CBC
- API Call: Send encrypted data to YagoutPay API endpoint
- Response Handling: Decrypt and process YagoutPay response
- Result Processing: Handle success/failure and update UI
Encryption Service
Create a service to handle AES-256-CBC encryption for direct payments:
Example Encryption Service with Flutter:
// yagoutpay_encryption_service.dart
import 'dart:convert';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:encrypt/encrypt.dart';
class YagoutPayEncryptionService {
final String merchantId;
final String encryptionKey;
final String iv = '0123456789abcdef'; // Fixed 16-byte IV
YagoutPayEncryptionService({
required this.merchantId,
required this.encryptionKey,
});
// AES-256-CBC Encryption for Direct Payments
String encrypt(String data) {
try {
final key = Key.fromBase64(encryptionKey);
final ivBytes = IV.fromUtf8(iv);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc));
final encrypted = encrypter.encrypt(data, iv: ivBytes);
return encrypted.base64;
} catch (e) {
throw Exception('Encryption failed: $e');
}
}
// AES-256-CBC Decryption for Response Handling
String decrypt(String encryptedData) {
try {
final key = Key.fromBase64(encryptionKey);
final ivBytes = IV.fromUtf8(iv);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc));
final encrypted = Encrypted.fromBase64(encryptedData);
final decrypted = encrypter.decrypt(encrypted, iv: ivBytes);
return decrypted;
} catch (e) {
throw Exception('Decryption failed: $e');
}
}
}
Direct Payment Service
Create a service to handle direct payment processing:
Example Direct Payment Service with Flutter:
// yagoutpay_direct_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class YagoutPayDirectService {
final YagoutPayEncryptionService encryptionService;
final String merchantId;
final String apiUrl;
YagoutPayDirectService({
required this.merchantId,
required String encryptionKey,
required this.apiUrl,
}) : encryptionService = YagoutPayEncryptionService(
merchantId: merchantId,
encryptionKey: encryptionKey,
);
// Build Complete Payment Data Structure
Map buildPaymentData({
required String orderNo,
required String amount,
required String email,
required String mobile,
String? customerName,
String? billAddress,
String? billCity,
String? billState,
String? billCountry,
String? billZip,
}) {
return {
'card_details': {
'card_number': '',
'expiry_month': '',
'expiry_year': '',
'cvv': '',
},
'other_details': {
'order_no': orderNo,
'amount': amount,
'currency': 'ETB',
'country': 'ETH',
},
'ship_details': {
'ship_name': customerName ?? '',
'ship_address': billAddress ?? 'N/A',
'ship_city': billCity ?? 'Addis Ababa',
'ship_state': billState ?? 'Addis Ababa',
'ship_country': billCountry ?? 'ET',
'ship_zip': billZip ?? '1000',
},
'txn_details': {
'txn_type': 'SALE',
'txn_sub_type': 'PAYMENT',
},
'item_details': [
{
'item_name': 'Payment',
'item_amount': amount,
'item_quantity': '1',
}
],
'cust_details': {
'customer_name': customerName ?? '',
'customer_email': email,
'customer_mobile': mobile,
},
'pg_details': {
'pg_id': '67ee846571e740418d688c3f',
'paymode': 'WA',
'scheme_id': '7',
'wallet_type': 'telebirr',
},
'bill_details': {
'bill_name': customerName ?? '',
'bill_address': billAddress ?? 'N/A',
'bill_city': billCity ?? 'Addis Ababa',
'bill_state': billState ?? 'Addis Ababa',
'bill_country': billCountry ?? 'ET',
'bill_zip': billZip ?? '1000',
},
};
}
// Process Direct Payment
Future
Payment Controller
Create a controller to handle payment flow in your Flutter app:
Example Payment Controller with Flutter:
// payment_controller.dart
import 'package:flutter/material.dart';
import 'yagoutpay_direct_service.dart';
class PaymentController extends ChangeNotifier {
final YagoutPayDirectService paymentService;
bool _isLoading = false;
String? _error;
PaymentController({
required String merchantId,
required String encryptionKey,
required String apiUrl,
}) : paymentService = YagoutPayDirectService(
merchantId: merchantId,
encryptionKey: encryptionKey,
apiUrl: apiUrl,
);
bool get isLoading => _isLoading;
String? get error => _error;
// Process Payment
Future processPayment({
required String orderNo,
required String amount,
required String email,
required String mobile,
String? customerName,
String? billAddress,
String? billCity,
String? billState,
String? billCountry,
String? billZip,
}) async {
try {
_setLoading(true);
_clearError();
// Validate payment data
final validation = paymentService.validatePaymentData(
amount: amount,
email: email,
mobile: mobile,
orderNo: orderNo,
);
if (!validation['isValid']) {
_setError('Validation failed: ${validation['errors']}');
return false;
}
// Process payment
final result = await paymentService.processPayment(
orderNo: orderNo,
amount: amount,
email: email,
mobile: mobile,
customerName: customerName,
billAddress: billAddress,
billCity: billCity,
billState: billState,
billCountry: billCountry,
billZip: billZip,
);
if (result['success']) {
_clearError();
return true;
} else {
_setError(result['error']);
return false;
}
} catch (e) {
_setError('Payment processing failed: $e');
return false;
} finally {
_setLoading(false);
}
}
void _setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
void _setError(String error) {
_error = error;
notifyListeners();
}
void _clearError() {
_error = null;
notifyListeners();
}
}
Frontend Integration
Create a complete payment form with Flutter integration:
Example Payment Form with Flutter:
// payment_form.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'payment_controller.dart';
class PaymentForm extends StatefulWidget {
@override
_PaymentFormState createState() => _PaymentFormState();
}
class _PaymentFormState extends State {
final _formKey = GlobalKey();
final _orderNoController = TextEditingController();
final _amountController = TextEditingController();
final _emailController = TextEditingController();
final _mobileController = TextEditingController();
final _customerNameController = TextEditingController();
final _billAddressController = TextEditingController();
final _billCityController = TextEditingController();
final _billStateController = TextEditingController();
final _billCountryController = TextEditingController();
final _billZipController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('YagoutPay Payment'),
backgroundColor: Colors.blue,
),
body: Consumer(
builder: (context, paymentController, child) {
return Padding(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: ListView(
children: [
TextFormField(
controller: _orderNoController,
decoration: InputDecoration(
labelText: 'Order Number *',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Order number is required';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _amountController,
decoration: InputDecoration(
labelText: 'Amount (ETB) *',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Amount is required';
}
if (double.tryParse(value) == null || double.parse(value) <= 0) {
return 'Amount must be greater than 0';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email *',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
if (!RegExp(r'^[^\s@]+@[^\s@]+\.[^\s@]+$').hasMatch(value)) {
return 'Valid email is required';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _mobileController,
decoration: InputDecoration(
labelText: 'Mobile Number *',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.phone,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Mobile number is required';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _customerNameController,
decoration: InputDecoration(
labelText: 'Customer Name',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
TextFormField(
controller: _billAddressController,
decoration: InputDecoration(
labelText: 'Billing Address',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
TextFormField(
controller: _billCityController,
decoration: InputDecoration(
labelText: 'Billing City',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
TextFormField(
controller: _billStateController,
decoration: InputDecoration(
labelText: 'Billing State',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
TextFormField(
controller: _billCountryController,
decoration: InputDecoration(
labelText: 'Billing Country',
border: OutlineInputBorder(),
),
),
SizedBox(height: 16),
TextFormField(
controller: _billZipController,
decoration: InputDecoration(
labelText: 'Billing Zip Code',
border: OutlineInputBorder(),
),
),
SizedBox(height: 24),
if (paymentController.error != null)
Container(
padding: EdgeInsets.all(12),
margin: EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.red.shade50,
border: Border.all(color: Colors.red.shade200),
borderRadius: BorderRadius.circular(8),
),
child: Text(
paymentController.error!,
style: TextStyle(color: Colors.red.shade700),
),
),
ElevatedButton(
onPressed: paymentController.isLoading ? null : _processPayment,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 16),
),
child: paymentController.isLoading
? CircularProgressIndicator(color: Colors.white)
: Text('Process Payment'),
),
],
),
),
);
},
),
);
}
Future _processPayment() async {
if (_formKey.currentState!.validate()) {
final paymentController = Provider.of(context, listen: false);
final success = await paymentController.processPayment(
orderNo: _orderNoController.text,
amount: _amountController.text,
email: _emailController.text,
mobile: _mobileController.text,
customerName: _customerNameController.text,
billAddress: _billAddressController.text,
billCity: _billCityController.text,
billState: _billStateController.text,
billCountry: _billCountryController.text,
billZip: _billZipController.text,
);
if (success) {
_showSuccessDialog();
} else {
_showErrorDialog();
}
}
}
void _showSuccessDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Payment Successful'),
content: Text('Your payment has been processed successfully.'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('OK'),
),
],
),
);
}
void _showErrorDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Payment Failed'),
content: Text('Your payment could not be processed. Please try again.'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('OK'),
),
],
),
);
}
}
Key Implementation Points
- Encryption: All payment data must be encrypted using AES-256-CBC
- API Endpoint:
/apiRedirection/apiIntegration - Headers Required:
Content-Type: application/json - Response Handling: All responses need to be processed for success/failure
- Error Handling: Implement proper error handling for network and API errors
- Validation: Validate all required fields before processing
NEXT STEPS
After implementing API integration, explore Hosted Payments for WebView-based payment processing.