easymerchantsdk 1.2.8
easymerchantsdk: ^1.2.8 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:
apiKeyandsecretKeyfor themakePaymentflow.clientTokenfor themakePaymentV2flow (obtained externally, e.g., from your backend).- Contact Lyfecycle Payments support for credentials.
Android Setup #
-
Add the SDK to
pubspec.yaml:dependencies: easymerchantsdk: ^1.2.8Run
flutter pub getto install the package. -
Update
build.gradle: Inandroid/build.gradle, add the following to theallprojects.repositoriessection: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, andyour-github-tokenwith values provided by Lyfecycle Payments. -
Add Permissions: In
android/app/src/main/AndroidManifest.xml, add:<uses-permission android:name="android.permission.INTERNET" /> -
Sync Project: Run
flutter pub getand ensure the Android project builds successfully.
iOS Setup #
-
Add Permissions: Update
ios/Runner/Info.plistto allow network access:<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict> -
Podfile Configuration: Update
ios/Podfileto include theLyfecycle Paymentsdependency. Runpod installin theiosdirectory: -
Install Pods: Navigate to the
iosdirectory and run:cd ios pod install -
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) } } -
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 usingapiKeyandsecretKey. Requires environment initialization.makePaymentV2: Client token-based payment using aclientTokenprovided 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')),
);
}
}
✅ Success Response From the SDK :- #
{
"status": true,
"message": "Payment processed successfully.",
"charge_id": "cha_517268bad50fc1b9b",
"data": "NA",
"last_4": "4242",
"card_last_4": "4242",
"card_brand_name": "Visa",
"exp_month": "02",
"exp_year": "2029",
"payment_method": "card",
// NOTE: card_id exists ONLY when the card is saved
// or when making a payment with a previously saved card
"card_id": "card_abcdef9876543210",
// NOTE: billingInfo will only be included
// if billing details were passed in the payment config
"billingInfo": {
"address": "123 Main Street, Suite 100",
"country": "United States",
"state": "California",
"city": "San Francisco",
"postal_code": "94105"
},
// NOTE: additionalInfo will only be included
// if additional details (like phone_number, description) were passed in the payment config
"additionalInfo": {
"phone_number": "+19999999999",
"description": "Test payment for development purposes"
}
}
✅ Error Response from the SDK :- #
{
"status": false,
"message": "Error message here"
}
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
easymerchantsdkis correctly added topubspec.yaml, and Android/iOS native setups are complete. - Invalid Amount: Verify
amountis a valid number greater than 0. - Empty Payment Methods: At least one payment method (
cardorach) must be included inpaymentMethods. - Empty Recurring Intervals: If
is_recurringis true,recurringIntervalsmust include at least one interval. - JSON Serialization Errors: Ensure all configuration maps are properly formatted before calling
jsonEncode. - Authentication Errors: Verify
apiKeyandsecretKeymatch the selected environment formakePayment. - Missing Client Token: For
makePaymentV2, ensure a validclientTokenis provided. - iOS ViewController Issues: Call
setViewControllerbeforemakePaymentormakePaymentV2on 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.