1.2.1 • Published 3 years ago

paygate-sdk v1.2.1

Weekly downloads
11
License
MIT
Repository
github
Last release
3 years ago

PayGate SDK

GitHub Release License Total alerts Language grade: JavaScript

Introduction

This documentation is currently in the process of being written and refined, and is not in a final state.

This project is made up of a few parts which can be used seperately or in combination, to aid with building an integration to the South African payment provider PayGate.

The main components are:

  • A standalone Typescript and Javascript (NodeJS) client to interface with the PayGate API
  • ExpressJS middleware which can be used to rapidly create endpoints for payment processing
  • A utility module, also available in a browser friendly version, which can help with common PayGate tasks, such as generating a payload checksum, or extracting a transaction summary message from a payment status response

Only pick what you need in order to help with your specific integration requirements. For example, if you are already using ExpressJS and want to easily enable endpoints that can handle payment processing, then it's likely that the only thing you need from this project is the ExpressJS middleware functions. Or, perhaps more likely, you want to use the TS/JS client in a Node based project so that you don't have to deal directly with the PayGate HTTP API. In this case the utility module and typings may also be helpful.

Important links:

Table Of Contents

Quick Start

For Typescript ...

import { PayGateClient } from "paygate-sdk";

or alternatively, for NodeJS/Javascript ...

const { PayGateClient } = require("paygate-sdk");

and then you can use the client ...

const client = new PayGateClient({
  payGateId: "id",
  payGateSecret: "secret",
  returnUrl: "http://app.ui/payment-status",
  notifyUrl: "http://backend/api/handle-payment-notification",
  autoTransactionDate: true,
  autoPaymentReference: true,
  fallbackToZA: true,
});

const paymentResponse = await client.requestPayment({
  AMOUNT: 100.0,
  EMAIL: "client@email.com",
});

console.log(paymentResponse.paymentRef);

More detail is covered in the sections below.

Back to top

Reference Implementation

In addition to the reference documentation, there is also a reference implementation available under the impl folder, which you can run in your local development environment (which will use a PayGate demo account) in a few simple steps. It serves as an example of how to use the various modules, and also as a playground where one can easily test an implementation. It consists of an ExpressJS backend that exposes endpoints via the middleware functions (which in turn uses the TS/JS API client), and a very simple frontend with which to test payments. To run the implementation, follow the steps below.

Ngrok Token

In order to run the reference implementation locally, you will need to have an account with Ngrok. A basic account is free and quick to set up. Ngrok, if you don't already know, is a service that can make your local development server available on a public URL via a secure tunnel. This is needed because PayGate needs to return to a URL that is publically available, after processing a payment. So we use Ngrok to expose the frontend app to the web.

Once you have an Ngrok authentication token, copy the file impl/proxy/.env.sample to impl/proxy/.env and replace the fake value with your authentication token. This is all that will be required to configure the tunelling environment.

PayGate Credentials

The backend server implementation needs to know which PayGate credentials to use for testing. Copy the file impl/server/.env.sample to impl/server.env. It is already configured with the default PayGate test credentials, so no further action is needed. But if you wanted to test with different credentials, you may update them in this file.

Running the reference implementation locally

If you have an Ngrok auth token and PayGate credentials configured, then you can simply run yarn install and yarn develop in each of the folders impl/proxy, impl/server and impl/app, and the proxy needs to be the first one to run. This (the fact that 3 different instance are running in 3 consoles) may be improved later to allow for a somewhat better development experience.

Once they are all up and running, navigate to http://localhost:8000 to test payments.

Back to top

Process Flow

If you are not already familiar with the PayGate payment flow, it's best to first familiarize yourself with it, since this implementation is heavily influenced by it, and it will be easier to get going. A high level overview is provided below.

Process Diagram

TODO: complete this overview

Back to top

Configuration

Both the API Client as well as the ExpressJS middleware are configured via a PayGateConfig object, described by the properties below. The payGateId and payGateKey properties are required, all the rest is optional. Where optional properties or default configuration values are not specified, those values will have to be explicitly passed in with each PaymentRequest. For example, you would typically have to set the RETURN_URL and a unique TRANSACTION_DATE on the PaymentRequest, as per the PayGate Specification. But if you were to specify a returnUrl on the configuration, and set autoTransactionDate to true, then you don't have to supply those values with the PaymentRequest, as they will be set automatically by the client and middleware. Values explicitly found on the PaymentRequest will always take precedence over default configuration values.

For all of this README documentation, all fields in CAPS, such as those found on PaymentRequest, are values specified and required by PayGate. Please refer to the PayGate specification for the details and requirements on those.

Configuration Properties

payGateId: string Required

Your PayGate account ID

payGateKey: string Required

Your PayGate password/secret

returnUrl: string Optional

A default URL that PayGate should return to after processing a payment. If you set a RETURN_URL with an individual PaymentRequest, then that value will take precedence over this one.

notifyUrl: string Optional

A default URL that PayGate should post payment notifications to. If you set a NOTIFY_URL with an individual PaymentRequest, then that value will take precedence over this one. If no value is found on either, then PayGate will not post payment notification data back.

autoPaymentReference: boolean Optional

A unique payment reference (ID) has to be passed in with each PaymentRequest on the REFERENCE property. If you prefer to have control over that ID generation, then leave this value unset or false and set it on the PaymentRequest. But if you prefer to have a payment reference auto generated for you, then set this configuration value to true, and don't specify anything on the REFERENCE property of the PaymentRequest itself. If a value is found on the PaymentRequest, even with autoPaymentReference enabled, then the value on the payment request would take precedence over an auto generated one. Currently the unique ID being generated is a UUID4 string. An option may exist in the future to configure your own custom ID generator.

autoTransactionDate: boolean Optional

A transaction date has to be passed in with each PaymentRequest on the TRANSACTION_DATE property. If you prefer to have control over the date generation, then leave this value unset or false and set it on the PaymentRequest. But if you prefer to have a date auto generated for you (at the time the request is invoked), then set this configuration value to true, and don't specify anything on the TRANSACTION_DATE property of the PaymentRequest itself. If a value is found on the PaymentRequest, even with autoTransactionDate enabled, then the value on the payment request would take precedence over an auto generated one.

defaultCountry: CountryCode Optional

A country code has to be passed in with each PaymentRequest on the COUNTRY property. If no value is set on the PaymentRequest and a defaultCountry is configured, then the default value will be used. If a COUNTRY value is present on the PaymentRequest, it will be used instead.

defaultCurrency: CurrencyCode Optional

A currency code has to be passed in with each PaymentRequest on the CURRENCY property. If no value is set on the PaymentRequest and a defaultCurrency is configured, then the default value will be used. If a CURRENCY value is present on the PaymentRequest, it will be used instead.

defaultLocale: PayGateLocale Optional

A locale code has to be passed in with each PaymentRequest on the LOCALE property. If no value is set on the PaymentRequest and a defaultLocale is configured, then the default value will be used. If no value is found anywhere, then an english locale will be returned. This value is only relevant for the PayGate UI, and is not used for anything related to currency and country when processing payments.

defaultPaymentMethod: PaymentMethod Optional

The payment method is an optional value according to the PayGate specification. However, if no value is set on the PaymentRequest and a defaultPaymentMethod is configured, then the default value will be used.

fallbackToZA: boolean Optional

The fallbackToZA configuration option only affects COUNTRY and CURRENCY on the PaymentRequest. If it is set to true, and no value is expicitly set on the PaymentRequest, and furthermore no value is configured for defaultCountry and defaultCurrency respectively, then it will fallback to ZAF for country and ZAR for currency. If you are predominantly processing payments within South Africa, this can be a convenient option to set.

Back to top

API Client

Documentation in progress ...

Make sure you are familiar with the PayGate process flow before continuing.

The PayGate API client is the core component of the SDK, and what most developers will use to build a PayGate integration. It is a Typescript API, but the compiled Javascript client can also be used in a NodeJS application. The TS reference documentation is available here, but an introduction is provided below.

For Typescript projects ...

import { PayGateClient } from "paygate-sdk";

For a NodeJS/Javascript project ..

const { PayGateClient } = require("paygate-sdk");

Constructor

+ new PayGateClient(payGateIdOrConfig: string | PayGateConfig, payGateKey?: string): PayGateClient

The constructor expects either a PayGateConfig object or a payGateId and payGateKey as parameters. If it's a PayGateConfig, then it's assumed that the payGateId and payGateKey properties will be set on the config, as they are required. Thus, the 2nd constructor parameter is ignored. However, if the first constructor parameter is a string, then it's assumed that no configuration object is provided, and therefore both the parameters payGateId and payGateKey are required.

Methods

static getInstance(payGateIdOrConfig: string | PayGateConfig, payGateKey?: string): PayGateClient

In addition to the regular constructor, a static singleton method is also provided, which will return the same client instance every time it's called. Once it has been invoked with arguments, it can subsequently be invoked with no arguments to return the same instance. If the same payGateId and payGateSecret is specified in the arguments or config of subsequent invocations, then it will also provide the same instance as that which already exists. If, however, a different payGateId and payGateSecret is specified, then it will replace the existing instance with a new one, using the new values and configuration.

requestPayment(paymentRequest: PaymentRequest): Promise\[PaymentResponse](https://distributhor.github.io/paygate-sdk/interfaces/_types_.paymentresponse.html)

Will issue a payment request to PayGate. To understand what is expected from all the properties available on the PaymentRequest, as well as which properties are required or optional, consult the PayGate documentation. By using a PayGateConfig with the client, some of the required properies can be turned into optional ones, by virtue of the fact that they can either be assigned default values or auto generated. Have a look at the Configuration section at the top for more details on how the configuration can be used.

handlePaymentNotification(paymentStatus: PaymentStatus): Promise\[SuccessIndicator](https://distributhor.github.io/paygate-sdk/interfaces/_types_.successindicator.html)

TODO: notes on caching

queryPaymentStatus(paymentRef: PaymentReference): Promise\[PaymentStatus](https://distributhor.github.io/paygate-sdk/classes/_client_.paygateclient.html)

TODO: finish documenting the function

Will issue a query to PayGate for the status of a payment.

Static generateChecksum(data: UntypedObject, encryptionKey: string): string

TODO: finish documenting the function

Will generate a checksum for a given object, according to the specification required by PayGate

Back to top

ExpressJS Middleware

TODO: notes on caching, urlencoded body parser requirement

Make sure you are familiar with the PayGate process flow before continuing.

The middleware module exposes 3 functions that can be used in your existing ExpressJS application.

paymentRequestHandler(config: PayGateConfig): (void)

paymentNotificationHandler(config: PayGateConfig): (void)

paymentStatusHandler(config: PayGateConfig): (void)

▸ paymentRequestHandler

The paymentRequestHandler can to be used on an endpoint of your choice, exposed as a POST request, and expects a request body that conforms to a PaymentRequest. It must be configured using a PayGateConfig. After handling the request, whether an error occurred or not, the result will be available on a paygate property on the ExpressJS request inside your endpoint function, from where you can do further processing.

The paygate property will contain a PayGateMiddlewarePaymentResult, which has the following properties:

paymentResponse: PaymentResponse

If the payment was processed, then the paymentResponse property will be set with the result. Note that this does not indicate whether the payment itself was successfull, or declined etc. The PaymentResponse has to be consulted to see the status of the actual payment. The fact that this property is set only means that the payment was processed (or handled), ie, there was no errors in providing the service.

badRequest: string

If there was an issue with the data provided, such as required fields missing on the PaymentRequest or any other issue that can/should be rectified by the service that uses this endpoint, then an appropriate message will be set on this property, and no other properties will be set. This should usually result in an HTTP 400 or Bad Request, but you can can deal with it any way you want.

serviceError: any

If there was an internal error, or a caught exception, and the service could not be provided due to it, then the low level error or exception will be set on this property, and no other properties will be set. This should usually result in an HTTP 500 or Internal Server Error, but you can deal with any way you want.

▸ paymentNotificationHandler

The paymentNotificationHandler can to be used on an endpoint of your choice, exposed as a POST request, and expects a request body that conforms to a PaymentStatus. It must be configured using a PayGateConfig. This endpoint can therefore be used as the URI where PayGate sends payment notifications, as per the NOTIFY_URL.

This endpoint will only receive such notifications, and by default does nothing else. You will typically use this endpoint to persist payment notifications or trigger any other event driven processing that needs to happen on receiving payment notifications.

TODO: must return OK according to PayGate spec

TODO: notes on caching

After receiving the request, whether an error occurred or not, the result will be available on a paygate property on the ExpressJS request inside your endpoint function, from where you can do further processing.

The paygate property will contain a PayGateMiddlewarePaymentStatus, which has the following properties:

paymentStatus: PaymentStatus

If the payment notification was received, then the paymentStatus property will be set with the result. Note that this does not indicate whether the payment itself was successfull, or declined etc. The paymentStatus has to be consulted to see the status of the actual payment. The fact that this property is set only means that the payment notification was received, ie, there was no errors in providing the service.

badRequest and serviceError is same as above

▸ paymentStatusHandler

The paymentStatusHandler can to be used on an endpoint of your choice, exposed as a GET request, and expects to receive either one of, or both, of the request parameters PAY_REQUEST_ID and REFERENCE. It must be configured using a PayGateConfig. This handler will query PayGate for the status of a payment. After handling the request, whether an error occurred or not, the result will be available on a paygate property on the ExpressJS request inside your endpoint function, from where you can do further processing.

The paygate property will contain a PayGateMiddlewarePaymentStatus, which has the following properties:

paymentStatus: PaymentStatus

If the payment status was queried, then the paymentStatus property will be set with the result. You will usually want to return this payment status to the caller, but can handle it any way. If a failure occurred, then either of badRequest or serviceError will be set.

TODO: notes on caching and both fields required if not quering from the cache, ie, query directly with payagte

badRequest and serviceError is same as above

Example

Below is example showing how to use these middleware handlers in an ExpressJS environment. Note that the urlencoded body parser is required, since PayGate sends responses as that, and that the paymentNotificationHandler returns the text "OK" as required by PayGate.

const express = require("express");
const bodyParser = require("body-parser");

const { paymentRequestHandler, paymentNotificationHandler, paymentStatusHandler } = require("paygate-sdk");

const server = express();

server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: true }));

const middlewareConfig = {
  payGateId: process.env.PAYGATE_ID,
  payGateKey: process.env.PAYGATE_SECRET,
  returnUrl: "https://your.return.url",
  notifyUrl: "https://your.notify.url",
  autoTransactionDate: true,
  autoPaymentReference: true,
  fallbackToZA: true,
};

server.post("/payment-request", paymentRequestHandler(middlewareConfig), async (req, res) => {
  if (req.paygate.badRequest) {
    return res.status(400).send({ message: req.paygate.badRequest });
  }

  if (req.paygate.serviceError) {
    return res.sendStatus(500);
  }

  // send the response back to the caller
  res.send(req.paygate.paymentResponse);
});

server.post("/payment-notification", paymentNotificationHandler(middlewareConfig), async (req, res) => {
  if (req.paygate.badRequest) {
    return res.status(400).send({ message: req.paygate.badRequest });
  }

  if (req.paygate.serviceError) {
    return res.sendStatus(500);
  }

  // persist  the payment status available on req.paygate.paymentStatus,
  // or trigger other payment notification events

  // must send back text with "OK"
  res.send("OK");
});

Back to top

Common Utility Functions

Documentation in progress ...

For Typescript projects ...

import { Util } from "paygate-sdk";
import { generatePayGateChecksum } from "paygate-sdk/lib/util";

Util.toCentAmount(123.45);
generatePayGateChecksum({ prop: "val" }, "key");

For a NodeJS/Javascript project ..

const { Util } = require("paygate-sdk");
const { generatePayGateChecksum } = require("paygate-sdk/lib/util");

Also available for the browser ...

<script src="https://unpkg.com/paygate-sdk@1.0.4/dist/paygate.js"></script>
<script>
  Paygate.Util.toCentAmount(123.45);
  Paygate.Util.redirectBrowser(redirectUrl, redirectParams);
  PayGateUtil.TransactionCode.APPROVED;
</script>

Functions

toCentAmount(amount: string | number): string

generatePayGateChecksum(data: UntypedObject, encryptionKey: string): string

getTransactionInfo(paymentStatus: PaymentStatus): TransactionStatus

getTestCards(): CreditCard[]

getTestCardsByTransactionType(): CreditCard[]

removeAllNonValuedProperties(obj: UntypedObject): void

redirectBrowser(uri: string, params: any): void

Back to top

Types

Documentation in progress ...

Enumerations

Interfaces

Object literals

PayGateEndpoints

Const PayGateEndpoints: object

Properties:

NameTypeValue
INITIATE_URIstring"https://secure.paygate.co.za/payweb3/initiate.trans"
QUERY_URIstring"https://secure.paygate.co.za/payweb3/query.trans"
REDIRECT_URIstring"https://secure.paygate.co.za/payweb3/process.trans"

TransactionStatus

Const TransactionStatus: object

Properties:

NameTypeValue
0string"Not Done"
1string"Approved"
2string"Declined"
3string"Cancelled"
4string"User Cancelled"
5string"Received by PayGate"
7string"Settlement Voided"

PayGateErrorCodes

Const PayGateErrorCodes: object

Properties:

NameTypeValue
CNTRY_INVALIDstring"Country Invalid"
DATA_AMT_NUMstring"Amount is not a number"
DATA_AMT_ZEROstring"Amount value is zero"
DATA_CHKstring"Checksum calculated incorrectly"
DATA_CREFstring"No transaction reference"
DATA_DTTMstring"Transaction date invalid"
DATA_INSstring"Error creating record for transaction request"
DATA_PAY_REQ_IDstring"Pay request ID missing or invalid"
DATA_PMstring"Pay Method or Pay Method Detail fields invalid"
DATA_PWstring"Not all required fields have been posted to PayWeb"
DATA_REGIONstring"No Country or Locale"
DATA_URLstring"No return url"
INVALID_VAULTstring"Vault value invalid"
INVALID_VAULT_IDstring"Vault ID invalid"
INV_EMAILstring"Invalid Email address"
LOCALE_INVALIDstring"Invalid Locale"
ND_INV_PGIDstring"Invalid PayGate ID"
NOT_LIVE_PMstring"No available payment methods"
NO_TRANS_DATAstring"No transaction data found"
PAYVAULT_NOT_ENstring"PayVault not enabled"
PGID_NOT_ENstring"PayGate ID not enabled, there are no available payment methods or there are no available currencies"
TXN_CANstring"Transaction has already been cancelled"
TXN_CMPstring"Transaction has already been completed"
TXN_PRCstring"Transaction is older than 30 minutes or there has been an error processing it"
VAULT_NOT_ACCEPTEDstring"Card types enabled on terminal not available for vaulting"

CreditCardResultCodes

Const CreditCardResultCodes: object

Properties:

NameTypeValue
900001string"Call for approval"
900002string"Card expired"
900003string"Insufficient funds"
900004string"Invalid card number"
900005string"Bank interface timeout"
900006string"Invalid card"
900007string"Declined"
900009string"Lost card"
900010string"Invalid card length"
900011string"Suspected fraud"
900012string"Card reported as stolen"
900013string"Restricted card"
900014string"Excessive card usage"
900015string"Card blacklisted"
900017string"Auth done"
900207string"Declined; authentication failed (incorrect verification code)"
900210string"3D Secure lookup timeout"
990020string"Auth declined"
991001string"Invalid expiry date"
991002string"Invalid amount"

CommunicationAndDataResultCodes

Const CommunicationAndDataResultCodes: object

Properties:

NameTypeValue
900019string"Invalid PayVault scope"
900205string"Unexpected authentication result (phase 1)"
900206string"Unexpected authentication result (phase 2)"
900209string"Transaction verification failed (phase 2) (verification data altered)"
900210string"Authentication already complete; transaction must be restarted (verification done more than once)"
990001string"Could not insert into DB"
990013string"Error processing a batch transaction"
990022string"Bank not available"
990024string"Duplicate transaction detected"
990028string"Transaction cancelled"
990053string"Error processing transaction"

Properties:

NameTypeValue
afstring"Afrikaans"
enstring"Enblish"
sxstring"Sutu"
tnstring"Tswana"
vestring"Venda"
zustring"Zulu"

PaymentMethodName

Const PaymentMethodName: object

Properties:

NameTypeValue
BTstring"Bank Transfer"
CCstring"Credit Card"
CVstring"Cash Voucher"
DCstring"Debit Card"
EWstring"E-Wallet"
PCstring"Pre-Paid Card"

PayGateTestCards

Const PayGateTestCards: object

Properties:

NameTypeValue
Approvedobject{ MasterCard: string = "5200000000000015"; Visa: string = "4000000000000002" }
Declinedobject{ MasterCard: string = "4000000000000036"; Visa: string = "5200000000000049" }
InsufficientFundsobject{ MasterCard: string = "5200000000000023"; Visa: string = "4000000000000028" }
NotProcessedobject{ MasterCard: string = "5200000000000064" }

License

The MIT License (MIT). Please see License File for more information.

1.2.1

3 years ago

1.2.0

3 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.0.5

3 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago

0.9.3

4 years ago

0.9.2

4 years ago

0.9.1

4 years ago

0.2.3

4 years ago

0.2.4

4 years ago

0.2.1

4 years ago

0.2.0

4 years ago

0.1.4

4 years ago

0.1.2

4 years ago

0.1.3

4 years ago

0.1.1

4 years ago

0.1.0

4 years ago

0.0.9

4 years ago

0.0.8

4 years ago

0.0.5

4 years ago

0.0.7

4 years ago

0.0.6

4 years ago

0.0.3

4 years ago

0.0.4

4 years ago

0.0.2

4 years ago

0.0.1

4 years ago