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
In a Direct API integration, encryption happens on your server. Don't write browser code that collects raw card data or encrypts it yourself. If the card has to be entered in the browser, use Embedded Fields or the Hosted Payment Page: their Qore Payments components receive the public key from the session and encrypt in their own secure frame or page, so raw card data never reaches your code.