@jimrising/easymerchantsdk-react-native

1.8.8 • 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.8"
},

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 [showConfig, setShowConfig] = useState(true);


  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(() => {
  if (Platform.OS !== 'android') {
    console.log('Event listeners skipped: not Android');
    return;
  }

  if (!RNEasymerchantsdk) {
    console.warn('RNEasymerchantsdk native module is not available.');
    return;
  }

  const easyMerchantEvents = new NativeEventEmitter(RNEasymerchantsdk);

  const successSub = easyMerchantEvents.addListener('PaymentSuccess', (data) => {
    console.log('Payment success event:', data);

    try {
      const parsed = JSON.parse(data.response);
      console.log('Parsed PaymentSuccess:', parsed);

      const billing = safeParseMaybeJSON(parsed.billingInfo);
      const additional = safeParseMaybeJSON(parsed.additional_info);

      console.log('Billing Info:', billing);
      console.log('Additional Info:', additional);

      // Replace raw with parsed versions
      parsed.billingInfo = billing;
      parsed.additional_info = additional;

      setResult(JSON.stringify(parsed, null, 2));
    } catch (err) {
      console.error('Error parsing PaymentSuccess response:', err);
    }
  });

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

    try {
      const parsed = JSON.parse(data.statusResponse);
      console.log('Parsed PaymentStatus:', parsed);

      setResult(JSON.stringify(parsed, null, 2));
    } catch (err) {
      console.error('Error parsing PaymentStatus response:', err);
    }
  });

  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();
  };
}, []);


const safeParseMaybeJSON = (value) => {
  if (typeof value === 'string') {
    try {
      return JSON.parse(value);
    } catch (e) {
      console.warn('Failed to parse JSON string:', value);
      return value;
    }
  }
  return value;
};

  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: safeParseMaybeJSON(response.billingInfo),
  additional_info: safeParseMaybeJSON(response.additional_info),
};

console.log('Parsed safe response:', parsedResponse);

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}
        />

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

        <View style={styles.toggleContainer}>
          <Text style={styles.label}>Show Configurations</Text>
          <Switch
            value={showConfig}
            onValueChange={setShowConfig}
            trackColor={{ false: '#ccc', true: '#2563EB' }}
            thumbColor={showConfig ? '#fff' : '#f4f3f4'}
          />
        </View>

        {showConfig && (
          <>
            <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}
              onChangeText={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'}
            />

            {/* Android Configuration */}
            {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)}
                />
              </>
            )}

            {/* iOS Theme Configuration */}
            {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)}
                />
              </>
            )}

            {/* GrailPay Parameters */}
            <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)}
            />

            {/* Recurring Data */}
            <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)}
            />
          </>
        )}

        <Text style={styles.sectionTitle}>SDK Response</Text>
        <Text selectable style={styles.result}>{result || 'No response yet'}</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

731

Version

1.8.8

License

MIT

Unpacked Size

28.6 MB

Total Files

3263

Last publish

Collaborators

  • jimrising
  • pavan_lyfecycle