Basic concepts
One of the features of the MoneyHash React Native SDK is providing a single state for each step in the payment flow. The state along with stateDetails guide the actions and methods required to proceed and complete the flow.
SDK States Table
State | Action | Intent Type |
---|---|---|
MethodSelection | The MethodSelection state contains an IntentMethods object with available payment methods. You can render these methods natively with custom styles. After the user selects a method, use moneyHash.proceedWithMethod to proceed. | Payment , Payout |
FormFields | Render form fields received in FormFields state, including card fields, billing, and shipping fields. Submit them using moneyHash.submitForm . For more information on card fields, refer to Card Form Fields. | Payment , Payout |
UrlToRender | Use moneyHash.renderForm to let MoneyHash handle the current stage of the payment/payout flow. After completing the process, MoneyHash will call the completion handler with intent details or an error. | Payment , Payout , CardToken |
SavedCardCVV | Collect CVV using the schema from stateDetails.cvvField , then use moneyHash.submitCardCVV to proceed with the payment. | Payment |
IntentForm | Use moneyHash.renderForm to let MoneyHash handle the current stage of the payment/payout flow. After completing the process, MoneyHash will call the completion handler with intent details or an error. | Payment , Payout |
IntentProcessed | Display a success confirmation UI with the intent details. | Payment , Payout |
TransactionFailed | Display a failure UI with the intent details. | Payment , Payout |
TransactionWaitingUserAction | Display a pending actions confirmation UI with the intent details and any externalActionMessage if provided in TransactionData . | Payment , Payout |
Expired | Display an expired intent UI. | Payment |
Closed | Display a closed intent UI. | Payment , Payout |
CardIntentSuccessful | Display a success UI for the created card token. | CardToken |
CardIntentFailed | Display a failure UI for the created card token. | CardToken |
States Handling
There are some intent states that you can use to customize your payment/payout experience.
When handling the FormFields
state in MoneyHash, you will deal with two primary tasks:
- Handling Billing or Shipping Data if
billingFields
orshippingFields
are passed on the state and not null. - Handling Card Form if the
tokenizeCardInfo
is passed on the state object and not null.
1. Handling Billing or Shipping Data
-
Rendering Input Fields:
Use the
InputField
data inbillingFields
orshippingFields
to render the form fields. Each field has properties likelabel
,name
,isRequired
, andmaxLength
that can be used to guide the form's construction.
if (intentState.type === 'form_fields') {
const billingFields = intentState.billingFields;
const shippingFields = intentState.shippingFields;
// Rendering billing fields
if (billingFields) {
billingFields.forEach((field) => {
console.log(`Billing Field: ${field.label}, Required: ${field.isRequired}`);
// Use this information to dynamically render input components
// Example: <TextInput label={field.label} required={field.isRequired} />
});
}
// Rendering shipping fields
if (shippingFields) {
shippingFields.forEach((field) => {
console.log(`Shipping Field: ${field.label}, Required: ${field.isRequired}`);
// Use this information to dynamically render input components
});
}
}
-
Collecting User Input:
Collect input from the form fields and store it in an object for both billing and shipping data. Each field's
name
attribute can be used as the key.
Example of collecting billing and shipping data:
const billingData = {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
};
const shippingData = {
addressLine1: '123 Main St',
city: 'Anytown',
country: 'US',
};
2. Handling Card Form
For the card form, use the tokenizeCardInfo
property to handle card data input securely.
- Common Steps (Collect Card Information)
- Scenario 1: Create New Token (Save Card)
- Scenario 2: Pay with the Collected Card Info
3. Submit Form
After collecting the necessary billing, shipping, and card data, the next step is to submit this data using the submitForm
method provided by the MoneyHash SDK.
The submitForm
method allows you to send the collected information to proceed with the payment or payout process. Ensure that the selectedMethod
passed to the API is the one from IntentDetails
.
submitForm(
intentId: string,
selectedMethodId: string,
billingData?: Record<string, string>,
shippingData?: Record<string, string>,
vaultData?: VaultData
): Promise<IntentDetails>
- Parameters:
intentId
(string): The unique identifier of the intent you are processing.selectedMethodId
(string): The ID of the selected payment method fromIntentDetails
.billingData
(optional): An object containing the billing information.shippingData
(optional): An object containing the shipping information.vaultData
(optional): Refer collect method if card data is needed.
Example
import { MoneyHashSDKBuilder } from '@moneyhash/reactnative-sdk';
const moneyHashSDK = MoneyHashSDKBuilder.setPublicKey("your_public_key") // Set your public API key here
.build();
async function submitPayment(intentDetails) {
try {
// Get form fields from the intent details
const billingFields = intentDetails.state?.billingFields || [];
const shippingFields = intentDetails.state?.shippingFields || [];
// Prepare billing data based on the input fields
const billingData = {};
billingFields.forEach((field) => {
billingData[field.name] = 'user_input_value'; // Replace with actual input values
});
// Prepare shipping data based on the input fields
const shippingData = {};
shippingFields.forEach((field) => {
shippingData[field.name] = 'user_input_value'; // Replace with actual input values
});
// Optionally collect card data if needed
const cardData = {}; // Assume card data is collected from card form if tokenizeCardInfo is present
// Submit the form data
const submittedIntentDetails = await moneyHashSDK.submitForm(
intentDetails.intent?.id || '',
intentDetails.selectedMethod || '',
billingData, // Optional
shippingData, // Optional
cardData // Optional
);
// Handle success
console.log('Form submitted successfully:', submittedIntentDetails);
} catch (error) {
// Handle error
console.error('Error submitting form:', error);
}
}
In this example:
- Billing and shipping data are extracted from the fields within the
state
inIntentDetails
. - The
submitForm
method sends the data along with theselectedMethod
fromIntentDetails
, along with optional card data, to complete the payment.
The UrlToRender
state occurs when you need to render a URL as part of the payment or payout flow. The UrlToRender
class provides two key properties:
url
: The URL that needs to be rendered.renderStrategy
: A suggestion on how the URL should be rendered (iframe
,popupIframe
, orredirect
).
In this state, you should use the moneyHash.renderForm()
method to handle the payment/payout flow. This method automatically takes care of rendering the URL based on the intent and manages the interaction flow. After the flow is completed (either successfully or with an error), it returns the corresponding IntentDetails
or throws an exception.
Steps to Handle the UrlToRender
State
UrlToRender
State-
Invoke the
renderForm()
Method:Call the
renderForm()
method with the necessaryintentId
andintentType
parameters to proceed with rendering the form and handling the current stage of the payment/payout flow.// Example of handling UrlToRender state if (intentState.type === 'url_to_render') { const intentDetails = await moneyHash.renderForm( intentId, // The unique identifier of the intent intentType, // The type of the intent, either 'payment' or 'payout' null // Optional: Custom embed style if needed ); console.log('Form rendered successfully:', intentDetails); }
-
Completion Handling:
- On Processed: The
renderForm()
method returns anIntentDetails
object that provides the final state and outcome of the process. - On Error: If an error occurs during the process, the method throws an exception. You should handle this exception appropriately to display an error message or take corrective action.
- On Processed: The
// Example of handling the UrlToRender state in your app
if (intentState.type === 'url_to_render') {
try {
const intentDetails = await moneyHash.renderForm(
intentId,
intentType, // The intent type ('payment' or 'payout')
);
console.log('Payment completed:', intentDetails);
} catch (e) {
// Handle error
console.error('Error during form rendering:', e.message);
}
}
In this example, MoneyHash handles the entire flow of rendering the URL and managing user interaction. After the form rendering is complete, it returns either IntentDetails
if processed or throws an exception if something goes wrong.
Tip: You can customize the form appearance by providing an
EmbedStyle
object, allowing for a more tailored user interface during the form rendering process.
The SavedCardCVV
state occurs when a saved card requires the user to input their CVV to proceed with the payment. In this state, you will collect the CVV from the user and submit it using the moneyHash.submitCardCVV()
method.
The SavedCardCVV
class provides two key properties:
cvvField
: AnInputField
object that describes the CVV field, including its label, validation requirements, and other metadata.cardTokenData
: Optional information about the saved card for which the CVV is being collected.
Steps to Handle the SavedCardCVV
State
SavedCardCVV
State-
Render the CVV Input Field:
- Use the
cvvField
property from theSavedCardCVV
state to render the input field for the CVV. - Ensure you apply the field validation based on the rules provided in the
cvvField
.
- Use the
-
Retrieve Card Details and CVV Field Information from the State
- Access Card Details: Use the
cardTokenData
property from theSavedCardCVV
state to access details about the saved card. - Access CVV Field Information: Use the
cvvField
property to obtain information about the CVV input field, including its label, validation requirements, and other metadata.
Example of Retrieving Card Details and CVV Field Information:
if (intentState.type === 'saved_card_cvv') {
// Access card details
const cardDetails = intentState.cardTokenData;
if (cardDetails) {
const last4 = cardDetails.last4; // e.g., '4242'
const brand = cardDetails.brand; // e.g., 'Visa'
const expiryMonth = cardDetails.expiryMonth; // e.g., '12'
const expiryYear = cardDetails.expiryYear; // e.g., '2025'
const cardHolderName = cardDetails.cardHolderName; // e.g., 'John Doe'
// Display card information to the user
console.log(`Using saved card ending with ${last4} (${brand}) expiring on ${expiryMonth}/${expiryYear}`);
// Optionally, show this information in your UI
// For example:
// <Text>{`Card: ${brand} **** **** **** ${last4}`}</Text>
// <Text>{`Expires: ${expiryMonth}/${expiryYear}`}</Text>
} else {
console.log('No card details available.');
}
// Access CVV field information
const cvvField = intentState.cvvField;
// cvvField is an InputField object with properties such as:
const fieldName = cvvField.name; // e.g., 'cvv'
const fieldLabel = cvvField.label; // e.g., 'CVV'
const isRequired = cvvField.isRequired; // e.g., true
const maxLength = cvvField.maxLength; // e.g., 4
const minLength = cvvField.minLength; // e.g., 3
const hint = cvvField.hint; // e.g., 'Enter the 3 or 4-digit code on the back of your card'
// Use cvvField information to render the input field
// For example, in your UI:
// <TextInput
// placeholder={fieldLabel}
// maxLength={maxLength}
// secureTextEntry={true}
// onChangeText={(text) => setCvv(text)}
// />
}
Available Properties in cvvField
(InputField):
type
: The type of the input field (e.g.,InputFieldType.String
).name
: The name of the field (e.g., 'cvv').label
: The label to display for the field (e.g., 'CVV').isRequired
: A boolean indicating whether the field is required.maxLength
: The maximum allowed length of the input.minLength
: The minimum required length of the input.hint
: A hint or placeholder text for the input field.readOnly
: A boolean indicating whether the field is read-only (typicallyfalse
for CVV).- Other properties as applicable.
-
Collect the CVV Value:
After the user inputs their CVV, collect the value and validate it based on the constraints provided in
cvvField
. -
Submit the CVV Using
submitCardCVV()
:Once you have the CVV from the user, use the
submitCardCVV()
method to submit the CVV and proceed with the payment.// Example of handling the SavedCardCVV state if (intentState.type === 'saved_card_cvv') { const cvv = '123'; // Example of the collected CVV from the user try { const intentDetails = await moneyHash.submitCardCVV( intentId, // The unique identifier of the intent cvv ); console.log('Payment processed successfully:', intentDetails); } catch (e) { // Handle error console.error('Error during CVV submission:', e.message); } }
-
Completion Handling:
- On Success: If the CVV is submitted successfully, the
submitCardCVV()
method returns anIntentDetails
object, containing information about the current intent state. - On Error: If an error occurs during the CVV submission, handle the exception appropriately by displaying an error message or prompting the user to retry.
- On Success: If the CVV is submitted successfully, the
The IntentForm
state occurs when MoneyHash needs to handle the current stage of the payment or payout flow via the embedded form. In this state, you will use the moneyHash.renderForm()
method to render the form and handle the completion of the process through the provided completion handler.
Steps to Handle the IntentForm
State
IntentForm
State-
Render the MoneyHash Form:
Use the
moneyHash.renderForm()
method to render the form within your app, passing the required parameters such asintentId
,intentType
, and optionally,embedStyle
to customize the form's appearance. -
Handle Completion or Error:
The form will guide the user through the payment or payout process. After the user completes the form, MoneyHash will call the completion handler with the
IntentDetails
if the process is successful, or an error if it fails.
Example of Handling the IntentForm
State
IntentForm
State// Example of handling the IntentForm state
if (intentState.type === 'intent_form') {
try {
const intentId = '<intent_id>';
const intentType = IntentType.Payment; // Example of intent type (Payment or Payout)
// Render the MoneyHash form
const intentDetails = await moneyHash.renderForm(
intentId,
intentType,
null // Optional: Pass EmbedStyle if you want to customize the form
);
console.log('Form completed successfully:', intentDetails);
} catch (e) {
// Handle error during form rendering or submission
console.error('Error rendering form:', e.message);
}
}
Other Available MoneyHash SDK Methods
In addition to the essential steps and methods previously described, the MoneyHash React Native SDK provides other methods to customize the user experience. These additional methods are presented and described next.
In the MoneyHash React Native SDK, you can reset the selected payment method for a specific intent using the resetSelectedMethod()
method. This allows your customers to go back and choose a different payment method if needed. It can be used in the following scenarios:
- Offering a button to let the customer return and select a new payment method after they already selected one.
- Allowing the customer to retry and choose a different payment method after a failed transaction.
Example of Resetting the Selected Method
try {
const intentId = '<intent_id>';
const intentType = IntentType.Payment; // Example: IntentType.Payment or IntentType.Payout
// Reset the selected method
const intentDetails = await moneyHash.resetSelectedMethod(intentId, intentType);
console.log('Intent details:', intentDetails);
// You can now render available payment methods again for the user to select a different one.
} catch (e) {
// Handle any error during resetting the selected method
console.error('Error resetting selected method:', e.message);
}
By using this method, you can offer more flexibility to your users during the payment flow, letting them go back and change their payment method even after selection.
In the MoneyHash React Native SDK, you can delete a customer's saved card using the deleteSavedCard()
method. This is useful when managing a list of customer-saved cards, allowing them to remove a card they no longer wish to keep on file.
Example of Deleting a Saved Card
try {
const cardTokenId = '<card_token_id>'; // The ID of the saved card to be deleted
const intentSecret = '<intent_secret>'; // The intent secret for verification
// Call the method to delete the saved card
await moneyHash.deleteSavedCard(cardTokenId, intentSecret);
console.log('Card deleted successfully.');
// Handle any post-deletion logic, like refreshing the card list or showing a confirmation message
} catch (e) {
// Handle any error during the card deletion process
console.error('Error deleting saved card:', e.message);
}
By using this method, you can allow users to manage their saved cards within your app, providing the option to delete cards as needed.
In the MoneyHash React Native SDK, you can update the discount for an intent using the updateDiscount()
method. This method allows you to pass the intentId
and a DiscountItem
object to adjust the discount on an intent level.
Please note the following restrictions when updating a discount:
- Discounts can't be updated for intents that have transactions.
- Can't update discount when the intent has product items that have a discount.
- Can't update discount when the intent has fees that have a discount.
- Discount value must not be more than the intent amount.
Example of Updating a Discount
try {
const intentId = '<intent_id>'; // The ID of the intent to update the discount
const discount = new DiscountItem(
new Map([
[Language.ENGLISH, 'Discount Title'],
[Language.ARABIC, 'عنوان الخصم'],
[Language.FRENCH, 'Titre de la remise'],
]),
DiscountType.AMOUNT, // Discount type: AMOUNT or PERCENTAGE
'10' // Discount value
);
// Call the method to update the discount
const updatedDiscount = await moneyHash.updateDiscount(intentId, discount);
console.log('Discount updated successfully:', updatedDiscount.amount);
} catch (e) {
// Handle any error during the discount update process
console.error('Error updating discount:', e.message);
}
This method allows you to apply or update a discount on an intent, adhering to the defined conditions and limitations.
In the MoneyHash React Native SDK, you can update the fees associated with an intent using the updateFees()
method. This method requires the intentId
and a list of FeeItem
objects, allowing you to adjust the fees at the intent level.
Please note that fees cannot be updated for intents that already have transactions.
Example of Updating Fees
try {
const intentId = '<intent_id>'; // The ID of the intent to update the fees
const fees = [
new FeeItem(
new Map([
[Language.ENGLISH, 'Service Fee'],
[Language.ARABIC, 'رسوم الخدمة'],
[Language.FRENCH, 'Frais de service'],
]),
'10' // Fee value
),
new FeeItem(
new Map([
[Language.ENGLISH, 'Handling Fee'],
[Language.ARABIC, 'رسوم المناولة'],
[Language.FRENCH, 'Frais de manutention'],
]),
'15' // Fee value
),
];
// Call the method to update the fees
const updatedFees = await moneyHash.updateFees(intentId, fees);
console.log('Fees updated successfully:', updatedFees.amount);
} catch (e) {
// Handle any error during the fees update process
console.error('Error updating fees:', e.message);
}
This method enables you to modify the fees associated with an intent while following the necessary restrictions regarding existing transactions.
In the MoneyHash React Native SDK, you can set the locale for the SDK to handle localization for different languages. This can be done by calling the setLocale()
method and passing the desired Language
enum value.
Example of Setting Locale
try {
// Set the SDK locale to Arabic
moneyHash.setLocale(Language.ARABIC);
console.log('Locale set to Arabic successfully');
} catch (e) {
// Handle error if setting locale fails
console.error('Error setting locale:', e.message);
}
You can choose from the following languages:
Language.ARABIC
Language.ENGLISH
Language.FRENCH
To authenticate and make use of the MoneyHash SDK, you need to set your public API key by calling the setPublicKey()
method. This should be done early in your application's initialization process.
Example of Setting Public Key
try {
// Set the public API key
moneyHash.setPublicKey('<your-public-api-key>');
console.log('Public key set successfully');
} catch (e) {
// Handle error if setting the public key fails
console.error('Error setting public key:', e.message);
}
Make sure to replace '<your-public-api-key>'
with your actual public API key.
You can adjust the SDK's log level using the setLogLevel()
method. The LogLevel
enum defines the possible levels of logging, allowing you to control the verbosity of the logs.
Example of Setting Log Level
try {
// Set the log level to 'debug'
moneyHash.setLogLevel(LogLevel.DEBUG);
console.log('Log level set to debug successfully');
} catch (e) {
// Handle error if setting log level fails
console.error('Error setting log level:', e.message);
}
You can set the log level to any of the following options:
LogLevel.VERBOSE
LogLevel.DEBUG
LogLevel.INFO
LogLevel.WARN
LogLevel.ERROR
LogLevel.ASSERTION
This helps manage the SDK's logging behavior, which can be useful for debugging and monitoring purposes.
Customization
With MoneyHash's React Native SDK, you can customize the appearance of the embedded form, including the submit button, input fields, and loader. To do so, you can pass an EmbedStyle
object when calling the renderForm()
method, allowing you to modify styles according to your brand’s needs.
Here’s how you can use the customization options with EmbedStyle
for different components:
Customizing Submit Button
You can customize the submit button by providing the EmbedButtonStyle
inside the EmbedStyle
. This allows you to style the button's base, hover, and focus states.
Example:
const buttonStyle = new EmbedButtonStyle(
new EmbedButtonViewStyle(
'#FFFFFF', // color
'Arial, sans-serif', // fontFamily
'bold', // fontWeight
'16px', // fontSize
null, // fontSmoothing
null, // lineHeight
null, // textTransform
null, // letterSpacing
'#000000', // background
'10px', // padding
'5px', // borderRadius
null, // boxShadow
null, // borderStyle
null, // borderColor
null // borderWidth
),
new EmbedButtonViewStyle(
null, // color
null, // fontFamily
null, // fontWeight
null, // fontSize
null, // fontSmoothing
null, // lineHeight
null, // textTransform
null, // letterSpacing
'#333333' // background on hover
),
new EmbedButtonViewStyle(
null, // color
null, // fontFamily
null, // fontWeight
null, // fontSize
null, // fontSmoothing
null, // lineHeight
null, // textTransform
null, // letterSpacing
null, // background
null, // padding
null, // borderRadius
null, // boxShadow
null, // borderStyle
'#FF0000' // borderColor on focus
)
);
Customizing Input Fields
The EmbedInputStyle
allows you to customize the base, focus, and error states of the input fields in the form. This helps you to match the input fields with the overall design of your application.
Example:
const inputStyle = new EmbedInputStyle(
new EmbedInputViewStyle(
null, // height
'10px', // padding
'#F5F5F5', // background
'5px', // borderRadius
null, // boxShadow
null, // borderStyle
'#CCCCCC', // borderColor
null, // borderWidth
null, // color
null, // fontFamily
null, // fontWeight
null, // fontSize
null, // fontSmoothing
null // lineHeight
),
new EmbedInputViewStyle(
null, // height
null, // padding
null, // background
null, // borderRadius
null, // boxShadow
null, // borderStyle
'#000000' // borderColor on focus
),
new EmbedInputViewStyle(
null, // height
null, // padding
null, // background
null, // borderRadius
null, // boxShadow
null, // borderStyle
'#FF0000' // borderColor on error
)
);
Customizing Loader
You can also customize the appearance of the loader that is shown during the form submission process.
Example:
const loaderStyle = new EmbedLoaderStyle('#000000', '#FFFFFF'); // backgroundColor, color
Applying the Custom Styles
Once you’ve customized the styles, you can apply them when calling the renderForm()
method.
Example:
async function renderCustomForm(intentId, intentType) {
try {
const customStyle = new EmbedStyle(
buttonStyle,
loaderStyle,
inputStyle
);
const result = await moneyHash.renderForm(intentId, intentType, customStyle);
if (result) {
// Handle successful form rendering
console.log('Form rendered successfully');
}
return result;
} catch (e) {
console.error('Error rendering form:', e.message);
return null;
}
}
By customizing the EmbedStyle
, you can ensure that the MoneyHash form integrates seamlessly with your app’s design and provides a consistent user experience.
Handling Errors in the SDK
In the MoneyHash React Native SDK, errors are represented using the MHError
class. This class provides detailed information about the error that occurred during a request or operation, including the error message, type, and additional details. This allows you to handle different error scenarios effectively and provide users with meaningful feedback.
The MHError
class contains the following key attributes:
message
: A user-friendly error message describing the issue.errors
: A list ofErrorDetail
objects that provide more granular details about specific fields or issues.type
: AnMHErrorType
enum value that categorizes the type of error.
The MHErrorType
enum categorizes the errors that can occur within the SDK. The possible types are:
NETWORK
: Indicates a network-related issue, such as connectivity problems or API error.UNKNOWN
: An unexpected or unknown error occurred.CARD_VALIDATION
: Validation failed for card details provided by the user.CANCELLED
: The operation was cancelled by the user or the system.APPLE_PAY_TRANSACTION_FAILED
: The Apple Pay transaction could not be completed.NOT_COMPATIBLE_WITH_APPLE_PAY
: The device is not compatible with Apple Pay.
Error Handling Example
When an error occurs during an operation, such as submitting payment details, you can catch the MHError
and handle it accordingly.
try {
// Some MoneyHash SDK operation
const result = await moneyHash.submitCardCVV(intentId, '123');
console.log('Payment submitted successfully.');
} catch (e) {
if (e instanceof MHError) {
// Handle different error types
switch (e.type) {
case MHErrorType.NETWORK:
console.error('Network error:', e.message);
break;
case MHErrorType.CARD_VALIDATION:
console.error('Card validation failed:', e.message);
break;
case MHErrorType.CANCELLED:
console.error('Operation cancelled:', e.message);
break;
case MHErrorType.APPLE_PAY_TRANSACTION_FAILED:
console.error('Apple Pay transaction failed:', e.message);
break;
case MHErrorType.NOT_COMPATIBLE_WITH_APPLE_PAY:
console.error('Device not compatible with Apple Pay:', e.message);
break;
default:
console.error('Unknown error:', e.message);
}
// Additional error details
if (e.errors && e.errors.length > 0) {
e.errors.forEach((errorDetail) => {
console.error(`Error detail - Key: ${errorDetail.key}, Message: ${errorDetail.message}`);
});
}
} else {
// Handle other types of errors
console.error('An unexpected error occurred:', e);
}
}
The ErrorDetail
class provides additional context for specific errors. Each error has:
key
: The specific field or component that caused the error.message
: A detailed message explaining the error related to that field or component.
Example of ErrorDetail
:
ErrorDetail
:const error = new ErrorDetail('cardNumber', 'Invalid card number');
console.error(`Error with field: ${error.key}, Message: ${error.message}`);
By handling errors with the MHError
class, you can ensure that your app provides clear and specific feedback to users, making the payment process more reliable and user-friendly.
This documentation provides React Native developers with the necessary guidance to handle various SDK states, use additional methods, customize the embedded form, and handle errors effectively, following the same structure and sections as the React Native documentation provided.
Updated about 1 month ago