@jimrising/easymerchantsdk-react-native

1.8.5 • Public • Published

EasyMerchantSdk React Native Implementation

To implement the EasyMerchantSdk in your React Native App project. Follow the below steps:

Add the sdk path in your project.

To add the path of sdk in your project. Open your package.json file and inside the dependencies section, add the below code and set the path of the sdk where you store on your disk.

"dependencies": {
  "@jimrising/easymerchantsdk-react-native": "^1.8.5"
},

or using command

npm i @jimrising/easymerchantsdk-react-native

Changes in android side.

Now open your android folder and there is a build.gradle file. Open it and add the below code in it.

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
           maven {
            url = uri(properties.getProperty('GITHUB_URL'))
            credentials {
                username = properties.getProperty('GITHUB_USERNAME')
                password = properties.getProperty('GITHUB_PASSWORD')
            }
        }
    }
}

Changes in IOS side.

Add below content inside the AppDelegate.swift File :-

Requirements

  • Ruby 3.2.8

Create a new file named AppDelegate.swift

import UIKit
import easymerchantsdk
import React

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
    ) -> Bool {
        let jsCodeLocation: URL

        #if DEBUG
        jsCodeLocation = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
        #else
        jsCodeLocation = Bundle.main.url(forResource: "main", withExtension: "jsbundle")!
        #endif

        let bridge = RCTBridge(
            bundleURL: jsCodeLocation,
            moduleProvider: nil,
            launchOptions: launchOptions
        )

        guard let validBridge = bridge else {
            fatalError("React Native bridge failed to initialize.")
        }

        let rootView = RCTRootView(
            bridge: validBridge,
            moduleName: "EasyMerchantTestApp",    // replace it with your app name
            initialProperties: nil
        )

        self.window = UIWindow(frame: UIScreen.main.bounds)
        let rootViewController = UIViewController()
        rootViewController.view = rootView
        self.window?.rootViewController = rootViewController
        self.window?.makeKeyAndVisible()
      
      if let easyMerchantSdkPlugin = bridge?.module(for: EasyMerchantSdkPlugin.self) as? EasyMerchantSdkPlugin {
            easyMerchantSdkPlugin.setViewController(rootViewController)
        } else {
            print("Failed to retrieve EasyMerchantSdkPlugin instance from React Native bridge.")
        }
        return true
    }
}

inside the PodFile add below


  require_relative '../node_modules/react-native/scripts/react_native_pods'
  require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

  platform :ios, 16.0
  
  pod 'easymerchantsdk', :path => '../node_modules/easymerchantsdk-react-native/ios'

How to call the sdk in App.js.

you can call the sdk using below example:

import React, { useState, useEffect } from 'react';
import {
  StyleSheet,
  Text,
  View,
  TextInput,
  Button,
  Alert,
  ScrollView,
  Platform,
  NativeModules,
  Switch,
  TouchableOpacity,
  NativeEventEmitter,
} from 'react-native';

const { RNEasymerchantsdk, EasyMerchantSdk } = NativeModules;

const externalConfig = {
  amount: '',
  email: '',
  isRecurring: false,
  isAuthenticatedACH: false,
  isSecureAuthentication: false,
  isBillingVisible: false,
  isAdditionalVisible: false,
  emailEditable: true, 
  isEmail: true, 
  billingInfo: {
    visibility: { billing: false, additional: false },
    billing: {
      address: 'San Fran, Punjab',
      country: 'USA',
      state: 'California',
      city: 'Paris',
      postal_code: '234234',
    },
    billingRequired: {
      address: true,
      country: true,
      state: true,
      city: false,
      postal_code: true,
    },
    additional: {
      name: 'Test User',
      email_address: 'test@gmail.com',
      phone_number: '21408713290',
      description: 'Test',
    },
    additionalRequired: {
      name: true,
      email_address: true,
      phone_number: true,
      description: false,
    },
  },
  themeConfiguration: {
    bodyBackgroundColor: '#0f1715',
    containerBackgroundColor: '#152321',
    primaryFontColor: '#FFFFFF',
    secondaryFontColor: '#A0B5A4',
    primaryButtonBackgroundColor: '#10B981',
    primaryButtonHoverColor: '#059669',
    primaryButtonFontColor: '#FFFFFF',
    secondaryButtonBackgroundColor: '#374151',
    secondaryButtonHoverColor: '#4B5563',
    secondaryButtonFontColor: '#E5E7EB',
    borderRadius: '8',
    fontSize: '14',
    fontWeight: 500,
    fontFamily: '"Inter", sans-serif',
  },
  grailPayParams: {
    role: 'business',
    timeout: 10,
    isSandbox: true,
    brandingName: 'Lyfecycle Payments',
    finderSubtitle: 'Search for your bank',
    searchPlaceholder: 'Enter bank name',
  },
  recurringData: {
    allowCycles: 2,
    intervals: ['weekly', 'monthly'],
    recurringStartType: 'custom',
    recurringStartDate: '07/08/2025',
  },
  androidConfig: {
    currency: 'usd',
    saveCard: true,
    saveAccount: true,
    showReceipt: true,
    showDonate: false,
    showTotal: true,
    showSubmitButton: true,
    paymentMethod: ['card', 'ach'],
    name: 'Pavan',
    fields: {
      visibility: { billing: false, additional: false },
      billing: [
        { name: 'address', required: true, value: 'New Address' },
        { name: 'country', required: true, value: 'India' },
        { name: 'state', required: true, value: 'California' },
        { name: 'city', required: true, value: 'Goa' },
        { name: 'postal_code', required: true, value: '1432456' },
      ],
      additional: [
        { name: 'name', required: true, value: 'Test User 7' },
        { name: 'email_address', required: true, value: 'usertest@gmail.com' },
        { name: 'phone_number', required: true, value: '8978967895' },
        { name: 'description', required: true, value: 'Hi This is description' },
      ],
    },
    appearanceSettings: {
      theme: 'dark',
      bodyBackgroundColor: '#121212',
      containerBackgroundColor: '#1E1E1E',
      primaryFontColor: '#FFFFFF',
      secondaryFontColor: '#B0B0B0',
      primaryButtonBackgroundColor: '#2563EB',
      primaryButtonHoverColor: '#1D4ED8',
      primaryButtonFontColor: '#FFFFFF',
      secondaryButtonBackgroundColor: '#374151',
      secondaryButtonHoverColor: '#4B5563',
      secondaryButtonFontColor: '#E5E7EB',
      borderRadius: '8',
      fontSize: '14',
      fontWeight: '500',
      fontFamily: 'Inter, sans-serif',
    },
  },
};

const App = () => {
  const [amount, setAmount] = useState(externalConfig.amount);
  const [email, setEmail] = useState(externalConfig.email);
  const [environment, setEnvironment] = useState('sandbox');
  const [isRecurring, setIsRecurring] = useState(externalConfig.isRecurring);
  const [isAuthenticatedACH, setAuthenticatedACH] = useState(externalConfig.isAuthenticatedACH);
  const [isSecureAuthentication, setSecureAuthentication] = useState(externalConfig.isSecureAuthentication);
  const [isBillingVisible, setBillingVisible] = useState(externalConfig.isBillingVisible);
  const [isAdditionalVisible, setAdditionalVisible] = useState(externalConfig.isAdditionalVisible);
  const [emailEditable, setEmailEditable] = useState(externalConfig.emailEditable); // Android
  const [isEmail, setIsEmail] = useState(externalConfig.isEmail); // iOS
  const [billingInfo, setBillingInfo] = useState(externalConfig.billingInfo);
  const [themeConfiguration, setThemeConfiguration] = useState(externalConfig.themeConfiguration);
  const [grailPayParams, setGrailPayParams] = useState(externalConfig.grailPayParams);
  const [recurringData, setRecurringData] = useState(externalConfig.recurringData);
  const [androidConfig, setAndroidConfig] = useState(externalConfig.androidConfig);
  const [result, setResult] = useState('');
  const [referenceToken, setReferenceToken] = useState('');
  const [loading, setLoading] = useState(false);
  const [apiKey, setApiKey] = useState('apiKey'); 
  const [apiSecret, setApiSecret] = useState('apiSecret');
  const [isEnvironmentLoading, setIsEnvironmentLoading] = useState(false); 


const easyMerchantEvents = new NativeEventEmitter(RNEasymerchantsdk);

useEffect(() => {
  const successSub = easyMerchantEvents.addListener('PaymentSuccess', (data) => {
    console.log('Payment success event:', data);
    const parsed = JSON.parse(data.response);
    console.log('Parsed PaymentSuccess:', parsed);

    if (parsed.billingInfo) {
      const billing = JSON.parse(parsed.billingInfo);
      console.log('Billing Info:', billing);
    }

    if (parsed.additional_info) {
      const additional = JSON.parse(parsed.additional_info);
      console.log('Additional Info:', additional);
    }

    setResult(JSON.stringify(parsed, null, 2));
  });

  const statusSub = easyMerchantEvents.addListener('PaymentStatus', (data) => {
    console.log('Raw Payment status event:', data);
    const parsed = JSON.parse(data.statusResponse);
    console.log('Parsed PaymentStatus:', parsed);

    setResult(JSON.stringify(parsed, null, 2));
  });

  const statusErrorSub = easyMerchantEvents.addListener('PaymentStatusError', (data) => {
    console.log('Payment status error event:', data);
    setResult(`Status Error: ${JSON.stringify(data)}`);
  });

  return () => {
    successSub.remove();
    statusSub.remove();
    statusErrorSub.remove();
  };
}, []);



  useEffect(() => {
    const updateEnvironment = async () => {
       const key = environment === 'staging'
        ? 'stagingApiKey'
        : 'sandboxApiKey';
      const secret = environment === 'staging'
        ? 'stagingApiSecret'
        : 'sandboxApiSecret';

      setApiKey(key);
      setApiSecret(secret);

      if (Platform.OS === 'ios') {
        setIsEnvironmentLoading(true);
        try {
          await EasyMerchantSdk.setViewController();
          await EasyMerchantSdk.configureEnvironment(environment, key, secret);
          console.log(`iOS Environment configured: ${environment} with key ${key}`);
        } catch (err) {
          console.error('iOS Initialization Error:', err);
          Alert.alert('Error', `Failed to configure iOS environment: ${err.message}`);
        } finally {
          setIsEnvironmentLoading(false);
        }
      }
    };

    updateEnvironment();
  }, [environment]);

  useEffect(() => {
    setBillingInfo(prev => ({
      ...prev,
      visibility: {
        billing: isBillingVisible,
        additional: isAdditionalVisible,
      },
    }));
  }, [isBillingVisible, isAdditionalVisible]);

  const updateBillingInfo = (section, field, value) => {
    setBillingInfo(prev => ({
      ...prev,
      [section]: {
        ...prev[section],
        [field]: value,
      },
    }));
  };

  const updateAndroidConfig = (field, value) => {
    setAndroidConfig(prev => ({
      ...prev,
      [field]: value,
    }));
  };

  const updateAndroidConfigFields = (section, index, field, value) => {
    setAndroidConfig(prev => ({
      ...prev,
      fields: {
        ...prev.fields,
        [section]: prev.fields[section].map((item, i) =>
          i === index ? { ...item, [field]: value } : item
        ),
      },
    }));
  };

  const updateAndroidConfigAppearanceSettings = (field, value) => {
    setAndroidConfig(prev => ({
      ...prev,
      appearanceSettings: {
        ...prev.appearanceSettings,
        [field]: value,
      },
    }));
  };

  const updateThemeConfiguration = (field, value) => {
    setThemeConfiguration(prev => ({
      ...prev,
      [field]: value,
    }));
  };

  const updateGrailPayParams = (field, value) => {
    setGrailPayParams(prev => ({
      ...prev,
      [field]: value,
    }));
  };

  const updateRecurringData = (field, value) => {
    setRecurringData(prev => ({
      ...prev,
      [field]: value,
    }));
  };

  const togglePaymentMethod = (method) => {
    setAndroidConfig(prev => ({
      ...prev,
      paymentMethod: prev.paymentMethod.includes(method)
        ? prev.paymentMethod.filter(m => m !== method)
        : [...prev.paymentMethod, method],
    }));
  };

  const toggleInterval = (interval) => {
    setRecurringData(prev => ({
      ...prev,
      intervals: prev.intervals.includes(interval)
        ? prev.intervals.filter(i => i !== interval)
        : [...prev.intervals, interval],
    }));
  };

  const handlePayment = async () => {
    if (!amount || isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) {
      return Alert.alert('Error', 'Please enter a valid amount');
    }
    if (!email) {
      return Alert.alert('Error', 'Please enter an email address');
    }

    setLoading(true);
    if (Platform.OS === 'android') {
      await handleAndroidBilling();
    } else {
      await handleIosBilling();
    }
  };

const handleAndroidBilling = async () => {
  const config = {
    amount,
    apiKey,
    secretKey: apiSecret,
    jsonConfig: {
      environment,
      amount,
      tokenOnly: false,
      currency: androidConfig.currency,
      saveCard: androidConfig.saveCard,
      saveAccount: androidConfig.saveAccount,
      authenticatedACH: isAuthenticatedACH,
      secureAuthentication: isSecureAuthentication,
      showReceipt: androidConfig.showReceipt,
      showDonate: androidConfig.showDonate,
      showTotal: androidConfig.showTotal,
      showSubmitButton: androidConfig.showSubmitButton,
      paymentMethod: androidConfig.paymentMethod,
      emailEditable,
      email,
      name: androidConfig.name,
      fields: {
        ...androidConfig.fields,
        visibility: {
          billing: isBillingVisible,
          additional: isAdditionalVisible,
        },
      },
      ...(isRecurring && {
        recurring: {
          enableRecurring: true,
          recurringData,
        },
      }),
      grailPayParams,
      appearanceSettings: androidConfig.appearanceSettings,
    },
  };

  try {
    const response = await RNEasymerchantsdk.makePayment(config);
    console.log('Full payment response:', response);

    const parsedResponse = {
      ...response,
      billingInfo: response.billingInfo ? JSON.parse(response.billingInfo) : null,
      additional_info: response.additional_info ? JSON.parse(response.additional_info) : null,
    };

    setResult(JSON.stringify(parsedResponse, null, 2));
  } catch (error) {
    setResult(`Error: ${error.message}`);
    Alert.alert('Payment Error', error.message);
  } finally {
    setLoading(false);
  }
};

  const handleIosBilling = async () => {
    try {
      const result = await EasyMerchantSdk.billing(
        amount,
        androidConfig.currency || 'usd',
        billingInfo,
        androidConfig.paymentMethod,
        themeConfiguration,
        false, // tokenOnly
        androidConfig.saveCard,
        androidConfig.saveAccount,
        isAuthenticatedACH,
        grailPayParams,
        'Submit',
        isRecurring,
        isRecurring ? recurringData.allowCycles : 0,
        isRecurring ? recurringData.intervals : [],
        isRecurring ? recurringData.recurringStartType : '',
        isRecurring ? recurringData.recurringStartDate : '',
        isSecureAuthentication,
        androidConfig.showReceipt,
        androidConfig.showTotal,
        androidConfig.showSubmitButton,
        isEmail,
        email,
        androidConfig.name
      );

      const refToken = result?.additionalInfo?.threeDSecureStatus?.data?.ref_token;
      if (refToken) setReferenceToken(refToken);
      setResult(JSON.stringify(result, null, 2));
    } catch (error) {
      console.error('Billing Error:', error);
      setResult(`Billing Error: ${error.message || JSON.stringify(error)}`);
    } finally {
      setLoading(false);
    }
  };

  const handleCheckStatus = async () => {
    setLoading(true);
    try {
      const response = await RNEasymerchantsdk.checkPaymentStatus();
      console.log('Full payment response:', response);
      setResult(JSON.stringify(response, null, 2));
    } catch (error) {
      setResult(`Error: ${error.message}`);
      Alert.alert('Status Check Error', error.message);
    } finally {
      setLoading(false);
    }
  };

  const handlePaymentReference = async () => {
    if (Platform.OS === 'android') {
      setResult('Payment Reference not supported on Android');
      return;
    }
    if (!referenceToken) {
      setResult('No reference token available');
      return;
    }
    try {
      const response = await EasyMerchantSdk.paymentReference(referenceToken);
      setResult(`Payment Reference:\n${JSON.stringify(response, null, 2)}`);
    } catch (error) {
      setResult(`Payment Reference Error: ${error.message || JSON.stringify(error)}`);
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={styles.container}>
      <ScrollView contentContainerStyle={styles.scrollContent}>
        <Text style={styles.title}>EasyMerchant SDK</Text>

        <Text style={styles.sectionTitle}>Basic Info</Text>
        <Text style={styles.label}>Amount</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter amount (e.g., 10.00)"
          keyboardType="decimal-pad"
          value={amount}
          onChangeText={setAmount}
        />
        <Text style={styles.label}>Email</Text>
        <TextInput
          style={styles.input}
          placeholder="Enter email"
          keyboardType="email-address"
          value={email}
          onChangeText={setEmail}
        />

      <Text style={styles.sectionTitle}>Environment</Text>
      <View style={styles.pickerContainer}>
        <Text style={styles.label}>Select Environment</Text>
        <View style={styles.buttonGroup}>
          <TouchableOpacity
            style={[
              styles.environmentButton,
              { backgroundColor: environment === 'sandbox' ? '#2563EB' : '#ccc' },
            ]}
            onPress={() => {
              console.log('Sandbox tapped, setting environment to sandbox');
              setEnvironment('sandbox');
            }}
            disabled={isEnvironmentLoading || environment === 'sandbox'}
          >
            <Text style={styles.buttonText}>Sandbox</Text>
          </TouchableOpacity>
          <TouchableOpacity
            style={[
              styles.environmentButton,
              { backgroundColor: environment === 'staging' ? '#2563EB' : '#ccc' },
            ]}
            onPress={() => {
              console.log('Staging tapped, setting environment to staging');
              setEnvironment('staging');
            }}
            disabled={isEnvironmentLoading || environment === 'staging'}
          >
            <Text style={styles.buttonText}>Staging</Text>
          </TouchableOpacity>
        </View>
      </View>


        <Text style={styles.sectionTitle}>Payment Options</Text>
        <View style={styles.toggleContainer}>
          <Text style={styles.label}>Recurring Payment</Text>
          <Switch
            value={isRecurring}
            onValueChange={setIsRecurring}
            trackColor={{ false: '#ccc', true: '#2563EB' }}
            thumbColor={isRecurring ? '#fff' : '#f4f3f4'}
          />
        </View>
        <View style={styles.toggleContainer}>
          <Text style={styles.label}>Authenticated ACH</Text>
          <Switch
            value={isAuthenticatedACH}
            onValueChange={setAuthenticatedACH}
            trackColor={{ false: '#ccc', true: '#2563EB' }}
            thumbColor={isAuthenticatedACH ? '#fff' : '#f4f3f4'}
          />
        </View>
        <View style={styles.toggleContainer}>
          <Text style={styles.label}>3DS</Text>
          <Switch
            value={isSecureAuthentication}
            onValueChange={setSecureAuthentication}
            trackColor={{ false: '#ccc', true: '#2563EB' }}
            thumbColor={isSecureAuthentication ? '#fff' : '#f4f3f4'}
          />
        </View>
        <View style={styles.toggleContainer}>
          <Text style={styles.label}>Billing Visible</Text>
          <Switch
            value={isBillingVisible}
            onValueChange={setBillingVisible}
            trackColor={{ false: '#ccc', true: '#2563EB' }}
            thumbColor={isBillingVisible ? '#fff' : '#f4f3f4'}
          />
        </View>
        <View style={styles.toggleContainer}>
          <Text style={styles.label}>Additional Info Visible</Text>
          <Switch
            value={isAdditionalVisible}
            onValueChange={setAdditionalVisible}
            trackColor={{ false: '#ccc', true: '#2563EB' }}
            thumbColor={isAdditionalVisible ? '#fff' : '#f4f3f4'}
          />
        </View>
        <Text style={styles.label}>Payment Methods (Shared)</Text>
        <View style={styles.buttonGroup}>
          <Button
            title="Card"
            onPress={() => togglePaymentMethod('card')}
            color={androidConfig.paymentMethod.includes('card') ? '#2563EB' : '#ccc'}
          />
          <Button
            title="ACH"
            onPress={() => togglePaymentMethod('ach')}
            color={androidConfig.paymentMethod.includes('ach') ? '#2563EB' : '#ccc'}
          />
        </View>
        {Platform.OS === 'android' && (
          <View style={styles.toggleContainer}>
            <Text style={styles.label}>Email Editable (Android)</Text>
            <Switch
              value={emailEditable}
              onValueChange={setEmailEditable}
              trackColor={{ false: '#ccc', true: '#2563EB' }}
              thumbColor={emailEditable ? '#fff' : '#f4f3f4'}
            />
          </View>
        )}
        {Platform.OS === 'ios' && (
          <View style={styles.toggleContainer}>
            <Text style={styles.label}>Allow Email (iOS)</Text>
            <Switch
              value={isEmail}
              onValueChange={setIsEmail}
              trackColor={{ false: '#ccc', true: '#2563EB' }}
              thumbColor={isEmail ? '#fff' : '#f4f3f4'}
            />
          </View>
        )}

        <Text style={styles.sectionTitle}>Billing Info</Text>
        <Text style={styles.label}>Billing Address</Text>
        <TextInput
          style={styles.input}
          value={billingInfo.billing.address}
          onChangeText={value => updateBillingInfo('billing', 'address', value)}
        />
        <Text style={styles.label}>Billing Country</Text>
        <TextInput
          style={styles.input}
          value={billingInfo.billing.country}
          onChangeText={value => updateBillingInfo('billing', 'country', value)}
        />
        <Text style={styles.label}>Billing State</Text>
        <TextInput
          style={styles.input}
          value={billingInfo.billing.state}
          onChangeText={value => updateBillingInfo('billing', 'state', value)}
        />
        <Text style={styles.label}>Billing City</Text>
        <TextInput
          style={styles.input}
          value={billingInfo.billing.city}
          onChangeText={value => updateBillingInfo('billing', 'city', value)}
        />
        <Text style={styles.label}>Billing Postal Code</Text>
        <TextInput
          style={styles.input}
          value={billingInfo.billing.postal_code}
          onChangeText={value => updateBillingInfo('billing', 'postal_code', value)}
        />
        <Text style={styles.label}>Billing Required: Address</Text>
        <Switch
          value={billingInfo.billingRequired.address}
          onValueChange={value => updateBillingInfo('billingRequired', 'address', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={billingInfo.billingRequired.address ? '#fff' : '#f4f3f4'}
        />
        <Text style={styles.label}>Billing Required: Country</Text>
        <Switch
          value={billingInfo.billingRequired.country}
          onValueChange={value => updateBillingInfo('billingRequired', 'country', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={billingInfo.billingRequired.country ? '#fff' : '#f4f3f4'}
        />
        <Text style={styles.label}>Billing Required: State</Text>
        <Switch
          value={billingInfo.billingRequired.state}
          onValueChange={value => updateBillingInfo('billingRequired', 'state', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={billingInfo.billingRequired.state ? '#fff' : '#f4f3f4'}
        />
        <Text style={styles.label}>Billing Required: City</Text>
        <Switch
          value={billingInfo.billingRequired.city}
          onValueChange={value => updateBillingInfo('billingRequired', 'city', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={billingInfo.billingRequired.city ? '#fff' : '#f4f3f4'}
        />
        <Text style={styles.label}>Billing Required: Postal Code</Text>
        <Switch
          value={billingInfo.billingRequired.postal_code}
          onValueChange={value => updateBillingInfo('billingRequired', 'postal_code', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={billingInfo.billingRequired.postal_code ? '#fff' : '#f4f3f4'}
        />
        <Text style={styles.label}>Additional: Name</Text>
        <TextInput
          style={styles.input}
          value={billingInfo.additional.name}
          onChangeText={value => updateBillingInfo('additional', 'name', value)}
        />
        <Text style={styles.label}>Additional: Email Address</Text>
        <TextInput
          style={styles.input}
          value={billingInfo.additional.email_address}
          onChangeText={value => updateBillingInfo('additional', 'email_address', value)}
        />
        <Text style={styles.label}>Additional: Phone Number</Text>
        <TextInput
          style={styles.input}
          value={billingInfo.additional.phone_number}
          onChangeText={value => updateBillingInfo('additional', 'phone_number', value)}
        />
        <Text style={styles.label}>Additional: Description</Text>
        <TextInput
          style={styles.input}
          value={billingInfo.additional.description}
          onChangeText={value => updateBillingInfo('additional', 'description', value)}
        />
        <Text style={styles.label}>Additional Required: Name</Text>
        <Switch
          value={billingInfo.additionalRequired.name}
          onValueChange={value => updateBillingInfo('additionalRequired', 'name', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={billingInfo.additionalRequired.name ? '#fff' : '#f4f3f4'}
        />
        <Text style={styles.label}>Additional Required: Email Address</Text>
        <Switch
          value={billingInfo.additionalRequired.email_address}
          onValueChange={value => updateBillingInfo('additionalRequired', 'email_address', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={billingInfo.additionalRequired.email_address ? '#fff' : '#f4f3f4'}
        />
        <Text style={styles.label}>Additional Required: Phone Number</Text>
        <Switch
          value={billingInfo.additionalRequired.phone_number}
          onValueChange={value => updateBillingInfo('additionalRequired', 'phone_number', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={billingInfo.additionalRequired.phone_number ? '#fff' : '#f4f3f4'}
        />
        <Text style={styles.label}>Additional Required: Description</Text>
        <Switch
          value={billingInfo.additionalRequired.description}
          onValueChange={value => updateBillingInfo('additionalRequired', 'description', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={billingInfo.additionalRequired.description ? '#fff' : '#f4f3f4'}
        />

        {Platform.OS === 'android' && (
          <>
            <Text style={styles.sectionTitle}>Android Configuration</Text>
            <Text style={styles.label}>Currency</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.currency}
              onChangeText={value => updateAndroidConfig('currency', value)}
            />
            <Text style={styles.label}>Save Card</Text>
            <Switch
              value={androidConfig.saveCard}
              onValueChange={value => updateAndroidConfig('saveCard', value)}
              trackColor={{ false: '#ccc', true: '#2563EB' }}
              thumbColor={androidConfig.saveCard ? '#fff' : '#f4f3f4'}
            />
            <Text style={styles.label}>Save Account</Text>
            <Switch
              value={androidConfig.saveAccount}
              onValueChange={value => updateAndroidConfig('saveAccount', value)}
              trackColor={{ false: '#ccc', true: '#2563EB' }}
              thumbColor={androidConfig.saveAccount ? '#fff' : '#f4f3f4'}
            />
            <Text style={styles.label}>Show Receipt</Text>
            <Switch
              value={androidConfig.showReceipt}
              onValueChange={value => updateAndroidConfig('showReceipt', value)}
              trackColor={{ false: '#ccc', true: '#2563EB' }}
              thumbColor={androidConfig.showReceipt ? '#fff' : '#f4f3f4'}
            />
            <Text style={styles.label}>Show Donate</Text>
            <Switch
              value={androidConfig.showDonate}
              onValueChange={value => updateAndroidConfig('showDonate', value)}
              trackColor={{ false: '#ccc', true: '#2563EB' }}
              thumbColor={androidConfig.showDonate ? '#fff' : '#f4f3f4'}
            />
            <Text style={styles.label}>Show Total</Text>
            <Switch
              value={androidConfig.showTotal}
              onValueChange={value => updateAndroidConfig('showTotal', value)}
              trackColor={{ false: '#ccc', true: '#2563EB' }}
              thumbColor={androidConfig.showTotal ? '#fff' : '#f4f3f4'}
            />
            <Text style={styles.label}>Show Submit Button</Text>
            <Switch
              value={androidConfig.showSubmitButton}
              onValueChange={value => updateAndroidConfig('showSubmitButton', value)}
              trackColor={{ false: '#ccc', true: '#2563EB' }}
              thumbColor={androidConfig.showSubmitButton ? '#fff' : '#f4f3f4'}
            />
            <Text style={styles.label}>Name</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.name}
              onChangeText={value => updateAndroidConfig('name', value)}
            />
            <Text style={styles.sectionTitle}>Android Fields</Text>
            {androidConfig.fields.billing.map((field, index) => (
              <View key={`billing-${index}`}>
                <Text style={styles.label}>{`Billing ${field.name}`}</Text>
                <TextInput
                  style={styles.input}
                  value={field.value}
                  onChangeText={value => updateAndroidConfigFields('billing', index, 'value', value)}
                />
                <Text style={styles.label}>{`Billing ${field.name} Required`}</Text>
                <Switch
                  value={field.required}
                  onValueChange={value => updateAndroidConfigFields('billing', index, 'required', value)}
                  trackColor={{ false: '#ccc', true: '#2563EB' }}
                  thumbColor={field.required ? '#fff' : '#f4f3f4'}
                />
              </View>
            ))}
            {androidConfig.fields.additional.map((field, index) => (
              <View key={`additional-${index}`}>
                <Text style={styles.label}>{`Additional ${field.name}`}</Text>
                <TextInput
                  style={styles.input}
                  value={field.value}
                  onChangeText={value => updateAndroidConfigFields('additional', index, 'value', value)}
                />
                <Text style={styles.label}>{`Additional ${field.name} Required`}</Text>
                <Switch
                  value={field.required}
                  onValueChange={value => updateAndroidConfigFields('additional', index, 'required', value)}
                  trackColor={{ false: '#ccc', true: '#2563EB' }}
                  thumbColor={field.required ? '#fff' : '#f4f3f4'}
                />
              </View>
            ))}
            <Text style={styles.sectionTitle}>Android Appearance Settings</Text>
            <Text style={styles.label}>Theme</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.theme}
              onChangeText={value => updateAndroidConfigAppearanceSettings('theme', value)}
            />
            <Text style={styles.label}>Body Background Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.bodyBackgroundColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('bodyBackgroundColor', value)}
            />
            <Text style={styles.label}>Container Background Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.containerBackgroundColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('containerBackgroundColor', value)}
            />
            <Text style={styles.label}>Primary Font Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.primaryFontColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('primaryFontColor', value)}
            />
            <Text style={styles.label}>Secondary Font Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.secondaryFontColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryFontColor', value)}
            />
            <Text style={styles.label}>Primary Button Background Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.primaryButtonBackgroundColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonBackgroundColor', value)}
            />
            <Text style={styles.label}>Primary Button Hover Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.primaryButtonHoverColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonHoverColor', value)}
            />
            <Text style={styles.label}>Primary Button Font Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.primaryButtonFontColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('primaryButtonFontColor', value)}
            />
            <Text style={styles.label}>Secondary Button Background Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.secondaryButtonBackgroundColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonBackgroundColor', value)}
            />
            <Text style={styles.label}>Secondary Button Hover Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.secondaryButtonHoverColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonHoverColor', value)}
            />
            <Text style={styles.label}>Secondary Button Font Color</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.secondaryButtonFontColor}
              onChangeText={value => updateAndroidConfigAppearanceSettings('secondaryButtonFontColor', value)}
            />
            <Text style={styles.label}>Border Radius</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.borderRadius}
              onChangeText={value => updateAndroidConfigAppearanceSettings('borderRadius', value)}
            />
            <Text style={styles.label}>Font Size</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.fontSize}
              onChangeText={value => updateAndroidConfigAppearanceSettings('fontSize', value)}
            />
            <Text style={styles.label}>Font Weight</Text>
            <TextInput
              style={styles.input}
              value={String(androidConfig.appearanceSettings.fontWeight)}
              onChangeText={value => updateAndroidConfigAppearanceSettings('fontWeight', parseInt(value) || '500')}
            />
            <Text style={styles.label}>Font Family</Text>
            <TextInput
              style={styles.input}
              value={androidConfig.appearanceSettings.fontFamily}
              onChangeText={value => updateAndroidConfigAppearanceSettings('fontFamily', value)}
            />
          </>
        )}

        {Platform.OS === 'ios' && (
          <>
            <Text style={styles.sectionTitle}>Theme Configuration (iOS)</Text>
            <Text style={styles.label}>Body Background Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.bodyBackgroundColor}
              onChangeText={value => updateThemeConfiguration('bodyBackgroundColor', value)}
            />
            <Text style={styles.label}>Container Background Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.containerBackgroundColor}
              onChangeText={value => updateThemeConfiguration('containerBackgroundColor', value)}
            />
            <Text style={styles.label}>Primary Font Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.primaryFontColor}
              onChangeText={value => updateThemeConfiguration('primaryFontColor', value)}
            />
            <Text style={styles.label}>Secondary Font Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.secondaryFontColor}
              onChangeText={value => updateThemeConfiguration('secondaryFontColor', value)}
            />
            <Text style={styles.label}>Primary Button Background Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.primaryButtonBackgroundColor}
              onChangeText={value => updateThemeConfiguration('primaryButtonBackgroundColor', value)}
            />
            <Text style={styles.label}>Primary Button Hover Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.primaryButtonHoverColor}
              onChangeText={value => updateThemeConfiguration('primaryButtonHoverColor', value)}
            />
            <Text style={styles.label}>Primary Button Font Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.primaryButtonFontColor}
              onChangeText={value => updateThemeConfiguration('primaryButtonFontColor', value)}
            />
            <Text style={styles.label}>Secondary Button Background Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.secondaryButtonBackgroundColor}
              onChangeText={value => updateThemeConfiguration('secondaryButtonBackgroundColor', value)}
            />
            <Text style={styles.label}>Secondary Button Hover Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.secondaryButtonHoverColor}
              onChangeText={value => updateThemeConfiguration('secondaryButtonHoverColor', value)}
            />
            <Text style={styles.label}>Secondary Button Font Color</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.secondaryButtonFontColor}
              onChangeText={value => updateThemeConfiguration('secondaryButtonFontColor', value)}
            />
            <Text style={styles.label}>Border Radius</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.borderRadius}
              onChangeText={value => updateThemeConfiguration('borderRadius', value)}
            />
            <Text style={styles.label}>Font Size</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.fontSize}
              onChangeText={value => updateThemeConfiguration('fontSize', value)}
            />
            <Text style={styles.label}>Font Weight</Text>
            <TextInput
              style={styles.input}
              value={String(themeConfiguration.fontWeight)}
              onChangeText={value => updateThemeConfiguration('fontWeight', parseInt(value) || 500)}
            />
            <Text style={styles.label}>Font Family</Text>
            <TextInput
              style={styles.input}
              value={themeConfiguration.fontFamily}
              onChangeText={value => updateThemeConfiguration('fontFamily', value)}
            />
          </>
        )}

        <Text style={styles.sectionTitle}>GrailPay Parameters</Text>
        <Text style={styles.label}>Role</Text>
        <TextInput
          style={styles.input}
          value={grailPayParams.role}
          onChangeText={value => updateGrailPayParams('role', value)}
        />
        <Text style={styles.label}>Timeout</Text>
        <TextInput
          style={styles.input}
          value={String(grailPayParams.timeout)}
          onChangeText={value => updateGrailPayParams('timeout', parseInt(value) || 10)}
        />
        <Text style={styles.label}>Is Sandbox</Text>
        <Switch
          value={grailPayParams.isSandbox}
          onValueChange={value => updateGrailPayParams('isSandbox', value)}
          trackColor={{ false: '#ccc', true: '#2563EB' }}
          thumbColor={grailPayParams.isSandbox ? '#fff' : '#f4f3f4'}
        />
        <Text style={styles.label}>Branding Name</Text>
        <TextInput
          style={styles.input}
          value={grailPayParams.brandingName}
          onChangeText={value => updateGrailPayParams('brandingName', value)}
        />
        <Text style={styles.label}>Finder Subtitle</Text>
        <TextInput
          style={styles.input}
          value={grailPayParams.finderSubtitle}
          onChangeText={value => updateGrailPayParams('finderSubtitle', value)}
        />
        <Text style={styles.label}>Search Placeholder</Text>
        <TextInput
          style={styles.input}
          value={grailPayParams.searchPlaceholder}
          onChangeText={value => updateGrailPayParams('searchPlaceholder', value)}
        />

        <Text style={styles.sectionTitle}>Recurring Data</Text>
        <Text style={styles.label}>Allow Cycles</Text>
        <TextInput
          style={styles.input}
          value={String(recurringData.allowCycles)}
          onChangeText={value => updateRecurringData('allowCycles', parseInt(value) || 2)}
        />
        <Text style={styles.label}>Intervals</Text>
        <View style={styles.buttonGroup}>
          <Button
            title="Weekly"
            onPress={() => toggleInterval('weekly')}
            color={recurringData.intervals.includes('weekly') ? '#2563EB' : '#ccc'}
          />
          <Button
            title="Monthly"
            onPress={() => toggleInterval('monthly')}
            color={recurringData.intervals.includes('monthly') ? '#2563EB' : '#ccc'}
          />
        </View>
        <Text style={styles.label}>Recurring Start Type</Text>
        <TextInput
          style={styles.input}
          value={recurringData.recurringStartType}
          onChangeText={value => updateRecurringData('recurringStartType', value)}
        />
        <Text style={styles.label}>Recurring Start Date</Text>
        <TextInput
          style={styles.input}
          value={recurringData.recurringStartDate}
          onChangeText={value => updateRecurringData('recurringStartDate', value)}
        />

        <View style={styles.buttonGroup}>
          <Button title="Pay" onPress={handlePayment}  />
          {Platform.OS === 'ios' ? (
            <Button title="Payment Ref" onPress={handlePaymentReference} disabled={loading} />
          ) : (
           <></>
          )
          }
        </View>

        <Text selectable style={styles.result}>{result}</Text>
      </ScrollView>
    </View>
  );
};

export default App;

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#F9FAFB' },
  scrollContent: { flexGrow: 1, padding: 20 },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 16,
    marginTop: 50,
    textAlign: 'center',
  },
  sectionTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    marginTop: 20,
    marginBottom: 10,
  },
  input: {
    height: 40,
    borderColor: '#ccc',
    borderWidth: 1,
    borderRadius: 5,
    paddingHorizontal: 10,
    marginBottom: 10,
  },
  pickerContainer: {
    marginBottom: 20,
  },
  toggleContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 10,
  },
  label: {
    fontSize: 16,
    fontWeight: '500',
    marginBottom: 5,
  },
  buttonGroup: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 20,
    marginTop: 20,
    gap: 10,

  },
  result: {
    fontSize: 14,
    fontFamily: 'monospace',
    color: '#333',
    marginTop: 20,
  },
  environmentButton: {
    flex: 1,
    paddingVertical: 10,
    paddingHorizontal: 20,
    borderRadius: 5,
    marginHorizontal: 5,
    alignItems: 'center',
    justifyContent: 'center',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '500',
  },
});

You can send null if billing info not available.

Package Sidebar

Install

npm i @jimrising/easymerchantsdk-react-native

Weekly Downloads

804

Version

1.8.5

License

MIT

Unpacked Size

28.6 MB

Total Files

3268

Last publish

Collaborators

  • jimrising
  • pavan_lyfecycle