React Native Payfort | Amazon Payment Service Integration
Tags: react, reactnative, android, ios
Published: 2024-04-17 10:45:15

Several months back I was struggling to integrate payfort payment gateway into my React Native application, even after I found some posts regarding that I couldn't achieve it. 

After multiple attempts finally I was able to complete that integration. 

I had been thinking to share this for long time but now only I got the time. 

Shall we start ?

###Contents

- Test account
- Packages & Configurations
- Base Page
- Device id
- SDK Token
- Transaction Object
- Checkout Component
- OnSuccess OnFailure
- Show Payfort
- Thats it

###Test Account

First we need a test account to simulate the process. For this, contact merchantsupport-ps@amazon.com and get our test account. Once we got and logged in into our account we need to collect this information from security settings. 

- Merchant Identifier
- Access Code
- Request Phrase

Reference > https://paymentservices-reference.payfort.com/docs/api/build/index.html#before-starting-your-integration-follow-these-steps 

###Packages & Configuration

Coming to the application, create a simple basic react native project and install these packages.

- axios (for network call)
- crypto-js (for encryption)
- rn-amazon-payment-services (for payfort)

for **rn-amazon-payment-services** we need to do some platform oriented configurations. 

**Android**
In `android\build.gradle`

```gradle
allprojects {
  repositories {
    ...
    maven { url 'https://android-sdk.payfort.com' }
  }
}
```
In `AndroidManifest.xml` add 2 permissions 

```xml
  < uses-permission android:name="android.permission.INTERNET" />
  < uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```

**iOS**
In Podfile (inside the target section)

```objectivec
pod 'PayFortSDK'
```

In Podfile (at bottom)
```objectivec
 post_install do |installer|
      installer.pods_project.targets.each do |target|
        if ['PayFortSDK'].include? target.name
          target.build_configurations.each do |config|
              config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
          end
        end
      end
    end
```

```objectivec
pod install
```

Reference > https://paymentservices-reference.payfort.com/docs/api/build/index.html#installing-the-mobile-sdk155

###Base Page

All set, lets start touching the front page. Create a simple page with one button at the center which shows the payfort payment page. 

```javascript
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <TouchableOpacity style={styles.btn}>
        <Text style={styles.text}>Proceed to Pay</Text>
      </TouchableOpacity>     
    </View>
  );
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/csjnt4plw3wt0tvrkwqj.png)

To perform a payfort transaction we need to form a transaction object which has the transaction details and an SDK token. For each and every transaction we need a unique SDK token. To generate an SDK token we need device id on which this  application runs. 

So we have to perform these 5 steps,

1. Get Device Id
2. Generate SDK Token
3. Form Transaction Object
4. Configure Checkout Component
5. Process Payfort Transaction

###Step 1 - Get Device Id

For this we have to call a method from amazon package called `getDeviceId` and set the value on page load. 

```javascript
import { getDeviceId } from 'rn-amazon-payment-services';

var deviceId = '';
export default function App() {
. . .
  const setupDeviceId = async () => {
    deviceId = await getDeviceId();
  };

  const loadPageData = async () => {
    await setupDeviceId();    
  };

  useEffect(() => {
    loadPageData();
  }, []);

  return (
    <View style={styles.container}>
    . . .
  )
}
```
###Step 2 - Generate SDK Token

This is quite tricky. 

To get the SDK token, we have to call an API with a request object. This request object should contain properties `device_id`, `language`, `merchant_identifier`, `access_code`, `service_command` and `signature`. Among these properties the signature property value has to be generated from other property values.

Shall we start forming the request object?  Lets go one by one. 

Define payfort field values of Merchant Identifier, Access code, Request Phrase which we collected from the portal. 

```javascript
// testing environment payfort field values, production values are different
const payfortFields = {
  merchant_identifier: 'PQRstuv',
  access_code: '123axisCode123',
  request_phrase: 'abcd1234$',
};

var deviceId = '';
export default function App() {
```

Now in SDK token request object, assign initial properties like this. 

```javascript
export default function App() {
const [token, setToken] = useState('');
. . .

const getSDKToken = async () => {
    const sdkTokenObj = {
      device_id: deviceId,
      merchant_identifier: payfortFields.merchant_identifier,
      access_code: payfortFields.access_code,
      language: 'en',
      service_command: 'SDK_TOKEN',
    };
}
. . .
}
```
Now in this object we need to add the signature property value which is an encrypted value of a string pattern. To create that string pattern we need to follow 4 steps. 

Step 1, sort the request object property keys in ascending alphabetical order. 

```javascript
    const sortedKeys = Object.keys(sdkTokenObj).sort((a, b) =>
      a.localeCompare(b)
    );
```
Step 2, concatenate the request object properties as string in ascending order. Property separator null and Key Value separator `=`. 

```javascript
  let concatenatedString = '';
    sortedKeys.forEach((ele) => {
      concatenatedString += ele + '=' + sdkTokenObj[ele];
    });
```

At this point, the concatenatedString value will be like this

```javascript
// concatenatedString = 'access_code=SILgpo7pWbmzuURp2qridevice_id=aAbC123zZfF56700language=enmerchant_identifier=MxvOupuGservice_command=SDK_TOKEN'
```

Step 3, Add the PASS_PHRASE or REQUEST_PHRASE at the beginning and the end of the concatenated string. 

```javascript
    concatenatedString =
      payfortFields.request_phrase +
      concatenatedString +
      payfortFields.request_phrase;
console.log('concatenated string', concatenatedString )
// concatenatedString = 'abcd1234$access_code=SILgpo7pWbmzuURp2qridevice_id=aAbC123zZfF56700language=enmerchant_identifier=MxvOupuGservice_command=SDK_TOKENabcd1234$'

```
Step 4, now generate hash value from the concatenated string using a method in `crypto-js` package 

```javascript
import CryptoJS, { SHA256 } from 'crypto-js'
. . .
  const getSDKToken = async () => {
. . .
   const hash = SHA256(concatenatedString);
   console.log('hash', hash) 
```

After following these 4 steps we prepared the string pattern. Lets encrypt this value.

```javascript
const sign = hash.toString(CryptoJS.enc.Hex);
console.log('sign', sign )
// sample output
// sign = 94C38AFC7BDAE0114FC8C740EDF12416F22998241CE4B4EA70D5521233A2C882
```
Now we generated the required encrypted signature value. Lets setup the request object with the signature property. 

```javascript
sdkTokenObj.signature = sign;
```
The request object for the API call is ready, its time to call the API and get the SDK token. Note, the API url is different for TESTING and PRODUCTION.

```javascript
import axios from 'axios'
. . .
  const getSDKToken = async () => {
. . .
    const headers = { 'Content-type': 'application/json' };
    // const productionUrl = 'https://paymentservices.payfort.com/FortAPI/paymentApi';
    const testUrl = 'https://sbpaymentservices.payfort.com/FortAPI/paymentApi';
    const response = await axios.post(testUrl, sdkTokenObj, { headers });

    if (response?.data?.sdk_token) {
      /**
       response object
       {
        access_code: '***HDZRe****P4$$$$',
        device_id: 'e34abc-123-abcdf-742-d987b67957',
        language: 'en',
        merchant_identifier: 'PQRstuv',
        response_code: '22000',
        response_message: 'Success',
        sdk_token: 'abcd1234AFGHTYUI00bnmghjklpo',
        service_command: 'SDK_TOKEN',
        signature: '660288c0e0cabcd$5e06c57bb19126e181185291195ad6ef33006191aa741604218',
        status: '22',
      }
       */
      setToken(response.data.sdk_token);
    }
```

Yeah, we have successfully got the SDK token and updated the state. So far all fine? If you missed anything the whole **getSDKToken** method FYR.

```javascript
  const getSDKToken = async () => {
    const sdkTokenObj = {
      device_id: deviceId,
      language: 'en',
      merchant_identifier: payfortFields.merchant_identifier,
      access_code: payfortFields.access_code,
      service_command: 'SDK_TOKEN',
    };

    const sortedKeys = Object.keys(sdkTokenObj).sort((a, b) =>
      a.localeCompare(b)
    );

    let concatenatedString = '';
    sortedKeys.forEach((ele) => {
      concatenatedString += ele + '=' + sdkTokenObj[ele];
    });
    concatenatedString =
      payfortFields.request_phrase +
      concatenatedString +
      payfortFields.request_phrase;
    console.log('concatenated string', concatenatedString);
    
    const hash = SHA256(concatenatedString);
    console.log('hash', hash);
    const sign = hash.toString(CryptoJS.enc.Hex);
    console.log('sign', sign);

    sdkTokenObj.signature = sign;

    const headers = { 'Content-type': 'application/json' };
    // const productionUrl  = 'https://paymentservices.payfort.com/FortAPI/paymentApi';
    const testUrl = 'https://sbpaymentservices.payfort.com/FortAPI/paymentApi';
    const response = await axios.post(testUrl, sdkTokenObj, { headers });

    if (response?.data?.sdk_token) {
      setToken(response.data.sdk_token);
    }
  };
```
Calling this method in `useEffect` for the first transaction. 

```javascript

  const loadPageData = async () => {
    await setupDeviceId();
    await getSDKToken();
  };

  useEffect(() => {
    loadPageData();
  }, []);
```
###Step 3 - Transaction Object

Form transaction object with the SDK token value and the transaction information. 

```javascript
  const paymentObject = {
    command: 'PURCHASE',
    merchant_reference: 'companyName' + Date.now().toString(),
    amount: 100, // use state variable
    currency: 'USD',
    language: 'en',
    customer_email: 'custemail@email.com',
    sdk_token: token,
  };
```
Here in this sample we are making a purchase transaction for the amount of $100. You can use a state variable which holds the amount information like cartTotal or totalPrice. 

###Step 4 - Checkout Component

In the UI part, we are using the payfort checkout component and define the values for the respective properties. 

```javascript
import { getDeviceId, StandardCheckout } from 'rn-amazon-payment-services';
. . .
  return (
    <View style={styles.container}>
      <TouchableOpacity style={styles.btn}>
        <Text style={styles.text}>Proceed to Pay</Text>
      </TouchableOpacity>
      <StandardCheckout
        showStandardCheckoutPage={showStandardCheckout}
        environment={'TEST'} // TEST or PRODUCTION
        requestCode={1}
        showLoading={true}
        showResponsePage={true}
        requestObject={paymentObject}
        onSuccess={onSuccess}
        onFailure={onFailure}
        onCancel={onCancel}
      />
    </View>
  );

```
Here `showStandardCheckout` state variable will be used to show/hide the payfort payment page. 

###Step 5 - OnSuccess OnFailure

As we defined in the component, need to write methods for onSuccess and onFailure props. 

```javascript
export default function App() {
  const [token, setToken] = useState('');
  const [showStandardCheckout, setShowStandardCheckout] = useState(false);
. . .

   const onSuccess = (response) => {
    console.log('payfort success', response);
    /**
     success response object
     {
      amount: '100',
      authorization_code: '4567891230',
      card_holder_name: 'my cust name',
      card_number: '12345******0001',
      command: 'PURCHASE',
      currency: 'USD',
      customer_email: 'custemail@email.com',
      customer_ip: '123.456.789.01',
      eci: 'ECOMMERCE',
      expiry_date: '2505',
      fort_id: '16900000098',
      language: 'en',
      merchant_reference: 'companyName202404158789',
      payment_option: 'VISA',
      response_code: '14000',
      response_message: 'Success',
      sdk_token: 'bb39b7a54abcdffghjklpoiuytrewq12',
      status: '14',
      token_name: 'abcdfghty6a84100112233440377cecfghtyuer',
    }
     */
    setToken('');
    setShowStandardCheckout(false);
  };

  const onFailure = (response) => {
    console.log('payfort failure', response);
    setToken('');
    setShowStandardCheckout(false);
  };

  const onCancel = (response) => {
    console.log('payfort cancel', response);
    setToken('');
    setShowStandardCheckout(false);
  };
```
We have to clear the SDK token on each attempt ends. 

###Step 6 - Show Payfort

After setting up the device id, SDK token, transaction object and checkout component its time to show the payfort payment page. In the `onPress` of the button

```javascript
  const openPayfort = async () => {
    if (!token) {
      await get_sdk_token();
    }
    setShowStandardCheckout(true);
  };

. . .

  return (
    <View style={styles.container}>
      <TouchableOpacity onPress={() => openPayfort()} style={styles.btn}>
        <Text style={styles.text}>Proceed to Pay</Text>
      </TouchableOpacity>
. . .
```
Have to generate SDK token on each attempt starts.

###That's It

When we click the ProceedToPay button we will be redirected to payfort payment page.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/86c0a7i4m25ln377ng8g.png)

Yes finally we achieved and successfully integarted Amazon Payment Service | Payfort into our application. 

Full source code [available here](https://gist.github.com/RaguRam1991/9f7de0aae1b7943cfb59b9fa7ca5aac8) 

Thank you !