# 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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.kitegateway.com/receiving-money/direct-card-payment.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
