moneyhash_payment 4.0.5
moneyhash_payment: ^4.0.5 copied to clipboard
MoneyHash is a Super-API infrastructure for payment orchestration and revenue operations in emerging markets.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:moneyhash_payment/data/apple_pay/apple_pay_receipt_params.dart';
import 'package:moneyhash_payment/data/apple_pay_configuration.dart';
import 'package:moneyhash_payment/data/card/card_field_type.dart';
import 'package:moneyhash_payment/data/card_form_configuration.dart';
import 'package:moneyhash_payment/data/collectible_billing_data.dart';
import 'package:moneyhash_payment/data/google_pay/native_google_pay_config.dart';
import 'package:moneyhash_payment/data/intent_methods.dart';
import 'package:moneyhash_payment/data/intent_operation.dart';
import 'package:moneyhash_payment/data/intent_state.dart';
import 'package:moneyhash_payment/data/intent_type.dart';
import 'package:moneyhash_payment/data/method_metadata.dart';
import 'package:moneyhash_payment/data/methods/get_methods_params.dart';
import 'package:moneyhash_payment/data/native_pay_data.dart';
import 'package:moneyhash_payment/log/log_level.dart';
import 'package:moneyhash_payment/vault/card_collector.dart';
import 'package:moneyhash_payment/vault/widget/secure_text_field.dart';
import 'package:moneyhash_payment/vault/card_form_builder.dart';
import 'package:moneyhash_payment/moneyhash_payment.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CardForm? _cardForm;
MoneyHashSDK moneyHashSDK = MoneyHashSDKBuilder()
.setNativeGooglePayConfig(NativeGooglePayConfig(
environment: GooglePayEnvironment.test,
collectibleBillingData: [],
))
.setNativeApplePayConfig(ApplePayConfiguration(
collectibleBillingData: [
CollectibleBillingData.email,
],
merchantDisplayName: "Kero Store",
))
.build();
@override
void initState() {
_cardForm = CardFormBuilder()
.setCardHolderNameField((state) {
// print("CardHolderName: ${state?.isValid}");
// print("CardHolderName: ${state?.errorMessage}");
})
.setCardNumberField((state) {
// print("CardNumber: ${state?.isValid}");
// print("CardNumber: ${state?.errorMessage}");
// print("CardNumber: ${state?.length}");
})
.setCVVField((state) {
// print("CVV: ${state?.isValid}");
// print("CVV: ${state?.errorMessage}");
})
.setExpiryMonthField((state) {
// print("ExpireMonth: ${state?.isValid}");
// print("ExpireMonth: ${state?.errorMessage}");
})
.setExpiryYearField((state) {
// print("ExpireYear: ${state?.isValid}");
// print("ExpireYear: ${state?.errorMessage}");
})
.setCardBrandChangeListener((cardBrand) {
// print("CardBrand: ${cardBrand?.brand}");
// print("CardBrand: ${cardBrand?.brandIconUrl}");
// print("CardBrand: ${cardBrand?.first6Digits}");
})
.setConfiguration(CardFormConfiguration(
isCardHolderNameRequired: true,
// enableCardNumberValidation: false
))
.build();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Testing card"),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: <Widget>[
SecureTextField(
label: "cardNumber",
placeholder: "cardNumber",
cardForm: _cardForm!,
type: CardFieldType.cardNumber,
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: SecureTextField(
label: "expireMonth",
placeholder: "MM",
cardForm: _cardForm!,
type: CardFieldType.expiryMonth,
),
),
const SizedBox(width: 8),
Expanded(
child: SecureTextField(
label: "expireYear",
placeholder: "YY",
cardForm: _cardForm!,
type: CardFieldType.expiryYear,
),
),
const SizedBox(width: 8),
Expanded(
flex: 2,
child: SecureTextField(
label: "cvv",
placeholder: "***",
cardForm: _cardForm!,
type: CardFieldType.cvv,
),
),
],
),
const SizedBox(height: 8),
SecureTextField(
label: "cardHolderName",
placeholder: "John Doe",
cardForm: _cardForm!,
type: CardFieldType.cardHolderName,
),
const SizedBox(height: 16),
Expanded(
child: ListView(
children: [
_buildScenarioButton(
"Submit Card Form",
() => _submitCardForm(),
),
_buildScenarioButton(
"🔍 Test Card Bin Lookup",
() => _testCardBinLookup(),
),
_buildScenarioButton(
"🍎 Test Apple Pay Flow",
() => _testApplePayFlow(),
),
_buildScenarioButton(
"📱 Check Apple Pay Compatibility",
() => _checkApplePayCompatibility(),
),
_buildScenarioButton(
"🔍 Test Apple Pay Bin Lookup",
() => _testApplePayBinLookup(),
),
_buildScenarioButton(
"💳 Test Google Pay Flow",
() => _testGooglePayFlow(),
),
_buildScenarioButton(
"📱 Check Google Pay Compatibility",
() => _checkGooglePayCompatibility(),
),
_buildScenarioButton(
"🚀 Test Google Pay with proceedWithMethod",
() => _testGooglePayWithProceedWithMethod(),
),
_buildScenarioButton(
"📋 Get Payment Methods",
() => _getPaymentMethods(),
),
_buildScenarioButton(
"🔧 Get Intent Details",
() => _getIntentDetails(),
),
_buildScenarioButton(
"🖼️ Test Render Form (Custom Style)",
() => _testRenderFormCustomStyle(),
),
_buildScenarioButton(
"🖼️ Test Render Form (Default Style)",
() => _testRenderFormDefaultStyle(),
),
_buildScenarioButton(
"📋 Test Subscription Flow",
() => _testSubscriptionFlow(),
),
_buildScenarioButton(
"🔍 Debug Subscription Embed Flow",
() => _debugSubscriptionEmbedFlow(),
),
],
),
),
],
),
),
);
}
// Configuration constants
static const String INTENT_ID = 'gEbGy4R';
static const String PUBLIC_KEY = 'public.DxJyVTDp.MAIvOrqToMG0eJxPYbimAjRu9Pn5Oolg6bKgIyuv';
static const double TEST_AMOUNT = 10;
static const String TEST_CURRENCY = 'AED';
Widget _buildScenarioButton(String title, VoidCallback onPressed) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: ElevatedButton(
onPressed: onPressed,
child: Text(title),
),
);
}
void _initializeSDK() {
moneyHashSDK.setPublicKey(PUBLIC_KEY);
moneyHashSDK.setLogLevel(LogLevel.verbose);
print('✅ SDK initialized with public key');
}
Future<void> _submitCardForm() async {
try {
bool isFormValid = await _cardForm?.isValid() ?? false;
print('Card form validity: $isFormValid');
print('Using intent ID: $INTENT_ID');
_initializeSDK();
// Step 1: Collect card data from secure form
var vaultData = await _cardForm?.collect();
print('💳 Card vault data collected: $vaultData');
// Step 2: Proceed with CARD method
print('🚀 Proceeding with CARD method for intent: $INTENT_ID');
var proceedResult = await moneyHashSDK.proceedWithMethod(
INTENT_ID,
IntentType.payment,
'CARD',
MethodType.paymentMethod,
null,
null,
);
print('✅ Proceed with CARD result: $proceedResult');
// Step 3: Pay with collected card data
var payResult = await _cardForm?.pay(
INTENT_ID,
vaultData!,
true,
null,
null,
);
print('✅ Card payment result: $payResult');
} catch (e) {
print('❌ Card payment error: $e');
}
}
Future<void> _testCardBinLookup() async {
try {
print('🔍 Starting card bin lookup test...');
bool isFormValid = await _cardForm?.isValid() ?? false;
print('Card form validity: $isFormValid');
if (!isFormValid) {
print('⚠️ Card form is not valid, please fill in valid card details');
return;
}
_initializeSDK();
// Collect card data from the form
var vaultData = await _cardForm?.collect();
print('💳 Card vault data for bin lookup: $vaultData');
// Perform bin lookup using the collected card data
var lookupResult = await _cardForm?.binLookup(vaultData);
print('✅ Card bin lookup completed successfully: $lookupResult');
} catch (error) {
print('❌ Card bin lookup error: $error');
}
}
Future<void> _testApplePayFlow() async {
try {
print('🍎 Starting complete Apple Pay flow with intent: $INTENT_ID');
_initializeSDK();
// Check compatibility
bool isCompatible = await moneyHashSDK.isDeviceCompatibleWithApplePay();
print('📱 Device Apple Pay compatibility: $isCompatible');
if (!isCompatible) {
throw Exception('Device not compatible with Apple Pay');
}
// Get methods and find Apple Pay
var methods = await moneyHashSDK.getMethods(
GetMethodsParams.withCurrency(
amount: TEST_AMOUNT,
currency: TEST_CURRENCY,
),
);
print('📋 Payment methods retrieved: $methods');
final applePayMethod = methods.expressMethods?.firstWhere(
(em) => em.nativePayData != null && em.nativePayData is ApplePayData,
orElse: () => throw Exception('Apple Pay method not available'),
);
final applePayData = applePayMethod?.nativePayData as ApplePayData;
print('✅ Apple Pay method found: ${applePayData.toString()}');
// Generate receipt using ApplePayReceiptParams with the actual Apple Pay data
var receipt = await moneyHashSDK.generateApplePayReceipt(
ApplePayReceiptParams.withApplePayData(TEST_AMOUNT, applePayData),
);
print('✅ Apple Pay receipt generated successfully');
// Proceed with payment
var proceedResult = await moneyHashSDK.proceedWithMethod(
INTENT_ID,
IntentType.payment,
'APPLE_PAY',
MethodType.paymentMethod,
null,
null,
);
print('✅ Proceed with Apple Pay result: $proceedResult');
// Submit receipt
var submitResult = await moneyHashSDK.submitPaymentReceipt(INTENT_ID, receipt!);
print('✅ Apple Pay receipt submitted successfully: $submitResult');
print('🎉 Apple Pay flow completed successfully for intent: $INTENT_ID');
} catch (error) {
print('❌ Apple Pay flow error: $error');
}
}
Future<void> _checkApplePayCompatibility() async {
try {
bool isCompatible = await moneyHashSDK.isDeviceCompatibleWithApplePay();
print('📱 Device Apple Pay compatible: $isCompatible');
} catch (error) {
print('❌ Compatibility check error: $error');
}
}
Future<void> _testApplePayBinLookup() async {
try {
_initializeSDK();
var methods = await moneyHashSDK.getMethods(
GetMethodsParams.withCurrency(
amount: TEST_AMOUNT,
currency: TEST_CURRENCY,
),
);
final applePayMethod = methods.expressMethods?.firstWhere(
(em) => em.nativePayData != null && em.nativePayData is ApplePayData,
orElse: () => throw Exception('Apple Pay method not available'),
);
final applePayData = applePayMethod?.nativePayData as ApplePayData;
var receipt = await moneyHashSDK.generateApplePayReceipt(
ApplePayReceiptParams.withApplePayData(TEST_AMOUNT, applePayData),
);
// Use the actual method ID from applePayData
var lookupData = await moneyHashSDK.getApplePayBinLookup(
receipt!,
applePayData.methodID!, // Use the actual method ID from the apple pay data
);
print('✅ Apple Pay bin lookup completed: $lookupData');
} catch (error) {
print('❌ Apple Pay bin lookup error: $error');
}
}
Future<void> _testGooglePayFlow() async {
try {
print('💳 Starting complete Google Pay flow with intent: $INTENT_ID');
_initializeSDK();
// Check compatibility
bool isCompatible = await moneyHashSDK.isReadyForGooglePay();
print('📱 Device Google Pay compatibility: $isCompatible');
if (!isCompatible) {
throw Exception('Device not compatible with Google Pay');
}
// Get methods and find Google Pay
var methods = await moneyHashSDK.getMethods(
GetMethodsParams.withCurrency(
amount: TEST_AMOUNT,
currency: TEST_CURRENCY,
),
);
final googlePayMethod = methods.expressMethods?.firstWhere(
(em) => em.nativePayData != null && em.nativePayData is GooglePayData,
orElse: () => throw Exception('Google Pay method not available'),
);
final googlePayData = googlePayMethod?.nativePayData as GooglePayData;
print('✅ Google Pay method found: ${googlePayData.toString()}');
// Generate receipt using actual Google Pay data with fallbacks
var receipt = await moneyHashSDK.generateGooglePayReceipt(
googlePayData.currencyCode ?? TEST_CURRENCY,
googlePayData.amount ?? TEST_AMOUNT,
googlePayData.countryCode ?? "AE",
googlePayData.gateway ?? "example",
googlePayData.gatewayMerchantId ?? "exampleGatewayMerchantId",
googlePayData.merchantId ?? "BCR2DN6T2MM7YIJK",
googlePayData.merchantName ?? "Example Merchant",
googlePayData.allowedCardNetworks ?? ["VISA", "MASTERCARD"],
googlePayData.allowedCardAuthMethods ?? ["CRYPTOGRAM_3DS"],
);
print('✅ Google Pay receipt generated successfully');
// Proceed with payment
var proceedResult = await moneyHashSDK.proceedWithMethod(
INTENT_ID,
IntentType.payment,
'GOOGLE_PAY',
MethodType.paymentMethod,
null,
null,
);
print('✅ Proceed with Google Pay result: $proceedResult');
// Submit receipt
var submitResult = await moneyHashSDK.submitPaymentReceipt(INTENT_ID, receipt!);
print('✅ Google Pay receipt submitted successfully: $submitResult');
print('🎉 Google Pay flow completed successfully for intent: $INTENT_ID');
} catch (error) {
print('❌ Google Pay flow error: $error');
}
}
Future<void> _checkGooglePayCompatibility() async {
try {
bool isCompatible = await moneyHashSDK.isReadyForGooglePay();
print('📱 Device Google Pay compatible: $isCompatible');
} catch (error) {
print('❌ Compatibility check error: $error');
}
}
Future<void> _testGooglePayWithProceedWithMethod() async {
try {
print('🚀 Starting Google Pay flow with proceedWithMethod...');
_initializeSDK();
// Check compatibility
bool isCompatible = await moneyHashSDK.isReadyForGooglePay();
if (!isCompatible) {
throw Exception('Device not compatible with Google Pay');
}
// Proceed with GOOGLE_PAY method
print('🚀 Proceeding with GOOGLE_PAY method for intent: $INTENT_ID');
var proceedResult = await moneyHashSDK.proceedWithMethod(
INTENT_ID,
IntentType.payment,
'GOOGLE_PAY',
MethodType.paymentMethod,
null,
null,
);
print('✅ Proceed with GOOGLE_PAY result: $proceedResult');
// Get intent details from the result
print('📋 Getting intent details to check state...');
var intentDetails = await moneyHashSDK.getIntentDetails(INTENT_ID, IntentType.payment);
print('✅ Intent details: $intentDetails');
// Check if state is native pay and get Google Pay data
if (intentDetails.intentState?.runtimeType.toString().contains('NativePay') ?? false) {
print('✅ Intent state is native_pay');
// Cast to NativePay to get the Google Pay data
final nativePayState = intentDetails.intentState as NativePay;
final googlePayData = nativePayState.nativePayData as GooglePayData;
print('✅ Google Pay data from intent: ${googlePayData.toString()}');
// Use proceedWithGooglePay with the data from intent state
var finalResult = await moneyHashSDK.proceedWithGooglePay(
INTENT_ID,
googlePayData.currencyCode ?? TEST_CURRENCY,
googlePayData.amount ?? TEST_AMOUNT,
googlePayData.countryCode ?? "AE",
googlePayData.gateway ?? "example",
googlePayData.gatewayMerchantId ?? "exampleGatewayMerchantId",
googlePayData.merchantId ?? "BCR2DN6T2MM7YIJK",
googlePayData.merchantName ?? "Example Merchant",
googlePayData.allowedCardNetworks,
googlePayData.allowedCardAuthMethods,
);
print('✅ Google Pay with proceedWithGooglePay completed: $finalResult');
} else {
print('❌ Intent state is not native_pay. State type: ${intentDetails.intentState?.runtimeType}');
}
} catch (error) {
print('❌ Google Pay with proceedWithMethod error: $error');
}
}
Future<void> _getPaymentMethods() async {
try {
_initializeSDK();
var methods = await moneyHashSDK.getMethods(
GetMethodsParams.withCurrency(
amount: TEST_AMOUNT,
currency: TEST_CURRENCY,
),
);
print('📋 Available payment methods: $methods');
} catch (error) {
print('❌ Get methods error: $error');
}
}
Future<void> _getIntentDetails() async {
try {
_initializeSDK();
print('Getting intent details for: $INTENT_ID');
var details = await moneyHashSDK.getIntentDetails(INTENT_ID, IntentType.payment);
print('🔧 Intent details: $details');
} catch (error) {
print('❌ Get intent details error: $error');
}
}
Future<void> _testRenderFormCustomStyle() async {
try {
print('📱 Starting render form test with intent: $INTENT_ID');
_initializeSDK();
// Render the payment form with custom style (pass null for now, can be enhanced)
var result = await moneyHashSDK.renderForm(
INTENT_ID,
IntentType.payment,
null, // Custom embed style would go here
);
print('✅ Render form completed successfully: $result');
} catch (error) {
print('❌ Render form error: $error');
}
}
Future<void> _testRenderFormDefaultStyle() async {
try {
print('📱 Starting render form test (default style) with intent: $INTENT_ID');
_initializeSDK();
// Render the payment form without custom styling
var result = await moneyHashSDK.renderForm(
INTENT_ID,
IntentType.payment,
null,
);
print('✅ Render form (default style) completed successfully: $result');
} catch (error) {
print('❌ Render form (default style) error: $error');
}
}
Future<void> _testSubscriptionFlow() async {
try {
print('📋 Starting subscription flow test...');
_initializeSDK();
// Configuration for subscription test
const customerID = '33dee031-d237-482f-ae6c-3f4091c0c8fb';
const embedID = '4lgb5LB';
print('📊 Getting subscription plan groups...');
var subscriptionGroups = await moneyHashSDK.getSubscriptionPlanGroups(2, 1, "aed");
print('✅ Subscription groups retrieved: $subscriptionGroups');
print('📋 Getting subscription plans for group: $embedID');
var subscriptionPlans = await moneyHashSDK.getSubscriptionPlans(embedID, customerID);
print('✅ Subscription plans retrieved: $subscriptionPlans');
const planIdx = 0;
if (subscriptionPlans.isNotEmpty && subscriptionPlans[planIdx].id != null) {
print('🎯 Selecting subscription plan: ${subscriptionPlans[planIdx].id}');
var intentDetails = await moneyHashSDK.selectSubscriptionPlan(
embedID,
customerID,
subscriptionPlans[planIdx].id!,
);
print('✅ Subscription plan selected, intent details: $intentDetails');
// Test renderSubscriptionEmbed with the intent ID from selected plan
if (intentDetails?.intent?.id != null) {
var subscriptionResult = await moneyHashSDK.renderSubscriptionEmbed(
intentDetails!.intent!.id!,
null,
);
print('✅ Render subscription embed completed successfully: $subscriptionResult');
}
print('📋 Subscription flow completed successfully');
} else {
print('❌ No subscription plans available or plan ID missing');
throw Exception('No subscription plans available');
}
} catch (error) {
print('❌ Subscription flow error: $error');
}
}
Future<void> _debugSubscriptionEmbedFlow() async {
try {
print('🔍 Starting DEBUG subscription embed flow...');
_initializeSDK();
// Configuration for subscription test
const customerID = 'fe3e99eb-97b2-481f-a0cb-9b9898dbd989';
const embedID = '4lgb5LB';
// Step 1: Get subscription plans
print('📋 Step 1: Getting subscription plans...');
var subscriptionPlans = await moneyHashSDK.getSubscriptionPlans(embedID, customerID);
print('✅ Subscription plans retrieved: $subscriptionPlans');
if (subscriptionPlans.isEmpty) {
throw Exception('No subscription plans available');
}
// Step 2: Select subscription plan
print('🎯 Step 2: Selecting subscription plan...');
const planIdx = 2;
final selectedPlan = subscriptionPlans[planIdx];
print('Selected plan: $selectedPlan');
if (selectedPlan.id == null) {
throw Exception('Selected subscription plan has no ID');
}
var intentDetails = await moneyHashSDK.selectSubscriptionPlan(
embedID,
customerID,
selectedPlan.id!,
);
print('✅ Subscription plan selected, intent details: $intentDetails');
// Step 3: Render subscription embed with error handling
if (intentDetails?.intent?.id != null) {
print('📱 Step 3: Testing renderSubscriptionEmbed...');
print('Intent ID: ${intentDetails!.intent!.id}');
try {
var subscriptionResult = await moneyHashSDK.renderSubscriptionEmbed(
intentDetails.intent!.id!,
null,
).timeout(const Duration(seconds: 30));
print('✅ Render subscription embed completed successfully: $subscriptionResult');
} catch (embedError) {
print('❌ renderSubscriptionEmbed error: $embedError');
rethrow;
}
} else {
throw Exception('No intent ID found in subscription plan details');
}
} catch (error) {
print('❌ DEBUG Subscription embed flow error: $error');
}
}
}