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

  1. Data Collection: Collect payment data from customer form
  2. Data Structure: Build complete payment structure with all required fields
  3. Encryption: Encrypt payment data using AES-256-CBC
  4. API Call: Send encrypted data to YagoutPay API endpoint
  5. Response Handling: Decrypt and process YagoutPay response
  6. 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> 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 {
      // Step 1: Build payment data structure
      final paymentData = buildPaymentData(
        orderNo: orderNo,
        amount: amount,
        email: email,
        mobile: mobile,
        customerName: customerName,
        billAddress: billAddress,
        billCity: billCity,
        billState: billState,
        billCountry: billCountry,
        billZip: billZip,
      );
      
      // Step 2: Encrypt payment data
      final encryptedData = encryptionService.encrypt(jsonEncode(paymentData));
      
      // Step 3: Prepare API request
      final requestData = {
        'merchantId': merchantId,
        'merchantRequest': encryptedData,
      };
      
      // Step 4: Make API call
      final response = await callYagoutPayAPI(requestData);
      
      // Step 5: Handle response
      if (response['status'] == 'Success') {
        return {
          'success': true,
          'transactionId': response['transactionId'],
          'message': 'Payment processed successfully',
        };
      } else {
        return {
          'success': false,
          'error': response['statusMessage'] ?? 'Payment failed',
        };
      }
    } catch (e) {
      return {
        'success': false,
        'error': 'Payment processing failed: $e',
      };
    }
  }
  
  // Call YagoutPay API
  Future> callYagoutPayAPI(Map request) async {
    final response = await http.post(
      Uri.parse(apiUrl),
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
      body: jsonEncode(request),
    );
    
    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception('API call failed with status code: ${response.statusCode}');
    }
  }
  
  // Validate Payment Data
  Map validatePaymentData({
    required String amount,
    required String email,
    required String mobile,
    required String orderNo,
  }) {
    final errors = {};
    bool isValid = true;
    
    if (amount.isEmpty || double.tryParse(amount) == null || double.parse(amount) <= 0) {
      errors['amount'] = 'Amount is required and must be greater than 0';
      isValid = false;
    }
    
    if (email.isEmpty || !RegExp(r'^[^\s@]+@[^\s@]+\.[^\s@]+$').hasMatch(email)) {
      errors['email'] = 'Valid email is required';
      isValid = false;
    }
    
    if (mobile.isEmpty) {
      errors['mobile'] = 'Mobile number is required';
      isValid = false;
    }
    
    if (orderNo.isEmpty) {
      errors['orderNo'] = 'Order number is required';
      isValid = false;
    }
    
    return {
      'isValid': isValid,
      'errors': errors,
    };
  }
}

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.