Skip to content

Encryption Example

All sensitive card data must be encrypted using your merchant RSA public key with RSA-OAEP (SHA-256) padding before sending to the API. Each field is encrypted individually and the result is Base64-encoded.

Fields requiring encryption: card number, CVV, expiration month, expiration year.

Your RSA public key is available in your merchant dashboard under Merchant details.


encrypt_field() {
  echo -n "$1" | openssl pkeyutl -encrypt \
    -pubin -inkey <(echo "$PUBLIC_KEY") \
    -pkeyopt rsa_padding_mode:oaep \
    -pkeyopt rsa_oaep_md:sha256 \
    -pkeyopt rsa_mgf1_md:sha256 | base64
}

PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"

ENCRYPTED_CARD_NUMBER=$(encrypt_field "4111111111111111")
ENCRYPTED_CVV=$(encrypt_field "123")
ENCRYPTED_EXP_MONTH=$(encrypt_field "12")
ENCRYPTED_EXP_YEAR=$(encrypt_field "2027")

Save as encrypt-card.mjs and run with node encrypt-card.mjs:

// encrypt-card.mjs
import { publicEncrypt, constants } from 'node:crypto';

const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----`;

function encryptField(plaintext) {
  const encrypted = publicEncrypt(
    {
      key: PUBLIC_KEY,
      padding: constants.RSA_PKCS1_OAEP_PADDING,
      oaepHash: 'sha256',
    },
    Buffer.from(plaintext, 'utf-8')
  );
  return encrypted.toString('base64');
}

console.log('encrypted_card_number:', encryptField('4111111111111111'));
console.log('encrypted_cvv:', encryptField('123'));
console.log('encrypted_expiration_month:', encryptField('12'));
console.log('encrypted_expiration_year:', encryptField('2027'));

Save as EncryptCard.java and run with java EncryptCard.java (Java 11+):

// EncryptCard.java
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class EncryptCard {

    // Paste the Base64 body of your PEM key here (without BEGIN/END markers)
    private static final String PUBLIC_KEY_BASE64 =
        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...";

    public static String encryptField(String plaintext) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(
            PUBLIC_KEY_BASE64.replaceAll("\\s", "")
        );
        PublicKey publicKey = KeyFactory.getInstance("RSA")
            .generatePublic(new X509EncodedKeySpec(keyBytes));

        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey, new OAEPParameterSpec(
            "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT
        ));

        byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }

    public static void main(String[] args) throws Exception {
        System.out.println("encrypted_card_number: " + encryptField("4111111111111111"));
        System.out.println("encrypted_cvv: " + encryptField("123"));
        System.out.println("encrypted_expiration_month: " + encryptField("12"));
        System.out.println("encrypted_expiration_year: " + encryptField("2027"));
    }
}

Using the Encrypted Values

Include the encrypted fields in your authorize request:

{
  "payment_method": {
    "type": "card",
    "data": {
      "encrypted_card_number": "<base64 encrypted value>",
      "encrypted_cvv": "<base64 encrypted value>",
      "encrypted_expiration_month": "<base64 encrypted value>",
      "encrypted_expiration_year": "<base64 encrypted value>"
    }
  }
}

Field Format Requirements

Encrypt the plain-text value before Base64-encoding. The table below shows the expected plain-text format for each field.

Field Plain-text format Example values Notes
encrypted_card_number Digits only, no spaces 4111111111111111
encrypted_cvv Digits only 123, 0987
encrypted_expiration_month Integer, no leading zero 1, 12 Range: 1–12
encrypted_expiration_year 2-digit or 4-digit year 26, 2026 Must be current or future year

Expiration Year Formats

Both 2-digit and 4-digit year formats are accepted:

Valid expiry year values

26      ✅  2-digit short year
2026    ✅  4-digit full year

Invalid expiry year values

2026-01      ❌  Date string — not accepted
01/26        ❌  MM/YY format — not accepted

Server-side only

Card encryption must happen on your server. Never expose the RSA public key or raw card data in the browser.