paint-brush
How Do You Deploy a Payment Gateway? - Practical Uses for Payment APIsby@tetianastoyko
942 reads
942 reads

How Do You Deploy a Payment Gateway? - Practical Uses for Payment APIs

by Tetiana StoykoSeptember 23rd, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

A bespoke payment gateway generally necessitates a higher time and financial commitment; nevertheless, a custom payment gateway solution will significantly assist your organization in growing and prospering. By API integration of a secure payment gateway, you can earn your clients' awareness and encourage them to deal with your application. So, we put together three prominent Payment gateway API Integration tools, such as Stripe, Apple Wallet, and ApplePay (PayFort API). Since we have experience in integrating every mentioned payment API, we are sharing code fillets to help you go through the deployment process simpler.

People Mentioned

Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - How Do You Deploy a Payment Gateway? - Practical Uses for Payment APIs
Tetiana Stoyko HackerNoon profile picture

Online payment gateways have never been easier to use. These apps enable users to pay on e-commerce platforms and perform internet shopping. It's a virtual version of the actual point of sale seen in many retail stores. Retailers should be able to take digital payments swiftly, transparently, and simply via a payment gateway. The fundamental goal of any secure payment gateway is to handle transactions in such a way that your customers' money and their personal data are kept safe.


A bespoke payment gateway generally necessitates a higher time and financial commitment; nevertheless, a custom payment gateway solution will significantly assist your organization in growing and prospering. By API integration of a secure payment gateway, you can earn your clients' awareness and encourage them to deal with your application. That is why it is better to consider implementing Payment API that is well-known throughout the world and which drawbacks wouldn’t appear as a surprise.


In this article, we put together three prominent Payment gateway API Integration tools, such as Stripe, Apple Wallet, and ApplePay (PayFort API). Since we have experience in integrating every mentioned payment API, we are sharing code fillets to help you go through the deployment process simpler.

Stripe API Integration

Stripe is one of the most popular payment systems. You can easily integrate it into your project: while building your unique checkout UI, you can establish a complete API integration or just use the javascript library to classify your clients' payment information. Stripe enables processing API integration tools for creating payment gateways for your web application, iOS, or Android app.

Getting started

First, you need to create an account on the Stripe website. To obtain keys for this Payment API you should navigate to the Developers page and then choose the API keys tab:

Frontend

On frontend you need to include Stripe.js on your checkout page:


import { loadStripe } from '@stripe/stripe-js';

const stripe = await loadStripe('pk_test_*************************');


Then you need to create a form where the user can enter payment data. Stripe provides elements that can be used to build the form. For example, if you use React you can use the ‘react-stripe-elements’ module. First, you need to wrap your application into StripeProvider:


import { StripeProvider } from 'react-stripe-elements'
import { AppContainer } from 'react-hot-loader'

render(
 <StripeProvider apiKey={process.env.STRIPE_PUBLIC_KEY}>
   <AppContainer>
     /* ... */
   </AppContainer>
 </StripeProvider>,
 document.getElementById('root')
)


Your form should be wrapped into the Elements component:


import React from 'react'
import { Elements } from 'react-stripe-elements'
import CreditCardForm from './CreditCardForm'

class CheckoutPage extends React.Component {
 render() {
   return (
       <Elements>
         <CreditCardForm />
       </Elements>
   );
 }
}

module.exports = CheckoutPage;


Then you should use HOC injectStripe to build your payment form.

Please note: injectStripe should be used on the child of Elements component.

import React from 'react'
import {
 CardNumberElement,
 CardExpiryElement,
 CardCVCElement,
 injectStripe
} from 'react-stripe-elements'
import RaisedButton from 'material-ui/RaisedButton'

class CreditCardForm extends React.Component {
 state = {
   error: null,
   cardholderName: '',
 }

 handleChangeCardholderName = e => this.setState({
   cardholderName: e.target.value
 })

 handleSubmit = (e) => {
   const { cardholderName } = this.state
   const { stripe, onSubmit } = this.props

   e.preventDefault()

   this.setState({
     error: null,
   })

   stripe.createToken({ name: cardholderName })
     .then(payload => {
       if (!payload.error) {
         onSubmit(payload.token)
       } else {
         this.setState({
           error: payload.error.message,
         })
       }
     })
 }

 render() {
   const { error, cardholderName} = this.state
   const { onCancel } = this.props

   return (
     <div>
       <form onSubmit={this.handleSubmit}>
         <div className="credit-card-wrapper">
           <div className="card-general card-row">
             <div className="card-number card-input-group">
               <div className="card-input-label">Card Number</div>
               <CardNumberElement />
             </div>
           </div>
           <div className="card-additional card-row mb-0">
             <div className="card-expire card-input-group">
               <div className="card-input-label">Expires</div>
               <CardExpiryElement />
             </div>
             <div className="card-cvc card-input-group">
               <div className="card-input-label">CVC</div>
               <CardCVCElement />
             </div>
           </div>
           <div className="card-row">
             <div className="card-input-group">
               <div className="card-input-label">Cardholder Name</div>
               <div className="StripeElement">
                 <input
                   name="cardholder"
                   placeholder="John Doe"
                   type="text"
                   value={cardholderName}
                   onChange={this.handleChangeCardholderName} />
               </div>
             </div>
           </div>
         </div>
         <FlatButton primary onClick={onCancel} />
         <RaisedButton type="submit" label="Submit" />
         {
           error && <p className="form-error">{error}</p>
         }
       </form>
     </div>
   )
 }
}

module.exports = injectStripe(CreditCardForm);


The onSubmit method should pass the Stripe token to the backend and the payment will be executed there.

Backend

On the backend, you should use a secret key to execute payment or perform any other action. You can use Stripe SDK for NodeJS:

import config from 'config';
import Stripe from 'stripe';
const stripe = new Stripe(config.get('stripe.secret_key'));


First you should exchange token you received on frontend to customer id:

function createCustomer(email, token, metadata) {
  return stripe.customers.create({ email, source: token, metadata });
}


With a customer id, you will be able to perform different actions. For example, CRUD operations with customers:

function getCustomer(customerId) {
 return stripe.customers.retrieve(customerId);
}

function deleteCustomer(customerId) {
 return stripe.customers.del(customerId);
}

function updateCustomerMetadata(customerId, metadata) {
 return stripe.customers.update(customerId, { metadata });
}

function updateCustomerEmail(customerId, email) {
 return stripe.customers.update(customerId, { email });
}

function getCustomerCards(customerId) {
 return stripe.customers.listCards(customerId);
}

function createCustomerCard(customerId, cardToken) {
 return stripe.customers.createSource(customerId, { source: cardToken });
}

function deleteCustomerCard(customerId, cardId) {
 return stripe.customers.deleteCard(customerId, cardId);
}


Also, you will be able to create a charge or subscription for a customer:

function charge(stripeCustomerId, amount, metadata) {
 return stripe.charges.create({
   customer: stripeCustomerId,
   amount,
   currency: 'usd',
   description,
   metadata,
 });
}

function subscribe(stripePlanId, stripeCustomerId, quantity, metadata) 
 return stripe.subscriptions.create({
   customer: stripeCustomerId,
   items: [
     {
       plan: stripePlanId,
       quantity,
     },
   ],
   metadata,
 });
}

function unsubscribe(subscriptionId) {
 return stripe.subscriptions.del(subscriptionId);
}

Webhooks

There is also a possibility to make a subscription to some events on Stripe using webhooks. To create a webhook you should just add an endpoint and select events that will trigger your endpoint:

Here is an endpoint example for webhooks:

const config = require('config');
const stripe = require('stripe')(config.get('stripe.secret_key'));

const apiResponse = require('helpers/apiResponse');
const { STRIPE_EVENTS } = require('./constants');

const endpointSecret = config.get('stripe.endpoint_secret');

/**
* Stripe web-hook endpoint.
*
* @param req
* @param res
* @returns {Promise<void>}
*/
async function webhooks(req, res) {
 const { type, data: { object } } = req.body;

 // validate stripe signature
 const sig = req.get('stripe-signature');
 stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret);

 // filter undesired events to be processed
 const allowedEvents = [
   ...Object.values(STRIPE_EVENTS.CHARGE),
 ];

 // Ok response to all event types which we will not handle
 if (!allowedEvents.includes(type)) {
   return apiResponse.success(res, {}, 200);
 }
 
 // ...

 return apiResponse.success(res, {}, 200);
}

module.exports = {
 webhooks,
};

Apple Wallet API Integration

Apple Wallet allows customers to manage and utilize boarding passes and tickets, as well as other items like gift cards all in one tool. Its API integration will give key data when consumers need it and provide passes depending on location using Apple Wallet on iPhone, iPod touch, and Apple Watch. Passes may be programmed to appear on the user's device at the right time, such as when the user arrives at the airport or walks into a shop because Wallet is time and location-oriented.

Getting started

You need to create your Apple Developer account and get a Pass Type ID Certificate.

So, you will get a .cer file but you need to extract .p12 from it. You can do it using Keychain. This certificate has an expiration date, so, don’t forget to renew it and the other keys (mentioned below) regularly.

Prepare keys

To generate an Apple Wallet pass we need four keys. First is the .p12 file we get on the previous step. Second, you need to download Apple’s World Wide Developer Relations (WWDR) certificate. The other 2 keys can be generated using the passbook module. You should put .p12 and Apple WWDR into one directory and then run this command:

./node_modules/passbook/bin/node-passbook prepare-keys -p <path-to-directory-with-.p12-and-wwdr>


You will be asked for the Import Password. If you leave it empty you will get the keys, but then you will have issues with Apple Wallet pass generating. So, use a secure password here.


wwdr.pem and Certificates.pem will be generated. You should rename Certificates.pem to your Pass Type ID. You can find it in the Certificates.pem file. You should find there CN=Pass Type ID: pass.<pass-type-id>. So, new name for your Certificates.pem should looks like <pass-type-id>.pem

Generate the Pass for Apple Wallet

Currently, you should have a directory with keys:

Once you get all the required keys you can generate a pass. It can be done using the passbook module in this way:


const passbook = require('passbook');
const { pkpassConfig } = require('../../constants');

/**
* Generates .pkpass for apple wallet
*
* @returns {Promise<void>}
*/
async function generatePkpass(passData, isDev = false) {
 let template = passbook(pkpassConfig.PASS_TYPES.GENERIC, {
   passTypeIdentifier: pkpassConfig.PASS_TYPE_INDERTIFIER,
   teamIdentifier: pkpassConfig.TEAM_IDENTIFIER,
   organizationName: pkpassConfig.ORGANIZATION_NAME,
   description: pkpassConfig.DESCRIPTION,
 });
 template.loadImagesFrom(pkpassConfig.IMAGES_DIRECTORY);
 template.fields.barcode = pkpassConfig.BARCODE(passData);
 template.fields.foregroundColor = pkpassConfig.FOREGROUND_COLOR;
 template.fields.backgroundColor = pkpassConfig.BACKGROUND_COLOR;
 template.fields.stripColor = pkpassConfig.FOREGROUND_COLOR;
 template.fields.labelColor = pkpassConfig.LABEL_COLOR;
 template.fields.generic = pkpassConfig.GENERIC(passData);
 template.fields.serialNumber = passData.accountNumber.toString();
 template.keys(pkpassConfig.KEYS_DIRECTORY, pkpassConfig.KEYS_PASSWORD);

 return template.createPass();
}

module.exports = {
 generatePkpass,
};


There are 5 templates for Apple Wallet Pass: boardingPass, coupon, eventTicket, generic, storeCard. Pass Type Identifier, Team Identifier, Organization Name are the values from Certificates.pem (renamed previously) CN=Pass Type ID: <Pass-Type-Identifier>, OU=<Team-Identifier>, O=<OrganizationName>.


In the image directory you should have these files:




A barcode should be an object with the following structure:

{
   format: 'PKBarcodeFormatQR',
   message: 'Some message',
   messageEncoding: 'utf-8',
}


The colors should be in RGB format, for example, rgb(0, 0, 0). Keys password is the password you entered in the previous step. A generic field should have the following structure:


{
   headerFields: [{
     key: 'someKey',
     label: 'someLabel',
     value: 'someValue',
   }],
   primaryFields: [{
     key: 'someKey',
     label: 'someLabel',
     value: 'someValue',
   }],
   secondaryFields: [{
     key: 'someKey',
     label: 'someLabel',
     value: 'someValue',
   }],
   backFields: [{
     key: 'someKey',
     label: 'someLabel',
     value: 'someValue',
   }],
}

ApplePay integration using PayFort API

PayFort solution is based in the United Arab Emirates, that provides payment API to customers across the Middle East through its payment gateway. Many countries of the Middle East don’t support popular and well-known payment systems, such as Stripe, Braintree, etc. So, when you work with clients from those countries, there is a big chance that you will face with PayFort.

Integrating PayFort into your project, you will notice that the lack of an online terminal reduces the capacity to conduct invoices through the Checkfront Booking Manager.

PayFort supports in-app refunds, which means that a Checkfront compensation will instantly appear in this payment API and reimburse the consumer. This makes these API integration tools more convenient for users and increases the benefit of implementing ApplePay into your app.

Getting Started

Backend

Generate Payment Processing and Merchant identity certificates in the ApplePay developer account.


In your Payfort account, go to the "ApplePay Settings" page and upload the Payment Processing certificate you obtained from Apple. The certificate should be placed into a .p12 file together with a private key and encrypted with a password(this password you should provide in ApplePay settings). You can do it in the “Keychain Access” tool if you are using Mac or OpenSSL on Windows.

Add apple developer merchant id domain association file on your server. Set up Merchant Identifier, Access Code, SHA Type, and SHA Request Phrase in your Payfort account:

We finished with preparations, let’s write some code. At first, we should validate the merchant session.


We finished with preparations, let’s write some code. At first, we should validate the merchant session. [Docs are here]


NodeJS code example:

import * as fs from 'file-system';
import * as request from 'request';

public validateSession(validationURL: string, res: Response) {
    const options = {
      url: validationURL,
      method: 'POST',
      cert: fs.readFileSync(path.join(__dirname, '../../../identityCert.pem'), 'binary'),
      key: fs.readFileSync(path.join(__dirname, '../../../key.pem'), 'binary'),
      body: {
        merchantIdentifier: 'merchant.com.example.mystore',
        displayName: 'MyStore',
        initiative: 'web',
        initiativeContext: 'mystore.example.com'
      },
      json: true,
    };
    request(options, (err, response, body) => {
      if (err) {
        return res.status(400).send(body);
      }

      return res.send(body);
    });
}


"key" property - a private key generated with your certificate in Apple developer account

Response from Apple should look like this:


{
    ...

    "token": {
        "paymentData": {
            "data": "...",
            "signature": "...",
            "header": {
                "publicKeyHash": "...",
                "ephemeralPublicKey": "...",
                "transactionId": ".."
            },
            "version": "EC_v1"
        },
        "paymentMethod": {
            "displayName": "...",
            "network": "...",
            "type": "..."
        },
        "transactionIdentifier": "..."
    }

    ...
}


This data will be used on the next step – The PayFort payment request. Let’s prepare data for this request [Docs are here]. Now create payment body.


NodeJS code example:

const paymentBody = {
        digital_wallet: 'APPLE_PAY',
        command: 'PURCHASE',
        access_code: PAYFORT_CONFIG.APPLE_PAY_ACCESS_CODE,
        merchant_identifier: PAYFORT_CONFIG.APPLE_PAY_MERCHANT_ID,
        merchant_reference: orderId,
        amount: price * 100,
        currency: 'SAR',
        language: data.language,
        customer_email: data.customerEmail,
        apple_data: data.token.paymentData.data,
        apple_signature: data.token.paymentData.signature,
        apple_header: {
          apple_transactionId: data.token.paymentData.header.transactionId,
          apple_ephemeralPublicKey: data.token.paymentData.header.ephemeralPublicKey,
          apple_publicKeyHash: data.token.paymentData.header.publicKeyHash,
        },
        apple_paymentMethod: {
          apple_displayName: data.token.paymentMethod.displayName,
          apple_network: data.token.paymentMethod.network,
          apple_type: data.token.paymentMethod.type,
        },
        customer_name: data.customerName,
};


Signature for request should be generated and encoder with SHA Type which set up in your Payfort account(default SHA 256).


Decoded signature(JavaScript example):

const decodedSignature = `${PAYFORT_CONFIG.APPLE_PAY_REQ_PASSPHRASE}access_code=${paymentBody.access_code}` +
        `amount=${paymentBody.amount}` +
        `apple_data=${paymentBody.apple_data}` +
        `apple_header={apple_transactionId=${paymentBody.apple_header.apple_transactionId}` +
        `, apple_ephemeralPublicKey=${paymentBody.apple_header.apple_ephemeralPublicKey}` +
        `, apple_publicKeyHash=${paymentBody.apple_header.apple_publicKeyHash}}` +
        `apple_paymentMethod={apple_displayName=${paymentBody.apple_paymentMethod.apple_displayName}` +
        `, apple_network=${paymentBody.apple_paymentMethod.apple_network}` +
        `, apple_type=${paymentBody.apple_paymentMethod.apple_type}}` +
        `apple_signature=${paymentBody.apple_signature}` +
        `command=${paymentBody.command}` +
        `currency=${paymentBody.currency}` +
        `customer_email=${paymentBody.customer_email}` +
        `customer_name=${paymentBody.customer_name}` +
        `digital_wallet=${paymentBody.digital_wallet}` +
        `language=${paymentBody.language}` +
        `merchant_identifier=${paymentBody.merchant_identifier}` +
  `merchant_reference=${paymentBody.merchant_reference}${PAYFORT_CONFIG.APPLE_PAY_REQ_PASSPHRASE}`;


Be careful, each character is important. Any typo will cause a "Signature mismatch" error. Let's encrypt the signature:

const hash = crypto
        .createHash('sha256')
        .update(decodedSignature, 'utf8')
        .digest('hex');


Attach signature to payment body and make a payment request:

      paymentBody.signature = hash;
      const options = {
        url: 'https://paymentservices.payfort.com/FortAPI/paymentApi',
        method: 'POST',
        body: paymentBody,
        json: true,
      };

      const paymentRequestResult = await request(options);


The response should contain status 14 meaning that the transaction was successful.

Frontend

Create index.html, style.css, and script.js files and place them as static ones on your server.

Set up ApplePay button [Docs for CSS are here] [Docs for JavaScript are here]. Write a function that will check if ApplePay is available in the script.js file:

const init = () => {
  const isApplePayAvailable = window.ApplePaySession;
  if(isApplePayAvailable) {
    // Show button
      ...
    // Add "click" event listener on the button which will trigger start apple pay session
    setButtonClickListener();
  } else {
    // Error: Apple Pay is unavailable
  }
}


Add ApplePay button listener which starts the session with payment detail:

const setButtonClickListener = () => {
    document
      .getElementById("appleButton")
      .addEventListener("click", function () {
        startApplePaySession({
          currencyCode: "SAR",
          countryCode: "SA",
          merchantCapabilities: [
            "supports3DS",
            "supportsCredit",
            "supportsDebit",
          ],
          supportedNetworks: config.payments.acceptedCardSchemes,
          shippingType: "shipping",
          total: {
            label: config.shop.shop_name,
            amount: config.shop.product_price,
            type: "final",
          },
        });
      });
  };


Let’s add the ‘start ApplePay Session’ method implementation. It is responsible for starting sessions with provided payment details and tracking events.

const startApplePaySession = (config) => {
    const applePaySession = new ApplePaySession(6, config);
    handleApplePayEvents(applePaySession);
    applePaySession.begin();
};


‘Handle ApplePay Events’ method:

const handleApplePayEvents = (appleSession) => {
    // This is the first event that Apple triggers. Here you need to validate the
    // Apple Pay Session from your Back-End

    appleSession.onvalidatemerchant = function (event) {
      validateApplePaySession(event.validationURL, function (merchantSession) {
        appleSession.completeMerchantValidation(merchantSession);
      });
    };

    appleSession.onpaymentauthorized = function (event) {
      performTransaction(event.payment, appleSession, function (outcome) {
        if (outcome) {
          appleSession.completePayment(ApplePaySession.STATUS_SUCCESS);
        } else {
          appleSession.completePayment(ApplePaySession.STATUS_FAILURE);
        }
      });
    };
  };


To validate merchant events should request validation on our Backend and complete merchant validation with the response. The ‘validateApplePaySession’ is responsible for sending requests:

const validateApplePaySession = (appleUrl, callback) => {
    axios
      .post('url of the backend API that triggers validateSession method which we implemented', {
        url: appleUrl + "",
      })
      .then(function (response) {
        callback(response.data);
      })
      .catch((err) => {
        alert(err, "ApplePaySession");
      });
  };


After validation succeeds, on payment authorized event will be triggered. The handler should make a transaction:

const performTransaction = (details, appleSession, callback) => {
    const payload = {
      token: details,
      customerEmail: '[email protected]',
      orderId: 'HERE IS ORDER ID',
      language: 'AR',
      customerName: "customerName",
    };
    axios
      .post(`Backend API url to Payfort payment request route`, payload)
      .then(function (response) {
        callback(response.data);
      })
      .catch((e) => {
        appleSession.completePayment(ApplePaySession.STATUS_FAILURE);
      });
  };

That’s it! Now, go to your static index.html file. If your device supports ApplePay, the payment button should appear and you are ready to test your payment API!

Summary

Whether your application is connected to E-Commerce and Online Shopping or not, Payment Gateway is a necessary solution for each software that offers purchases of goods or services. API integration of such solutions may expand the functionality of your platform and create a better user experience.


Also posted on Incora blog: https://incorainc.com/how-to-deploy-a-payment-gateway-payment-api-practical-use/