In this guide, you will learn:
- Best practices while building payment extension
- Common issues encountered
Best Practices
Validate creds before saving into db
Extension should validate creds being entered by merchant before saving saving into db. This is to ensure that merchant provide correct credentials. In case wrong creds are provided, then payment will fail for customer.
Encrypt merchant creds before storing
Extension should save the encrypted credentials into db. This is to ensure so that in any data breach, credentials are not leaked. You can do encryption and decryption using the AES-256 algorithm
While storing payment gateway secrets in the extensions database we recommend that these secrets are encrypted and then stored for it to be secure. Following are examples in javascript for encryption and decryption using the AES-256 algorithm.
Encryption
function encrypt(secretKey, msg) {
const key = Buffer.from(decode(secretKey, 'utf-8'), 'hex');
const msgBuffer = Buffer.from(msg, 'utf-8');
const iv = Buffer.from(decode(enc_iv), 'hex');
const cipher = crypto.createCipheriv('AES-256-GCM', key, iv);
const ctBytes = Buffer.concat([cipher.update(pad(msgBuffer, 16)), cipher.final()]);
const ct = encode(ctBytes.toString('hex'));
return {
enc_iv: enc_iv,
gateway_secret: ct,
authTag: cipher.getAuthTag().toString("hex")
};
}
This function above encrypts the provided msg
using the AES-256 algorithm in GCM mode, with the provided secretKey
and a random initialization vector (enc_iv
). It returns the encrypted data, the IV, and an authentication tag for verifying the integrity of the encrypted data.
Decryption
function decrypt(secretKey, data) {
let {enc_iv, gateway_secret, authTag} = data;
const key = Buffer.from(decode(secretKey, 'utf-8'), 'hex');
const iv = Buffer.from(decode(enc_iv), 'hex');
const ct = Buffer.from(decode(gateway_secret), 'hex');
const decipher = crypto.createDecipheriv('AES-256-GCM', key, iv);
decipher.setAuthTag(Buffer.from(authTag, 'hex'))
const decryptedBytes = Buffer.concat([decipher.update(ct), decipher.final()]);
const decDataString = unpad(decryptedBytes.toString('utf-8'));
const decDataObject = JSON.parse(decDataString);
return decDataObject;
}
The above decrypt function takes the encrypted data, initialization vector, authentication tag, and the secret key, and attempts to decrypt the data using the AES-256-GCM algorithm. It also verifies the integrity of the decrypted data using the provided authentication tag.
Include audit trail
You should include audit trail on config page to show when the last credentials were changed and who changed it. This is to show merchant that when creds were created/updated so that he knows if anyone has changed creds in the past. It can look like this.
Common issues
Order is not getting generated on the Fynd Platform
Extension may not have properly update the status of payment through updatePaymentSession API. In the updatePaymentSession API, we accept status from these only: started, complete, failed, pending. You need to make sure you properly map the payment status to one of these values only.
Getting error - cannot read properties of null (reading 'expires_in')
Extension is not installed correctly or the access_token is expired. Make sure you use refresh_token to update access_token before it expires.
What is the behavior of updating the payment status? What happens if extension updates status multiple times?
API wont throw any error. Its flow is as follows:
- If api is called multiple times with the same payment status ex. 'complete', then only the first api will be taken and subsequent api will be ignored.
- If current status is in [failed, pending, started] and then api marks it 'complete' then platform will update the status to 'complete'. But if invalid status is being marked, ex- current status is 'complete' and update status is 'started', then it will be ignored.
- However, extension should also make sure that invalid status should not be sent to update status.