Flutter Hosted Payments
Hosted payment integration with YagoutPay in Flutter applications using WebView for seamless payment processing.
Hosted payments redirect customers to YagoutPay's secure payment page using WebView integration. This method provides a seamless checkout experience with minimal integration effort and automatic success/failure detection.
Overview
Hosted payments use YagoutPay's secure payment page to process transactions. The process involves encrypting payment data, generating an HTML form, and redirecting users to YagoutPay's hosted payment page using WebView.
Hosted 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 with manual padding
- Hash Generation: Generate SHA-512 hash for security
- Form Generation: Create HTML form with encrypted data
- WebView Integration: Display form in WebView for automatic submission
- Result Handling: Handle success/failure callbacks
Encryption Service
Create a service to handle AES-256-CBC encryption with manual padding for hosted payments:
Example Hosted Encryption Service with Flutter:
// yagoutpay_hosted_encryption_service.dart
import 'dart:convert';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:encrypt/encrypt.dart';
class YagoutPayHostedEncryptionService {
final String merchantId;
final String encryptionKey;
final String iv = '0123456789abcdef'; // Fixed 16-byte IV
YagoutPayHostedEncryptionService({
required this.merchantId,
required this.encryptionKey,
});
// AES-256-CBC Encryption for Hosted Payments with Manual Padding
String encrypt(String text) {
try {
final key = Key.fromBase64(encryptionKey);
final ivBytes = IV.fromUtf8(iv);
// Manual padding for hosted payments
final size = 16;
final pad = size - (text.length % size);
final padtext = text + String.fromCharCode(pad).repeat(pad);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc, padding: null));
final encrypted = encrypter.encrypt(padtext, 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, padding: null));
final encrypted = Encrypted.fromBase64(encryptedData);
final decrypted = encrypter.decrypt(encrypted, iv: ivBytes);
// Remove padding
final pad = decrypted.codeUnitAt(decrypted.length - 1);
if (pad > decrypted.length) {
throw Exception('Invalid padding');
}
return decrypted.substring(0, decrypted.length - pad);
} catch (e) {
throw Exception('Decryption failed: $e');
}
}
// Generate SHA-512 Hash for Hosted Payments
String generateHash(String data, String saltKey) {
final bytes = utf8.encode(data + saltKey);
final digest = sha512.convert(bytes);
return digest.toString();
}
}
Hosted Payment Service
Create a service to handle hosted payment processing:
Example Hosted Payment Service with Flutter:
// yagoutpay_hosted_service.dart
import 'dart:convert';
import 'yagoutpay_hosted_encryption_service.dart';
class YagoutPayHostedService {
final YagoutPayHostedEncryptionService encryptionService;
final String merchantId;
final String gatewayUrl;
final String saltKey;
YagoutPayHostedService({
required this.merchantId,
required String encryptionKey,
required this.gatewayUrl,
required this.saltKey,
}) : encryptionService = YagoutPayHostedEncryptionService(
merchantId: merchantId,
encryptionKey: encryptionKey,
);
// Build Complete Hosted Payment Data Structure
Map buildHostedPaymentData({
required String orderNo,
required String amount,
required String email,
required String mobile,
required String successUrl,
required String failureUrl,
String? customerName,
String? billAddress,
String? billCity,
String? billState,
String? billCountry,
String? billZip,
String? shipAddress,
String? shipCity,
String? shipState,
String? shipCountry,
String? shipZip,
}) {
return {
'txn_details': {
'ag_id': 'yagout',
'me_id': merchantId,
'order_no': orderNo,
'amount': amount,
'country': 'ETH',
'currency': 'ETB',
'txn_type': 'SALE',
'success_url': successUrl,
'failure_url': failureUrl,
'channel': 'MOBILE',
},
'pg_details': {
'pg_id': '',
'paymode': '',
'scheme_id': '',
'wallet_type': 'telebirr',
},
'card_details': {
'card_no': '',
'exp_month': '',
'exp_year': '',
'cvv': '',
},
'cust_details': {
'card_name': '',
'cust_name': customerName ?? '',
'customer_email': email,
'mobile_no': mobile,
'unique_id': '',
'is_logged_in': 'Y',
},
'bill_details': {
'bill_addres': billAddress ?? 'N/A',
'bill_city': billCity ?? 'Addis Ababa',
'bill_state': billState ?? 'Addis Ababa',
'bill_country': billCountry ?? 'ET',
'bill_zip': billZip ?? '1000',
},
'ship_details': {
'ship_address': shipAddress ?? 'N/A',
'ship_city': shipCity ?? 'Addis Ababa',
'ship_state': shipState ?? 'Addis Ababa',
'ship_country': shipCountry ?? 'ET',
'ship_zip': shipZip ?? '1000',
'ship_days': '1',
'address_count': '1',
},
'item_details': {
'item_count': '1',
'item_value': amount,
'item_category': 'Payment',
},
'upi_details': {
'udf_1': '',
'udf_2': '',
'udf_3': '',
'udf_4': '',
'udf_5': '',
},
};
}
// Generate Hosted Payment HTML Form
Future
WebView Integration
Create a WebView screen to handle hosted payment processing:
Example WebView Screen with Flutter:
// yagoutpay_webview_screen.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'yagoutpay_hosted_service.dart';
class YagoutPayWebViewScreen extends StatefulWidget {
final String orderNo;
final String amount;
final String email;
final String mobile;
final String successUrl;
final String failureUrl;
final String? customerName;
final String? billAddress;
final String? billCity;
final String? billState;
final String? billCountry;
final String? billZip;
const YagoutPayWebViewScreen({
Key? key,
required this.orderNo,
required this.amount,
required this.email,
required this.mobile,
required this.successUrl,
required this.failureUrl,
this.customerName,
this.billAddress,
this.billCity,
this.billState,
this.billCountry,
this.billZip,
}) : super(key: key);
@override
_YagoutPayWebViewScreenState createState() => _YagoutPayWebViewScreenState();
}
class _YagoutPayWebViewScreenState extends State {
late WebViewController _controller;
bool _isLoading = true;
String? _error;
@override
void initState() {
super.initState();
_initializeWebView();
}
void _initializeWebView() async {
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (String url) {
setState(() {
_isLoading = true;
});
},
onPageFinished: (String url) {
setState(() {
_isLoading = false;
});
},
onNavigationRequest: (NavigationRequest request) {
// Handle success/failure URLs
if (request.url.contains('success') || request.url.contains('failure')) {
_handlePaymentResult(request.url);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
);
await _loadPaymentForm();
}
Future _loadPaymentForm() async {
try {
final hostedService = YagoutPayHostedService(
merchantId: 'YOUR_MERCHANT_ID',
encryptionKey: 'YOUR_ENCRYPTION_KEY',
gatewayUrl: 'https://uatcheckout.yagoutpay.com/ms-transaction-core-1-0/paymentRedirection/checksumGatewayPage',
saltKey: 'YOUR_SALT_KEY',
);
final result = await hostedService.generateHostedPayment(
orderNo: widget.orderNo,
amount: widget.amount,
email: widget.email,
mobile: widget.mobile,
successUrl: widget.successUrl,
failureUrl: widget.failureUrl,
customerName: widget.customerName,
billAddress: widget.billAddress,
billCity: widget.billCity,
billState: widget.billState,
billCountry: widget.billCountry,
billZip: widget.billZip,
);
if (result['success']) {
await _controller.loadHtmlString(result['html']);
} else {
setState(() {
_error = result['error'];
});
}
} catch (e) {
setState(() {
_error = 'Failed to load payment form: $e';
});
}
}
void _handlePaymentResult(String url) {
if (url.contains('success')) {
Navigator.of(context).pop({'success': true, 'url': url});
} else if (url.contains('failure')) {
Navigator.of(context).pop({'success': false, 'url': url});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('YagoutPay Payment'),
backgroundColor: Colors.blue,
leading: IconButton(
icon: Icon(Icons.close),
onPressed: () => Navigator.of(context).pop({'success': false, 'cancelled': true}),
),
),
body: _error != null
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error, color: Colors.red, size: 64),
SizedBox(height: 16),
Text(
'Payment Error',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
_error!,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.red),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => Navigator.of(context).pop({'success': false, 'error': _error}),
child: Text('Close'),
),
],
),
)
: Stack(
children: [
WebViewWidget(controller: _controller),
if (_isLoading)
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Loading payment page...'),
],
),
),
],
),
);
}
}
Payment Controller
Create a controller to handle hosted payment flow:
Example Hosted Payment Controller with Flutter:
// hosted_payment_controller.dart
import 'package:flutter/material.dart';
import 'yagoutpay_hosted_service.dart';
import 'yagoutpay_webview_screen.dart';
class HostedPaymentController extends ChangeNotifier {
final YagoutPayHostedService hostedService;
bool _isLoading = false;
String? _error;
HostedPaymentController({
required String merchantId,
required String encryptionKey,
required String gatewayUrl,
required String saltKey,
}) : hostedService = YagoutPayHostedService(
merchantId: merchantId,
encryptionKey: encryptionKey,
gatewayUrl: gatewayUrl,
saltKey: saltKey,
);
bool get isLoading => _isLoading;
String? get error => _error;
// Process Hosted Payment
Future
Key Implementation Points
- Encryption: Use AES-256-CBC with manual padding for hosted payments
- Hash Generation: Generate SHA-512 hash for security validation
- Form Submission: Auto-submit HTML form to YagoutPay gateway
- WebView Integration: Use WebView to display payment page
- URL Handling: Monitor navigation for success/failure URLs
- Error Handling: Implement proper error handling for network issues
NEXT STEPS
After implementing hosted payments, explore Payment Links for generating shareable payment URLs.