Direct card payment
A direct card payment is a payment transaction in which your customer has authorized you to take money from their card using their full card details. However, the Card details are sent as an encrypted string.
Posting a direct card request is the same as posting a card payment however theencrypted_card parameter will have to be specified. By providing this field, kitegateway will know that the transaction is a direct card payment.
Providing encrypted_card
encrypted_card Encryption
1. Create JSON of the Card details
{
"full_name": "Nakimbugwe Racheal",
"card_number": "012345666777888999",
"expiry_month": "02",
"expiry_year": "24",
"cvv": "111",
"billing_address": "Some Street",
"billing_city": "Some City",
"billing_zip": "88653",
"billing_state": "JK",
"billing_country": "UK"
}Parameter
Type
Description
Required
full_name
string
The full name of the card holder as registered by the card issuer
YES
card_number
string
The card number. Usually the length of 16 to 19
YES
expiry_month
string
Card expiry month. For single digit months, prefix 0 e.g. 06
YES
expiry_year
string
Card expiry year
YES
cvv
string
The card CVV, CVC, etc depending on the nature of the card
YES
billing_address
string
Billing address as registered by the card issuer.
YES
billing_city
string
Billing city name as registered by the issuer.
YES
billing_zip
string
The zip/postal code.
YES
billing_state
string
State name.
YES
billing_country
string
The 2 character ISO country code. The country code list can be obtained using the API description that follows
YES
2. Downloading Public Key
// Live
GET https://kitegateway.com/v1/public-key// Sandbox
GET https://sandbox.kitegateway.com/v1/public-key// live
curl -v -X GET 'https://kitegateway.com/v1/public-key -H 'Authorization: JWT ***' --output kitegateway.public.key.pem
3.Encryption Card
const crypto = require('crypto');
const fs = require('fs');
const card_data = {
full_name: "Ankunda Ruth",
card_number: "0002221238737737737",
expiry_month: "02",
expiry_year: "24",
cvv: "111",
billing_address: "Kensington Street No. 2",
billing_city: "London",
billing_zip: "12345",
billing_state: "London",
billing_country: "UK",
};
function encrypt(card_data) {
// Stringify card data
const payload = JSON.stringify(card_data);
console.log(`Payload size: ${Buffer.from(payload).length} bytes`);
// Load public key
const publicKeyFile = "path-to-file/kitegateway.public.key.pem";
const publicKey = fs.readFileSync(publicKeyFile).toString().replace(/\\n/g, '\n');
// Check payload size (max 214 bytes for 2048-bit key with OAEP padding)
if (Buffer.from(payload).length > 214) {
// Generate AES key and IV
const aesKey = crypto.randomBytes(32); // 256-bit AES key
const iv = crypto.randomBytes(16); // 128-bit IV
// Encrypt payload with AES-256-CBC
const cipher = crypto.createCipheriv('aes-256-cbc', aesKey, iv);
let encryptedData = cipher.update(payload, 'utf8', 'base64');
encryptedData += cipher.final('base64');
console.log(`AES encrypted data size: ${Buffer.from(encryptedData, 'base64').length} bytes`);
// Encrypt AES key with RSA-OAEP
const encryptedAesKey = crypto.publicEncrypt(
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
},
aesKey
);
// Combine encrypted data, IV, and encrypted AES key
const result = `${encryptedData}::${iv.toString('base64')}::${encryptedAesKey.toString('base64')}`;
const base64Result = Buffer.from(result).toString('base64');
// Validate base64
if (!/^[A-Za-z0-9+/=]+$/.test(base64Result)) {
console.error('Invalid base64 encoding of hybrid ciphertext.');
return null;
}
console.log(`Hybrid encrypted data (base64): ${base64Result}`);
return base64Result;
}
// Standard RSA-OAEP encryption
const encryptedData = crypto.publicEncrypt(
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
},
Buffer.from(payload)
);
const base64Result = encryptedData.toString('base64');
// Validate base64
if (!/^[A-Za-z0-9+/=]+$/.test(base64Result)) {
console.error('Invalid base64 encoding of ciphertext.');
return null;
}
return base64Result;
}<?php
/**
* Encrypts card details using hybrid encryption (RSA-OAEP + AES-256-CBC).
*
* @param string $public_key_path Path to the RSA public key file (e.g., 'kitegateway.public.key.pem').
* @param string $full_name Cardholder's full name.
* @param string $card_number Card number.
* @param string $expiry_month Expiry month (MM).
* @param string $expiry_year Expiry year (YY).
* @param string $cvv CVV code.
* @param string $billing_address Billing address.
* @param string $billing_city Billing city.
* @param string $billing_zip Billing ZIP/postal code.
* @param string $billing_state Billing state (e.g., 2-letter code).
* @param string $billing_country Billing country (e.g., 2-letter code).
* @return string|null Base64-encoded encrypted data or null on failure.
*/
function encrypt_card_details(
$public_key_path,
$full_name,
$card_number,
$expiry_month,
$expiry_year,
$cvv,
$billing_address,
$billing_city,
$billing_zip,
$billing_state,
$billing_country
) {
// Initialize return value
$ret = null;
// Check for OpenSSL extension
if (!extension_loaded('openssl')) {
return $ret;
}
// Load public key
$key_content = @file_get_contents($public_key_path);
if ($key_content === false) {
return $ret;
}
$public_key = openssl_get_publickey($key_content);
if ($public_key === false) {
return $ret;
}
// Verify key details
$key_details = openssl_pkey_get_details($public_key);
if ($key_details === false || !isset($key_details['bits']) || $key_details['bits'] !== 2048) {
openssl_free_key($public_key);
return $ret;
}
// Prepare card data as JSON
$card_data = json_encode([
'full_name' => $full_name,
'card_number' => $card_number,
'expiry_month' => $expiry_month,
'expiry_year' => $expiry_year,
'cvv' => $cvv,
'billing_address' => $billing_address,
'billing_city' => $billing_city,
'billing_zip' => $billing_zip,
'billing_state' => $billing_state,
'billing_country' => $billing_country
]);
if ($card_data === false) {
openssl_free_key($public_key);
return $ret;
}
$data_size = strlen($card_data);
// Hybrid encryption for data > 214 bytes
if ($data_size > 214) {
// Generate AES key and IV
$aes_key = openssl_random_pseudo_bytes(32);
$iv = openssl_random_pseudo_bytes(16);
// Encrypt data with AES-256-CBC
$encrypted_data = openssl_encrypt($card_data, 'AES-256-CBC', $aes_key, 0, $iv);
if ($encrypted_data === false) {
openssl_free_key($public_key);
return $ret;
}
// Encrypt AES key with RSA-OAEP
if (!openssl_public_encrypt($aes_key, $encrypted_aes_key, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
openssl_free_key($public_key);
return $ret;
}
// Combine components
$ret = base64_encode($encrypted_data . '::' . base64_encode($iv) . '::' . base64_encode($encrypted_aes_key));
} else {
// Standard RSA-OAEP encryption for small data
if (!openssl_public_encrypt($card_data, $result, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
openssl_free_key($public_key);
return $ret;
}
$ret = base64_encode($result);
}
openssl_free_key($public_key);
return $ret;
}
// Example usage (for testing)
/*
$public_key_path = 'kitegateway.public.key.pem';
$encrypted = encrypt_card_details(
$public_key_path,
'Test User',
'4111111111111111',
'12',
'25',
'123',
'123 Main St, Apt 4B',
'Springfield',
'62701',
'IL',
'US'
);
echo $encrypted ? $encrypted : 'Encryption failed';
*/
?>import json
import base64
import os
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
def encrypt_card_details(
public_key_path,
full_name,
card_number,
expiry_month,
expiry_year,
cvv,
billing_address,
billing_city,
billing_zip,
billing_state,
billing_country
):
try:
# Load public key
if not os.path.exists(public_key_path):
return None
with open(public_key_path, 'rb') as f:
public_key = serialization.load_pem_public_key(f.read(), backend=default_backend())
# Verify key size
if not isinstance(public_key, rsa.RSAPublicKey) or public_key.key_size != 2048:
return None
# Prepare card data as JSON
card_data = json.dumps({
'full_name': full_name,
'card_number': card_number,
'expiry_month': expiry_month,
'expiry_year': expiry_year,
'cvv': cvv,
'billing_address': billing_address,
'billing_city': billing_city,
'billing_zip': billing_zip,
'billing_state': billing_state,
'billing_country': billing_country
}).encode('utf-8')
data_size = len(card_data)
if data_size > 214:
# Generate AES key and IV
aes_key = os.urandom(32)
iv = os.urandom(16)
# Encrypt data with AES-256-CBC
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
padded_data = card_data + b'\0' * (16 - (data_size % 16)) # PKCS7-like padding
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
# Encrypt AES key with RSA-OAEP
encrypted_aes_key = public_key.encrypt(
aes_key,
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None)
)
# Combine components
return base64.b64encode(
encrypted_data + b'::' + base64.b64encode(iv) + b'::' + base64.b64encode(encrypted_aes_key)
).decode('utf-8')
else:
# Standard RSA-OAEP encryption
encrypted_data = public_key.encrypt(
card_data,
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None)
)
return base64.b64encode(encrypted_data).decode('utf-8')
except Exception:
return None
# Example usage
"""
if __name__ == '__main__':
result = encrypt_card_details(
'kitegateway.public.key.pem',
'Test User',
'4111111111111111',
'12',
'25',
'123',
'123 Main St, Apt 4B',
'Springfield',
'62701',
'IL',
'US'
)
print(result if result else 'Encryption failed')
"""require 'json'
require 'base64'
require 'openssl'
def encrypt_card_details(
public_key_path,
full_name,
card_number,
expiry_month,
expiry_year,
cvv,
billing_address,
billing_city,
billing_zip,
billing_state,
billing_country
)
begin
# Load public key
return nil unless File.exist?(public_key_path)
public_key = OpenSSL::PKey::RSA.new(File.read(public_key_path))
# Verify key size
return nil unless public_key.n.num_bytes * 8 == 2048
# Prepare card data as JSON
card_data = JSON.dump({
full_name: full_name,
card_number: card_number,
expiry_month: expiry_month,
expiry_year: expiry_year,
cvv: cvv,
billing_address: billing_address,
billing_city: billing_city,
billing_zip: billing_zip,
billing_state: billing_state,
billing_country: billing_country
})
data_size = card_data.bytesize
if data_size > 214
# Generate AES key and IV
aes_key = OpenSSL::Random.random_bytes(32)
iv = OpenSSL::Random.random_bytes(16)
# Encrypt data with AES-256-CBC
cipher = OpenSSL::Cipher.new('AES-256-CBC')
cipher.encrypt
cipher.key = aes_key
cipher.iv = iv
encrypted_data = cipher.update(card_data) + cipher.final
# Encrypt AES key with RSA-OAEP (simulated via PKCS1_OAEP padding)
encrypted_aes_key = public_key.public_encrypt(aes_key, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
# Combine components
Base64.encode64(
encrypted_data + '::' + Base64.encode64(iv) + '::' + Base64.encode64(encrypted_aes_key)
).chomp
else
# Standard RSA-OAEP encryption
encrypted_data = public_key.public_encrypt(card_data, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
Base64.encode64(encrypted_data).chomp
end
rescue
nil
end
end
# Example usage
=begin
result = encrypt_card_details(
'kitegateway.public.key.pem',
'Test User',
'4111111111111111',
'12',
'25',
'123',
'123 Main St, Apt 4B',
'Springfield',
'62701',
'IL',
'US'
)
puts result || 'Encryption failed'
=endusing System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
public class EncryptCardDetails
{
public static string Encrypt(
string publicKeyPath,
string fullName,
string cardNumber,
string expiryMonth,
string expiryYear,
string cvv,
string billingAddress,
string billingCity,
string billingZip,
string billingState,
string billingCountry)
{
try
{
// Load public key
if (!File.Exists(publicKeyPath)) return null;
string keyContent = File.ReadAllText(publicKeyPath);
using var rsa = RSA.Create();
rsa.ImportFromPem(keyContent.ToCharArray());
// Verify key size
if (rsa.KeySize != 2048) return null;
// Prepare card data as JSON
var cardData = JsonSerializer.Serialize(new
{
full_name = fullName,
card_number = cardNumber,
expiry_month = expiryMonth,
expiry_year = expiryYear,
cvv,
billing_address = billingAddress,
billing_city = billingCity,
billing_zip = billingZip,
billing_state = billingState,
billing_country = billingCountry
});
byte[] cardDataBytes = Encoding.UTF8.GetBytes(cardData);
int dataSize = cardDataBytes.Length;
if (dataSize > 214)
{
// Generate AES key and IV
using var aes = Aes.Create();
aes.KeySize = 256;
aes.GenerateKey();
aes.GenerateIV();
// Encrypt data with AES-256-CBC
using var ms = new MemoryStream();
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(cardDataBytes, 0, cardDataBytes.Length);
cs.FlushFinalBlock();
}
byte[] encryptedData = ms.ToArray();
// Encrypt AES key with RSA-OAEP
byte[] encryptedAesKey = rsa.Encrypt(aes.Key, RSAEncryptionPadding.OaepSHA1);
// Combine components
string result = Convert.ToBase64String(encryptedData) + "::" +
Convert.ToBase64String(aes.IV) + "::" +
Convert.ToBase64String(encryptedAesKey);
return Convert.ToBase64String(Encoding.UTF8.GetBytes(result));
}
else
{
// Standard RSA-OAEP encryption
byte[] encryptedData = rsa.Encrypt(cardDataBytes, RSAEncryptionPadding.OaepSHA1);
return Convert.ToBase64String(encryptedData);
}
}
catch
{
return null;
}
}
}
// Example usage
/*
class Program
{
static void Main()
{
string result = EncryptCardDetails.Encrypt(
"kitegateway.public.key.pem",
"Test User",
"4111111111111111",
"12",
"25",
"123",
"123 Main St, Apt 4B",
"Springfield",
"62701",
"IL",
"US"
);
Console.WriteLine(result ?? "Encryption failed");
}
}
*/import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import com.google.gson.Gson;
public class EncryptCardDetails {
public static String encrypt(
String publicKeyPath,
String fullName,
String cardNumber,
String expiryMonth,
String expiryYear,
String cvv,
String billingAddress,
String billingCity,
String billingZip,
String billingState,
String billingCountry) {
try {
// Load public key
File keyFile = new File(publicKeyPath);
if (!keyFile.exists()) return null;
String keyContent = new String(Files.readAllBytes(keyFile.toPath()))
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s", "");
byte[] keyBytes = Base64.getDecoder().decode(keyContent);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey = kf.generatePublic(spec);
// Verify key size
if (publicKey.getAlgorithm().equals("RSA") && ((java.security.interfaces.RSAPublicKey) publicKey).getModulus().bitLength() != 2048) {
return null;
}
// Prepare card data as JSON
Gson gson = new Gson();
String cardData = gson.toJson(new CardData(
fullName, cardNumber, expiryMonth, expiryYear, cvv,
billingAddress, billingCity, billingZip, billingState, billingCountry));
byte[] cardDataBytes = cardData.getBytes("UTF-8");
int dataSize = cardDataBytes.length;
if (dataSize > 214) {
// Generate AES key and IV
byte[] aesKey = new byte[32];
byte[] iv = new byte[16];
new java.security.SecureRandom().nextBytes(aesKey);
new java.security.SecureRandom().nextBytes(iv);
// Encrypt data with AES-256-CBC
Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(aesKey, "AES"), new IvParameterSpec(iv));
byte[] encryptedData = aesCipher.doFinal(cardDataBytes);
// Encrypt AES key with RSA-OAEP
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedAesKey = rsaCipher.doFinal(aesKey);
// Combine components
String result = Base64.getEncoder().encodeToString(encryptedData) + "::" +
Base64.getEncoder().encodeToString(iv) + "::" +
Base64.getEncoder().encodeToString(encryptedAesKey);
return Base64.getEncoder().encodeToString(result.getBytes("UTF-8"));
} else {
// Standard RSA-OAEP encryption
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedData = rsaCipher.doFinal(cardDataBytes);
return Base64.getEncoder().encodeToString(encryptedData);
}
} catch (Exception e) {
return null;
}
}
// Helper class for JSON serialization
private static class CardData {
String full_name, card_number, expiry_month, expiry_year, cvv;
String billing_address, billing_city, billing_zip, billing_state, billing_country;
CardData(String fullName, String cardNumber, String expiryMonth, String expiryYear, String cvv,
String billingAddress, String billingCity, String billingZip, String billingState, String billingCountry) {
this.full_name = fullName;
this.card_number = cardNumber;
this.expiry_month = expiryMonth;
this.expiry_year = expiryYear;
this.cvv = cvv;
this.billing_address = billingAddress;
this.billing_city = billingCity;
this.billing_zip = billingZip;
this.billing_state = billingState;
this.billing_country = billingCountry;
}
}
// Example usage
/*
public static void main(String[] args) {
String result = encrypt(
"kitegateway.public.key.pem",
"Test User",
"4111111111111111",
"12",
"25",
"123",
"123 Main St, Apt 4B",
"Springfield",
"62701",
"IL",
"US"
);
System.out.println(result != null ? result : "Encryption failed");
}
*/
}4. Set card_cipher parameter
From the step above, the resultant string after encryption is what we need to set as the encrypted_card and include it in the Post Collection Request when making a Card Collection Request
{
...
"encrypted_card": "***"
...
}Last updated