PCI Vault Logo
Klasha

Sending card data to Klasha

PCI Vault has the ability to forward card data to third parties. The data can be transformed to the format required by the third party at the time of the request, without modifying the stored data itself. This is done by using custom rules.

In this proxy recipe, we'll detail how to cater to the use-case, where card data is forwarded to Klasha to initiate a payment without it ever passing through your own server.

Overview

Klasha uses encryption to make request data difficult to read if that data were intercepted. The encryption also ensures that the data being sent is coming from a known source as only that sender will have access to the encryption key. The full request body gets encrypted and passed in a JSON object field. Computing the encrypted data can be quite a laborious task, thankfully PCI Vault has the Rule Engine which can be leveraged to do the heavy lifting for you.

In this proxy recipe, we'll show you how to:

  1. Create a rule to encrypt the data sent to Klasha
  2. Initiate a proxy request
  3. Handle the response from Klasha

Step 1: Create a rule to encrypt the data sent to Klasha

To create a custom rule, we'll make use of the Rules API, specifically the Create Rule endpoint.

First set a unique id for your rule, this is a string which will be referenced later.

For the list of operations we'll use the following (the template field names would need to be adjusted for, depending on your specific data format):

[
  {
    "order_position": 1,
    "name": "template",
    "intput_field": "*",
    "output_field": "body",
    "arguments": {
      "template": "{\"card_number\": \"{{card_number}}\",\"card_holder_name\": \"{{card_holder}}\",\"cvv\": \"{{cvv}}\",\"expiry_month\": \"{{expiry_month}}\",\"expiry_year\": \"{{expiry_year_short}}\",\"currency\": \"{{currency}}\",\"country\": \"{{country}}\",\"amount\": {{amount}}}"
    }
  },
  {
    "order_position": 2,
    "name": "encrypt",
    "intput_field": "body",
    "output_field": "body_encrypted",
    "arguments": {
      "algorithm": "3des",
      "key": "MIIEnzANBg....",
      "base64_key": true
    }
  },
  {
    "order_position": 3,
    "name": "encode",
    "intput_field": "body_encrypted",
    "output_field": "body_encrypted_base64",
    "arguments": {
      "scheme": "base64",
      "hex_input": true
    }
  }
]

This rule consists of 3 operations, let's break down what each operation does:

Operation 1

{
  "order_position": 1,
  "name": "template",
  "intput_field": "*",
  "output_field": "body",
  "arguments": {
    "template": "{\"card_number\": \"{{card_number}}\",\"card_holder_name\": \"{{card_holder}}\",\"cvv\": \"{{cvv}}\",\"expiry_month\": \"{{expiry_month}}\",\"expiry_year\": \"{{expiry_year_short}}\",\"currency\": \"{{currency}}\",\"country\": \"{{country}}\",\"amount\": {{amount}}}"
  }
}

The first operation outputs the full request body into a body field. The values stored in the vault will be populated to make a JSON string. When parsed, the template looks as follows:

{
  "card_number": "{{card_number}}",
  "card_holder_name": "{{card_holder}}",
  "cvv": "{{cvv}}",
  "expiry_month": "{{expiry_month}}",
  "expiry_year": "{{expiry_year_short}}",
  "currency": "{{currency}}",
  "country": "{{country}}",
  "amount": {{amount}}
}

Operation 2

{
  "order_position": 2,
  "name": "encrypt",
  "intput_field": "body",
  "output_field": "body_encrypted",
  "arguments": {
    "algorithm": "3des",
    "key": "MIIEnzANBg....",
    "base64_key": true
  }
}

The second operation encrypts the JSON string using 3DES encryption with CBC using PKCS #5 padding. We also set our encryption key obtained from Klasha. The operation can optionally be passed an extra argument nonce_field, this is where an IV (Initialization Vector) can be obtained from. 3DES with CBC uses an 8 byte vector, but if none is specified, by default the first 8 bytes of the key are used. This is how the Klasha encryption works, so we don't use the nonce_field argument.

Operation 3

{
  "order_position": 3,
  "name": "encode",
  "intput_field": "body_encrypted",
  "output_field": "body_encrypted_base64",
  "arguments": {
    "scheme": "base64",
    "hex_input": true
  }
}

The third operation takes the body_encrypted hexadecimal string and encodes it as Base64 into another field called body_encrypted_base64. Note we set "hex_input": true to tell the operation that the input field is hex. Otherwise it would assume that the input is a utf-8 string.

Note: this one rule is very specific to initiating a payment. If multiple Klasha endpoints are to be used, it would be a good idea to create a separate rule to output the body of each request from the rule to encrypt it. When making the proxy request one can apply multiple rules in succession, with the computed fields from the first being available to the next e.g. klasha_initiate_payment_body,klasha_encrypt_body. (comma separated list of rule ids)

Step 2: Initiate a proxy request

We'll use the Proxy Post endpoint to initiate a proxy request. The following query string parameters are required:

  • token: the token identifying the card we'd like to send to Klasha
  • user: the key used to encrypt the data
  • passphrase: the passphrase for the key used to encrypt the data
  • rules: comma separated list of rule ids, e.g. klasha_initiate_payment

The request URL will look like https://api.pcivault.io/v1/proxy/post?token=uniquetoken&user=ABCD&passphrase=secretpassphrase&rules=klasha_initiate_payment

The request body will look something like this:

{
  "request": {
    "method": "POST",
    "url": "https://{{env_url}}/pay/aggregators/{{gateway}}/card/payment/v2",
    "headers": [
      {
        "Content-Type": "application/json"
      },
      {
        "x-auth-token": "{{public_key}}"
      }
    ],
    "body": "{\"message\": \"{{body_encrypted_base64}}\"}",
    "extra_data": {
      "public_key": "PGW-PUBLICKEY-TEST-.....",
      "country": "NG",
      "currency": "NGN",
      "amount": 10,
      "env_url": "klashaenv",
      "gateway": "klashagateway"
    }
  },
  "synchronous": true
}

We define the proxy request as being a "POST" method, having the required Klasha request headers like api-key. For the body we'll set a mustache template that simply renders the body_encrypted_base64 field as the value for message. We set the extra_data object whose fields will be merged with the stored data before the rule(s) are applied. All the fields outputted by rule operations can be outputted in headers, body and even url fields using mustache templates.

One optional parameter that is set in this example is "synchronous": true. This will tell PCI Vault to wait for the Klasha response to be returned and will send the result back to us when the request completes.

Step 3: Handle the response

The response from Klasha will be wrapped in a JSON object like this:

{
  "id": "SLHpcudvjdUCokB2UCkR4n",
  "status": "success",
  "type": "post",
  "use_static_ip": false,
  "synchronous": true,
  "request": {
    "method": "POST",
    "url": "https://{{env_url}}/pay/aggregators/{{gateway}}/card/payment/v2",
    "headers": [
      {
        "content-type": "application/json"
      },
      ...
    ],
    "body": "{\"message\": \"{{body_encrypted_base64}}\"}",
    "extra_data": {
      "public_key": "PGW-PUBLICKEY-TEST-.....",
      "country": "NG",
      "currency": "NGN",
      "amount": 10,
      "env_url": "klashaenv",
      "gateway": "klashagateway"
    }
  },
  "webhook": {
    "status": "pending"
  },
  "max_attempts": 1,
  "attempts": 0,
  "debug_mode": false,
  "received": "2025-11-12T14:06:06.621177+00:00",
  "result": {
    "headers": [
      {
        "Content-Type": "application/json"
      },
      ...
    ],
    "body": "[third party resonse as JSON string]", //response body from Klasha
    "status_code": 200
  }
}

The response from Klasha is in the result field. The body is returned as a JSON string, which can be parsed and handled appropriately.