Broadleaf Microservices
  • v1.0.0-latest-prod

MyFatoorah Payment Integration

Frontend Responsibilities & Flow

  1. The frontend interaction begins with creating a MyFatoorah session by making a call to Broadleaf’s PaymentTransactionServices. This endpoint will resolve the authorized customer’s id (if the customer is logged-in & not anonymous) & pass it along to the MyFatoorah Initialize Session endpoint.

  2. The session id gathered from this endpoint must be provided to the MyFatoorah JS library to initialize the credit card form.

  3. When this form is submitted, the credit card data is submitted directly to MyFatoorah to associate the payment method with the session id.

  4. From there, the session id should be stored in a PaymentTransactionServices Payment object in preparation for executing transactions.

Frontend Project Setup

// Test Environment
<script src="https://demo.myfatoorah.com/cardview/v2/session.js"></script>

// Live Environment Except Saudi Arabia
<script src="https://portal.myfatoorah.com/cardview/v2/session.js"></script>

// Live Environment For Saudi Arabia
<script src="https://sa.myfatoorah.com/cardview/v2/session.js"></script>

Example Integration

import { FC, useEffect, useState } from 'react';

import {
  Form as FormikForm,
  Formik,
} from 'formik';

import {
  useSubmitPaymentRequest,
} from '@broadleaf/payment-react';

import { CardType } from '@broadleaf/payment-js';

import { useInitiateSessionRequest } from '@broadleaf/myfatoorah-payment-services-react';
import { InitiateSessionResponse } from '@broadleaf/myfatoorah-payment-services-api';

export const MyfatoorahPaymentForm: FC = () => {
  const { error, onSubmit } = useHandleSubmit();

  const [initiateSessionResponse, setInitiateSessionResponse] =
    useState<InitiateSessionResponse>(null);

  // the MyFatoorahPaymentServicesClient from '@broadleaf/myfatoorah-payment-services-api'
  const myFatoorahPaymentServicesClient = ...;
    // the AuthState from '@broadleaf/payment-js'
  const authState = ...;

  const {
    isFetching,
    initiateSession,
    error: initiateSessionError,
  } = useInitiateSessionRequest({ myFatoorahPaymentServicesClient, authState });

  const myFatoorah = window['myFatoorah'];

  useEffect(() => {
    if (myFatoorah) {
      if (!initiateSessionResponse) {
        initiateSession().then(response => {
          setInitiateSessionResponse(response);
        });
      } else {
        const config = {
          countryCode: initiateSessionResponse.data.countryCode,
          sessionId: initiateSessionResponse.data.sessionId,
          cardViewId: 'myfatoorah-card-element',
          style: {
            cardHeight: 180,
          },
        };

        myFatoorah.init(config);
      }
    }
  }, [
    myFatoorah,
    initiateSessionResponse,
    initiateSession,
  ]);

  if (isFetching || !initiateSessionResponse) {
    return null;
  }

  return (
    <Formik
      initialValues={...}
      onSubmit={onSubmit}
      validateOnBlur
      validateOnChange={false}
    >
      {() => (
        <FormikForm>
          <div id="myfatoorah-card-element"></div>

          {(error || initiateSessionError) && (
            <strong className="block my-4 text-red-600 text-lg font-normal">
              There was an error processing your request. Please check your info and try again.
            </strong>
          )}
          <button type="submit">Submit</button>
        </FormikForm>
      )}
    </Formik>
  );
};

const useHandleSubmit = () => {
  // resolve cart
  const { cart, setCart } ...;
  // load the existing payments
  const { payments }  = ...;
  // the PaymentClient from '@broadleaf/commerce-cart'
  const paymentClient = ...;
  // the AuthState from '@broadleaf/payment-js'
  const authState = ...;

  const { handleSubmitPaymentInfo } = useSubmitPaymentRequest({
    authState,
    payments,
    ownerId: cart?.id,
    owningUserEmailAddress: cart.emailAddress,
    paymentClient,
    multiplePaymentsAllowed: false,
    rejectOnError: true,
  });

  const [error, setError] = useState(null);

  const onSubmit = async (data, actions): Promise<void> => {
    const { savePaymentForFutureUse, ...billingAddress } = data;

    const myFatoorah = window['myFatoorah'];

    let displayAttributes: Record<string, string> = {};
    let paymentName;

    const protocol = window.location.protocol.slice(0, -1);
    const host = window.location.host;

    const paymentMethodProperties = {
      sessionId: '',
      callBackUrl: `${protocol}://${host}/checkout/3ds-verification?gatewayType=MY_FATOORAH`,
      errorUrl: `${protocol}://${host}/checkout/3ds-verification?3ds-verification-success=false`,
    };

    try {
        const myFatoorahSubmitResponse = await myFatoorah.submit();

        paymentMethodProperties.sessionId = myFatoorahSubmitResponse.sessionId;

        // cardBrand will be one of the following values: Master, Visa, Mada, Amex
        const creditCardType =
          myFatoorahSubmitResponse.cardBrand === 'Master'
            ? CardType.MASTERCARD
            : myFatoorahSubmitResponse.cardBrand.toUpperCase();

        displayAttributes = {
          creditCardType,
        };

        paymentName = myFatoorahSubmitResponse.cardBrand;
    } catch (err) {
      console.error(err);
      setError(err);

      return;
    }

    const paymentRequest = {
      name: paymentName,
      type: 'CREDIT_CARD',
      gatewayType: 'MY_FATOORAH',
      amount: { amount: 11, currency: 'KWD' },
      subtotal: cart.cartPricing.subtotal,
      adjustmentsTotal: cart.cartPricing.adjustmentsTotal,
      fulfillmentTotal: cart.cartPricing.fulfillmentTotal,
      taxTotal: cart.cartPricing.totalTax,
      billingAddress,
      shouldSavePaymentForFutureUse: savePaymentForFutureUse,
      paymentMethodProperties,
      displayAttributes,
    };

    try {
      const paymentSummary = await handleSubmitPaymentInfo(paymentRequest);

    } catch (err) {
      setError(err);
    } finally {
      ...
    }
  };

  return { error, onSubmit };
};