Encryption

Learn how to secure your payment data.

When making a card charge request, you must encrypt the card information before sending the request.

🚧

You do not need to worry about encryption when using any of our backend SDKs. You can pass your encryption key to the library, and it automatically encrypts your payload before sending the request.

When integrating our direct charge APIs, You'll have to handle the encryption in your code to ensure data integrity. Follow these steps to properly encrypt your request:

  1. Retrieve your encryption key from your dashboard (check the API settings section for your credentials).
  2. Use the Advanced Encryption Standard (AES 256) to encrypt your request. This is extended via the following libraries:
    1. Node-forge
    2. pycryptodome
  3. Add the encrypted data to your API requests.
export async function encryptAES(data: string, token: string, nonce: string): Promise<string> {
    if (nonce.length !== 12) {
        throw new Error("Nonce must be exactly 12 characters long");
    }

    const cryptoSubtle = globalThis.crypto?.subtle || require("crypto").webcrypto?.subtle;
    if (!cryptoSubtle) {
        throw new Error("Crypto API is not available in this environment.");
    }

    const decodedKeyBytes = Uint8Array.from(atob(token), c => c.charCodeAt(0));

    const key = await cryptoSubtle.importKey(
        "raw",
        decodedKeyBytes,
        { name: "AES-GCM" },
        false,
        ["encrypt"]
    );
    const iv = new TextEncoder().encode(nonce);

    const encryptedData = await cryptoSubtle.encrypt(
        {
            name: "AES-GCM",
            iv: iv,
        },
        key,
        new TextEncoder().encode(data)
    );

    return btoa(String.fromCharCode(...new Uint8Array(encryptedData)));
}
public class EncryptionService {

    private static final String AES = "AES";
    private static final String AES_ALGORITHM = "AES/GCM/NoPadding";
    private static final int TAG_LENGTH_BIT = 128;

    static String encrypt(String plainText, String nonce, String b64EncodedKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        if (plainText.isEmpty()) {
            throw new IllegalArgumentException("Must provide valid plainText");
        }
        if (b64EncodedKey.isEmpty()) {
            throw new IllegalArgumentException("Must provide valid b64EncodedAESKey");
        }
        if (nonce.isEmpty()) {
            throw new IllegalArgumentException("Must provide valid nonce");
        }

        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce.getBytes());
        byte[] decodedKeyBytes = Base64.getDecoder().decode(b64EncodedKey);
        SecretKey key = new SecretKeySpec(decodedKeyBytes, AES);
        cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());

        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    public static String generateNonce(int length) {
        return new Random().ints(length, 0, 62)
                .mapToObj("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"::charAt)
                .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
                .toString();
    }
}
ADD_PYTHON_EXAMPLE
ADD_PHP_EXAMPLE
{
   "amount":150,
   "currency":"NGN",
   "reference":"{{$guid}}",
   "customer":{
      "email":"[email protected]"
   },
   "payment_method":{
      "type":"card",
      "card": {
        "nonce": "{{$randomly_generated_nonce}}",
        "encrypted_card_number": "{{$encrypted_card_number}}",
        "encrypted_expiry_month": "{{$encrypted_expiry_month}}",
        "encrypted_expiry_year": "{{$encrypted_expiry_year}}",
        "encrypted_cvv": "{{$encrypted_cvv}}"
      }
   }
}

If you send unencrypted or improperly encrypted card details in your request, We return a 422 error with a failed encryption error message.

// UPDATE THIS ERROR MESSAGE AFTER THE RESPONSE IS FINAL
{
    "status": "failed",
    "message": "Unable to decrypt encrypted fields provided",
    "error": {
        "type": "client_encryption_error",
        "code": "11100"
    },
    "meta": {}
}