DEV Community

RamR
RamR

Posted on

Payfort | Amazon Payment Service Integration React Native

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

allprojects {
  repositories {
    ...
    maven { url 'https://android-sdk.payfort.com' }
  }
}
Enter fullscreen mode Exit fullscreen mode

In AndroidManifest.xml add 2 permissions

  < uses-permission android:name="android.permission.INTERNET" />
  < uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Enter fullscreen mode Exit fullscreen mode

iOS
In Podfile (inside the target section)

pod 'PayFortSDK'
Enter fullscreen mode Exit fullscreen mode

In Podfile (at bottom)

 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
Enter fullscreen mode Exit fullscreen mode
pod install
Enter fullscreen mode Exit fullscreen mode

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.

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Image description

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.

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}>
    . . .
  )
}
Enter fullscreen mode Exit fullscreen mode

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.

// 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() {
Enter fullscreen mode Exit fullscreen mode

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

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',
    };
}
. . .
}
Enter fullscreen mode Exit fullscreen mode

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.

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

Step 2, concatenate the request object properties as string in ascending order. Property separator null and Key Value separator =.

  let concatenatedString = '';
    sortedKeys.forEach((ele) => {
      concatenatedString += ele + '=' + sdkTokenObj[ele];
    });
Enter fullscreen mode Exit fullscreen mode

At this point, the concatenatedString value will be like this

// concatenatedString = 'access_code=SILgpo7pWbmzuURp2qridevice_id=aAbC123zZfF56700language=enmerchant_identifier=MxvOupuGservice_command=SDK_TOKEN'
Enter fullscreen mode Exit fullscreen mode

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

    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$'

Enter fullscreen mode Exit fullscreen mode

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

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

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

const sign = hash.toString(CryptoJS.enc.Hex);
console.log('sign', sign )
// sample output
// sign = 94C38AFC7BDAE0114FC8C740EDF12416F22998241CE4B4EA70D5521233A2C882
Enter fullscreen mode Exit fullscreen mode

Now we generated the required encrypted signature value. Lets setup the request object with the signature property.

sdkTokenObj.signature = sign;
Enter fullscreen mode Exit fullscreen mode

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.

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);
    }
Enter fullscreen mode Exit fullscreen mode

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.

  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);
    }
  };
Enter fullscreen mode Exit fullscreen mode

Calling this method in useEffect for the first transaction.


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

  useEffect(() => {
    loadPageData();
  }, []);
Enter fullscreen mode Exit fullscreen mode

Step 3 - Transaction Object

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

  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,
  };
Enter fullscreen mode Exit fullscreen mode

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.

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>
  );

Enter fullscreen mode Exit fullscreen mode

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.

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);
  };
Enter fullscreen mode Exit fullscreen mode

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

  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>
. . .
Enter fullscreen mode Exit fullscreen mode

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

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

Full source code available here

Thank you !

Top comments (0)