> For the complete documentation index, see [llms.txt](https://docs.kitegateway.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kitegateway.com/receiving-money/direct-card-payment.md).

# 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 th&#x65;**`encrypted_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`&#x20;

### Encryption

#### **1. Create JSON of the Card details**

{% tabs %}
{% tab title="Data" %}

```javascript
{
    "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"
}
```

{% endtab %}

{% tab title="Description" %}

| 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      |
| {% endtab %}     |        |                                                                                                                |          |
| {% endtabs %}    |        |                                                                                                                |          |

#### **2.** Downloading Public Key

```javascript
// Live
GET https://kitegateway.com/v1/public-key
```

```javascript
// Sandbox
GET https://sandbox.kitegateway.com/v1/public-key
```

{% tabs %}
{% tab title="Shell" %}

```
// live
curl -v -X GET 'https://kitegateway.com/v1/public-key -H 'Authorization: JWT ***' --output kitegateway.public.key.pem

```

{% endtab %}
{% endtabs %}

#### 3.Encryption Card

{% tabs %}
{% tab title="NodeJS" %}

```javascript
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;
}
```

{% endtab %}

{% tab title="PHP" %}

```php
<?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';
*/

?>
```

{% endtab %}

{% tab title="Python" %}

```python
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')
"""
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
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'
=end
```

{% endtab %}

{% tab title="C#" %}

```csharp
using 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");
    }
}
*/
```

{% endtab %}

{% tab title="Java" %}

```java
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");
    }
    */
}
```

{% endtab %}
{% endtabs %}

#### 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`**](/receiving-money/make-payment.md) when making a [**`Card Collection Request`**](/receiving-money/card-payments.md)

{% tabs %}
{% tab title="Sample Request Body" %}

```javascript
{
  ...
  "encrypted_card": "***"
  ...
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
When the **`encrypted_card`** the parameter is specified in the collection request body, Kitegateway will know that you want to make a direct card payment.&#x20;

Since it's 3D processing, the response object will contain the bank authentication URL where you will redirect the customer to complete the payment.
{% endhint %}
