easymerchantsdk 1.2.0 copy "easymerchantsdk: ^1.2.0" to clipboard
easymerchantsdk: ^1.2.0 copied to clipboard

Flutter SDK for EasyMerchant's native mobile checkout integration.

Lyfecycle Payments SDK for Flutter #

The Lyfecycle Payments SDK provides a Flutter interface for integrating secure payment processing into mobile applications on iOS and Android. It supports one-time and recurring payments, card and ACH transactions, 3D Secure authentication, and customizable UI elements, billing fields, and metadata. The SDK offers two payment flows: makePayment (direct payment with API keys) and makePaymentV2 (client token-based payment). This README provides a comprehensive guide to installing, configuring, and using the SDK, with example code and troubleshooting tips.

Installation #

Prerequisites #

  • Flutter: Version 3.0.0 or higher.
  • Dart: Version 2.17.0 or higher.
  • Android:
    • Android Studio with the Flutter plugin.
    • Minimum SDK version 21.
  • iOS:
    • Xcode 14.0 or higher.
    • Ruby 3.2.8 or higher (for CocoaPods).
  • Credentials:
    • apiKey and secretKey for the makePayment flow.
    • clientToken for the makePaymentV2 flow (obtained externally, e.g., from your backend).
    • Contact Lyfecycle Payments support for credentials.

Android Setup #

  1. Add the SDK to pubspec.yaml:

    dependencies:
      easymerchantsdk: ^1.2.0
    

    Run flutter pub get to install the package.

  2. Update build.gradle: In android/build.gradle, add the following to the allprojects.repositories section:

    allprojects {
        repositories {
            google()
            mavenCentral()
            maven { url 'https://jitpack.io' }
            maven {
                url = uri('https://maven.pkg.github.com/your-org/easymerchantsdk')
                credentials {
                    username = 'your-github-username'
                    password = 'your-github-token'
                }
            }
        }
    }
    

    Replace your-org, your-github-username, and your-github-token with values provided by Lyfecycle Payments.

  3. Add Permissions: In android/app/src/main/AndroidManifest.xml, add:

    <uses-permission android:name="android.permission.INTERNET" />
    
  4. Sync Project: Run flutter pub get and ensure the Android project builds successfully.

iOS Setup #

  1. Add Permissions: Update ios/Runner/Info.plist to allow network access:

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    
  2. Podfile Configuration: Update ios/Podfile to include the Lyfecycle Payments dependency. Run pod install in the ios directory:

  3. Install Pods: Navigate to the ios directory and run:

    cd ios
    pod install
    
  4. Initialize ViewController: In ios/Runner/AppDelegate.swift, ensure the SDK’s ViewController is initialized. Example:

    import UIKit
    import Flutter
    import EasyMerchantSDK
    
    @UIApplicationMain
    @objc class AppDelegate: FlutterAppDelegate {
        override func application(
            _ application: UIApplication,
            didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
        ) -> Bool {
            GeneratedPluginRegistrant.register(with: self)
            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
    }
    
  5. Verify Setup: Build and run the iOS app to ensure no integration errors occur.

Note: Refer to the official Lyfecycle Payments documentation for additional iOS-specific setup details or contact support for assistance.

Usage #

The Lyfecycle Payments SDK supports two payment flows:

  • makePayment: Direct payment using apiKey and secretKey. Requires environment initialization.
  • makePaymentV2: Client token-based payment using a clientToken provided externally (e.g., from your backend). No environment initialization required.

Initializing the SDK #

Import the SDK and set up event listeners for payment events. Initialize the ViewController for iOS.

import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:easymerchantsdk/easymerchantsdk.dart';

class PaymentView extends StatefulWidget {
  const PaymentView({super.key});

  @override
  _PaymentViewState createState() => _PaymentViewState();
}

class _PaymentViewState extends State<PaymentView> {
  final Easymerchantsdk easymerchant = Easymerchantsdk();
  final TextEditingController amountController = TextEditingController(text: '10.00');
  final TextEditingController emailController = TextEditingController(text: 'test@example.com');
  final TextEditingController nameController = TextEditingController(text: 'Test User');
  final TextEditingController clientTokenController = TextEditingController();
  String result = 'No response yet';
  String referenceToken = '';
  bool isLoading = false;
  bool isRecurring = false;

  @override
  void initState() {
    super.initState();
    _setupEventListeners();
    if (Platform.isIOS) {
      _setViewController();
    }
  }

  void _setupEventListeners() {
    easymerchant.setupEventListeners(
      onPaymentSuccess: (data) {
        debugPrint('🎉 Payment Success: $data');
        setState(() => isLoading = false);
        try {
          final decoded = jsonDecode(data);
          setState(() {
            result = const JsonEncoder.withIndent('  ').convert(decoded);
            referenceToken = decoded['secure_token'] ?? decoded['ref_token'] ?? '';
          });
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Payment Successful!')),
          );
        } catch (e) {
          debugPrint('❌ Error parsing success: $e');
          setState(() {
            result = 'Payment success but error parsing response: $e';
          });
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('Payment success, but parsing error.')),
          );
        }
      },
      onPaymentStatusError: (error) {
        debugPrint('❌ Payment Error: $error');
        setState(() {
          isLoading = false;
          result = 'Error: $error';
        });
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Payment failed: $error')),
        );
      },
      onPaymentPending: (data) {
        debugPrint('⏳ Payment Pending: $data');
        setState(() {
          result = 'Payment Pending: $data';
        });
      },
    );
  }

  Future<void> _setViewController() async {
    try {
      await easymerchant.setViewController();
      debugPrint('ViewController initialized');
    } catch (e) {
      debugPrint('Failed to initialize ViewController: $e');
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to initialize ViewController: $e')),
      );
    }
  }
}

Configuring the Environment #

For makePayment, configure the environment with apiKey and secretKey. This step is required for iOS and optional for Android.

Future<void> _configureEnvironment() async {
  setState(() => isLoading = true);
  try {
    final keys = {
      'apiKey': 'your-api-key',
      'secretKey': 'your-secret-key',
    };
    await easymerchant.configureEnvironment(
      'sandbox', // or 'production'
      keys['apiKey']!,
      keys['secretKey']!,
    );
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Environment configured')),
    );
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Configuration error: $e')),
    );
  } finally {
    setState(() => isLoading = false);
  }
}

Starting a Payment #

makePayment (Direct Payment)

The makePayment flow requires apiKey and secretKey in the configuration and environment initialization. The config is JSON-encoded for both platforms.

Future<void> handlePayment() async {
  if (amountController.text.isEmpty || double.tryParse(amountController.text) == null || double.parse(amountController.text) <= 0) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Please enter a valid amount')),
    );
    return;
  }
  if (paymentConfig['paymentMethods'].isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Select at least one payment method')),
    );
    return;
  }
  if (isRecurring && recurringData['intervals'].isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Select at least one recurring interval')),
    );
    return;
  }

  setState(() => isLoading = true);
  try {
    final config1 = {
      'environment': 'sandbox',
      'apiKey': 'your-api-key',
      'secretKey': 'your-secret-key',
      'amount': double.parse(amountController.text),
      'currency': 'usd',
       'tokenOnly':false,
      'email': emailController.text,
      'name': nameController.text.isNotEmpty ? nameController.text : 'Test User',
      'paymentMethods': ['card', 'ach'],
      'saveCard': true,
      'saveAccount': true,
      'authenticatedACH': true,
      'secureAuthentication': true,
      'showReceipt': true,
      'showDonate': false,
      'showTotal': true,
      'showSubmitButton': true,
      'isEmail': true,
      'is_recurring': isRecurring,
      'numOfCycle': isRecurring ? 2 : null,    // if is_recurring == true
      'recurringIntervals': isRecurring ? ['weekly', 'monthly'] : null,  // if is_recurring == true
      'recurringStartDateType': isRecurring ? 'custom' : null,  // if is_recurring == true
      'recurringStartDate': isRecurring ? '09/04/2025' : null,   // if is_recurring == true
      'fields': {
        'visibility': {'billing': false, 'additional': false},
        'billing': [
          {'name': 'address', 'required': true, 'value': '123 Main Street'},
          {'name': 'country', 'required': true, 'value': 'United States'},
          {'name': 'state', 'required': true, 'value': 'California'},
          {'name': 'city', 'required': false, 'value': 'San Francisco'},
          {'name': 'postal_code', 'required': true, 'value': '94105'},
        ],
        'additional': [
          {'name': 'phone_number', 'required': false, 'value': '+1-555-123-4567'},
          {'name': 'description', 'required': true, 'value': 'Test payment'},
        ],
      },
      'appearanceSettings': {
        'theme': 'light',
        'bodyBackgroundColor': '#121212',
        'containerBackgroundColor': '#1E1E1E',
        'primaryFontColor': '#FFFFFF',
        'secondaryFontColor': '#B0B0B0',
        'primaryButtonBackgroundColor': '#2563EB',
        'primaryButtonHoverColor': '#1D4ED8',
        'primaryButtonFontColor': '#FFFFFF',
        'secondaryButtonBackgroundColor': '#374151',
        'secondaryButtonHoverColor': '#4B5563',
        'secondaryButtonFontColor': '#E5E7EB',
        'borderRadius': '8',
        'fontSize': '16',
        'fontWeight': '500',
        'fontFamily': '"Inter", sans-serif',
      },
      'grailPayParams': {
        'role': 'business',
        'timeout': 10,
        'brandingName': 'Lyfecycle Payments',
        'finderSubtitle': 'Search for your bank Edg',
        'searchPlaceholder': 'Enter bank name',
      },
      'metadata': {
        'metadataOne': 'valueOne',
        'metadataTwo': 'valueTwo',
      },
    };

    final configJson = jsonEncode(config1);
    await easymerchant.makePayment(configJson);
  } catch (e) {
    setState(() {
      result = 'Payment Error: $e';
      isLoading = false;
    });
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Payment failed: $e')),
    );
  }
}

makePaymentV2 (Client Token-Based Payment)

The makePaymentV2 flow requires a clientToken provided externally (e.g., from your backend). Environment initialization is not required. The config is JSON-encoded for both platforms.

Future<void> handlePaymentV2() async {
  if (clientTokenController.text.isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Please provide a client token')),
    );
    return;
  }
  if (paymentConfig['paymentMethods'].isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Select at least one payment method')),
    );
    return;
  }
  if (isRecurring && recurringData['intervals'].isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Select at least one recurring interval')),
    );
    return;
  }

  setState(() => isLoading = true);
  try {
    final config2 = {
      'environment': 'sandbox',
      'clientToken': clientTokenController.text,
      'currency': 'usd',
      'email': emailController.text,
      'name': nameController.text.isNotEmpty ? nameController.text : 'Test User',
      'paymentMethods': ['card', 'ach'],
      'saveCard': true,
      'saveAccount': true,
      'authenticatedACH': true,
      'secureAuthentication': true,
      'showReceipt': true,
      'showDonate': false,
      'showTotal': true,
      'showSubmitButton': true,
      'isEmail': true,
      'fields': {
        'visibility': {'billing': false, 'additional': false},
        'billing': [
          {'name': 'address', 'required': true, 'value': '123 Main Street'},
          {'name': 'country', 'required': true, 'value': 'United States'},
          {'name': 'state', 'required': true, 'value': 'California'},
          {'name': 'city', 'required': false, 'value': 'San Francisco'},
          {'name': 'postal_code', 'required': true, 'value': '94105'},
        ],
        'additional': [
          {'name': 'phone_number', 'required': false, 'value': '+1-555-123-4567'},
          {'name': 'description', 'required': true, 'value': 'Test payment'},
        ],
      },
      'appearanceSettings': {
        'theme': 'light',
        'bodyBackgroundColor': '#121212',
        'containerBackgroundColor': '#1E1E1E',
        'primaryFontColor': '#FFFFFF',
        'secondaryFontColor': '#B0B0B0',
        'primaryButtonBackgroundColor': '#2563EB',
        'primaryButtonHoverColor': '#1D4ED8',
        'primaryButtonFontColor': '#FFFFFF',
        'secondaryButtonBackgroundColor': '#374151',
        'secondaryButtonHoverColor': '#4B5563',
        'secondaryButtonFontColor': '#E5E7EB',
        'borderRadius': '8',
        'fontSize': '16',
        'fontWeight': '500',
        'fontFamily': '"Inter", sans-serif',
      },
      'grailPayParams': {
        'role': 'business',
        'timeout': 10,
        'brandingName': 'Lyfecycle Payments',
        'finderSubtitle': 'Search for your bank',
        'searchPlaceholder': 'Enter bank name',
      },
      'metadata': {
        'metadataOne': 'valueOne',
        'metadataTwo': 'valueTwo',
      },
    };

    final configJson = jsonEncode(config2);
    await easymerchant.makePaymentV2(configJson);
  } catch (e) {
    setState(() {
      result = 'Payment Error: $e';
      isLoading = false;
    });
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Payment failed: $e')),
    );
  }
}

Checking Payment Reference (iOS Only) #

Retrieve payment details using a reference token on iOS.

Future<void> checkPaymentReference() async {
  if (referenceToken.isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('No reference token available')),
    );
    return;
  }
  try {
    final response = await easymerchant.paymentReference(referenceToken);
    setState(() {
      result = const JsonEncoder.withIndent('  ').convert(jsonDecode(response!));
    });
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Payment reference retrieved')),
    );
  } catch (e) {
    setState(() {
      result = 'Payment reference failed: $e';
    });
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Payment reference failed: $e')),
    );
  }
}

Configuration Parameters #

Environment Configuration Parameters #

Used in configureEnvironment for makePayment.

Parameter Type Description Example Value
environment String Environment: sandbox or production. sandbox
apiKey String API key for authentication. your-api-key
secretKey String Secret key for authentication. your-secret-key

makePayment Parameters #

Used in the makePayment method for both iOS and Android.

Parameter Type Description Example Value
environment String Environment: sandbox or production. sandbox
apiKey String API key for authentication. your-api-key
secretKey String Secret key for authentication. your-secret-key
amount double Payment amount (must be > 0). 10.00
currency String Currency code (e.g., usd). usd
email String Email address for the transaction. test@example.com
name String Name associated with the transaction. Test User
paymentMethods List Payment methods: card, ach. ['card', 'ach']
saveCard bool Save card details. true
saveAccount bool Save account details. true
authenticatedACH bool Enable authenticated ACH payments. true
secureAuthentication bool Enable 3D Secure authentication. true
showReceipt bool Show payment receipt. true
showDonate bool Show donate option. false
showTotal bool Show total amount in UI. true
showSubmitButton bool Show submit button in UI. true
isEmail bool Allow email field to be editable. true
is_recurring bool Enable recurring payments. false
numOfCycle int? Number of recurring cycles (if is_recurring). 2
recurringIntervals List Recurring intervals: daily, weekly, monthly (if is_recurring). ['weekly', 'monthly']
recurringStartDateType String? Recurring start date type: custom, fixed (if is_recurring). custom
recurringStartDate String? Start date for recurring payments: MM/DD/YYYY (if is_recurring). 09/04/2025
fields Map<String, dynamic> Billing and additional field configurations. See Fields Configuration
appearanceSettings Map<String, dynamic> UI theme settings. See Appearance Settings
grailPayParams Map<String, dynamic> GrailPay-specific parameters. See GrailPay Parameters
metadata Map<String, dynamic> Additional metadata for the transaction. See Metadata Parameters

makePaymentV2 Parameters #

Used in the makePaymentV2 method for both iOS and Android.

Parameter Type Description Example Value
environment String Environment: sandbox or production. sandbox
clientToken String Client token obtained externally (e.g., from your backend). your-client-token
currency String Currency code (e.g., usd). usd
email String Email address for the transaction. test@example.com
name String Name associated with the transaction. Test User
paymentMethods List Payment methods: card, ach. ['card', 'ach']
saveCard bool Save card details. true
saveAccount bool Save account details. true
authenticatedACH bool Enable authenticated ACH payments. true
secureAuthentication bool Enable 3D Secure authentication. true
showReceipt bool Show payment receipt. true
showDonate bool Show donate option. false
showTotal bool Show total amount in UI. true
showSubmitButton bool Show submit button in UI. true
isEmail bool Allow email field to be editable. true
fields Map<String, dynamic> Billing and additional field configurations. See Fields Configuration
appearanceSettings Map<String, dynamic> UI theme settings. See Appearance Settings
grailPayParams Map<String, dynamic> GrailPay-specific parameters. See GrailPay Parameters
metadata Map<String, dynamic> Additional metadata for the transaction. See Metadata Parameters

Fields Configuration #

Used in both makePayment and makePaymentV2.

Parameter Type Description Example Value
visibility Map<String, bool> Visibility of billing/additional fields. {'billing': false, 'additional': false}
billing List<Map<String, dynamic>> Billing fields with name, required status, and value. See example below
additional List<Map<String, dynamic>> Additional fields with name, required status, and value. See example below

Example:

{
  'visibility': {'billing': false, 'additional': false},
  'billing': [
    {'name': 'address', 'required': true, 'value': '123 Main Street'},
    {'name': 'country', 'required': true, 'value': 'United States'},
    {'name': 'state', 'required': true, 'value': 'California'},
    {'name': 'city', 'required': false, 'value': 'San Francisco'},
    {'name': 'postal_code', 'required': true, 'value': '94105'},
  ],
  'additional': [
    {'name': 'phone_number', 'required': false, 'value': '+1-555-123-4567'},
    {'name': 'description', 'required': true, 'value': 'Test payment'},
  ],
}

Appearance Settings #

Customize the UI for both iOS and Android.

Parameter Type Description Example Value
theme String Theme setting (e.g., light, dark). light
bodyBackgroundColor String Background color of the body (hex). #121212
containerBackgroundColor String Background color of containers (hex). #1E1E1E
primaryFontColor String Primary font color (hex). #FFFFFF
secondaryFontColor String Secondary font color (hex). #B0B0B0
primaryButtonBackgroundColor String Background color of primary buttons (hex). #2563EB
primaryButtonHoverColor String Hover color for primary buttons (hex). #1D4ED8
primaryButtonFontColor String Font color for primary buttons (hex). #FFFFFF
secondaryButtonBackgroundColor String Background color of secondary buttons (hex). #374151
secondaryButtonHoverColor String Hover color for secondary buttons (hex). #4B5563
secondaryButtonFontColor String Font color for secondary buttons (hex). #E5E7EB
borderRadius String Border radius for UI elements (pixels). 8
fontSize String Font size for text (pixels). 16
fontWeight String Font weight for text. 500
fontFamily String Font family for text. "Inter", sans-serif

GrailPay Parameters #

Configure GrailPay-specific settings.

Parameter Type Description Example Value
role String Role of the user (e.g., business). business
timeout int Timeout duration in seconds. 10
brandingName String Name for branding in the UI. Lyfecycle Payments
finderSubtitle String Subtitle for bank search UI. Search for your bank
searchPlaceholder String Placeholder text for bank search input. Enter bank name

Recurring Payment Parameters #

Configure recurring payments (if is_recurring is true).

Parameter Type Description Example Value
is_recurring bool Enable recurring payments. false
numOfCycle int? Number of recurring cycles. 2
recurringIntervals List Recurring intervals: daily, weekly, monthly. ['weekly', 'monthly']
recurringStartDateType String? Start date type: custom, fixed. custom
recurringStartDate String? Start date: MM/DD/YYYY. 09/04/2025

Metadata Parameters #

Add custom metadata to transactions.

Parameter Type Description Example Value
metadata Map<String, dynamic> Custom key-value pairs for additional data. {'metadataOne': 'valueOne', 'metadataTwo': 'valueTwo'}

Troubleshooting #

  • MissingPluginException: Ensure easymerchantsdk is correctly added to pubspec.yaml, and Android/iOS native setups are complete.
  • Invalid Amount: Verify amount is a valid number greater than 0.
  • Empty Payment Methods: At least one payment method (card or ach) must be included in paymentMethods.
  • Empty Recurring Intervals: If is_recurring is true, recurringIntervals must include at least one interval.
  • JSON Serialization Errors: Ensure all configuration maps are properly formatted before calling jsonEncode.
  • Authentication Errors: Verify apiKey and secretKey match the selected environment for makePayment.
  • Missing Client Token: For makePaymentV2, ensure a valid clientToken is provided.
  • iOS ViewController Issues: Call setViewController before makePayment or makePaymentV2 on iOS.
  • No Response Timeout: If no response is received within 10 seconds, check the payment screen or network connectivity.
  • Contact Support: For further assistance, reach out to Lyfecycle Payments support.