Skip to content

Retrieving a card by transaction

Returns the customer's linked card that was used for the operation, by the transaction identifier in the merchant's system. The response contains the card token for repeat payments, the masked card number, the issuing bank details and, if a public key is set for the shop, the encrypted card number hash.

Integration flow

  1. Process an operation with card linking: pass customer_key and bind_card: true, for example in a card payment via H2H or a payment with preauthorization.
  2. Save the transaction and customer_key of this operation.
  3. Generate a signature from X-Api-Key, transaction, customer_key, and secret_key.
  4. Send the request with the same X-Api-Key used to create the operation.
  5. Save the card token to use it for payments with the linked card.

URL

GET https://api.pay.kvell.group/v1/customers/{customer_key}/orders/{transaction}
GET https://api.pay.stage.kvell.group/v1/customers/{customer_key}/orders/{transaction}

Request

Path parameters

Parameter Type Required Description
customer_key string Yes Customer identifier in the merchant's system.
transaction string Yes Operation identifier in the merchant's system.

Headers

Name Type Required Description
X-Api-Key string Yes Shop identifier.
X-Signature string Yes Request signature.

Building the signature

Concatenate X-Api-Key, transaction, customer_key, and secret_key without separators, compute the SHA-256 hash of the resulting UTF-8 encoded string, and pass the hash in lowercase in the X-Signature header.

SHA256(X-Api-Key + transaction + customer_key + secret_key)

secret_key is found in the shop's settings.

Example

curl --request GET \
  --url 'https://api.pay.stage.kvell.group/v1/customers/customer-42/orders/payment-20261002-0001' \
  --header 'X-Api-Key: <api-key>' \
  --header 'X-Signature: <signature>'

Response

Select an HTTP code to see an example, the response parameters, and the recommended actions.

Example 200 (OK) response
{
  "customer_id": 25,
  "token": "4VNSbS7nF410G8WLEhUUUnflH2UKvWlHxIzN3Jr9JJs",
  "pan_mask": "411111******1111",
  "holder": "IVAN IVANOV",
  "expire": "2034-12",
  "created_at": "2026-10-02T07:52:29.720321Z",
  "pan_hash_encrypted": "EPNS42BPO6yazHvd6BKd...ObPg6EKv7mYKUy7zc8s=",
  "bank_id": "100000000004",
  "bank_name": "Tinkoff Bank",
  "bank_url": "tinkoff.ru"
}

Response parameters

Parameter Type Description
customer_id integer Customer identifier in KVELL.
token string Linked card token. Used for paying with a linked card.
pan_mask string Masked card number.
holder string | null Cardholder name.
expire string | null Card expiration date in YYYY-MM format.
created_at string Date and time the card was linked, in ISO 8601 format.
pan_hash_encrypted string | null Card number hash encrypted with the shop's public key, in Base64. Decryption is described in «Decrypting the card number hash».
bank_id string | null Card issuing bank identifier.
bank_name string | null Card issuing bank name.
bank_url string | null Card issuing bank website domain.

What this response means

The operation was found, and the card used for it is linked to the customer.

The bank_id, bank_name, and bank_url fields are determined by the card BIN and are returned as null if the bank could not be identified. If an error occurs while encrypting the card number hash, pan_hash_encrypted is returned as null, and the other response fields are filled in.

What to do next

  1. Save the token and use it for payments with the linked card.
  2. If you need pan_hash_encrypted, decrypt it with the shop's private key.
Example 400 (Bad Request) response
{
  "errors": [
    {
      "code": 20002,
      "message": "Invalid signature"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
errors[].code integer Error code. In the example — 20002.
errors[].message string Description of the reason the request was rejected.
Example 403 (Forbidden) response
{
  "errors": [
    {
      "code": 20037,
      "message": "Access denied"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
errors[].code integer Error code. In the example — 20037.
errors[].message string Description of the reason access was denied.
Example 404 (Not Found) response
{
  "errors": [
    {
      "code": 20009,
      "message": "Order not found"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
errors[].code integer Error code. In the example — 20009.
errors[].message string Description of the resource that was not found.
Example 422 (Unprocessable Entity) response
{
  "errors": [
    {
      "code": 20017,
      "message": "customer_key is not set"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
errors[].code integer Error code. In the example — 20017.
errors[].message string Reason for the error.
Example 5XX (Internal Server Error) response
{
  "errors": [
    {
      "code": 20000,
      "message": "Unknown error"
    }
  ]
}

Response parameters

Parameter Type Description
errors array List of errors. Codes and handling recommendations are given in «HTTP response errors».
errors[].code integer Error code. For an unknown error — 20000.
errors[].message string Description of the technical error.

What to do next

  1. Safely retry the same GET request with the original customer_key and transaction.
  2. If the error persists, contact KVELL support and provide the URL, request time, HTTP code, code, and message.

Decrypting the card number hash

The pan_hash_encrypted field lets you match the card against cards in your system without receiving the card number. KVELL computes the SHA-256 hash of the card number, writes the result as a lowercase hexadecimal string, and encrypts it with the shop's public key.

Parameter Value
Algorithm RSA-OAEP
Hash function SHA-256
Mask generation function MGF1 with SHA-256
Label Not used
Result encoding Base64

Decrypt the value with the shop's private key. The result is a string of 64 hexadecimal characters that you can compare with sha256(pan) computed on your side.

Working with the private key

Store the private key only on your server. Do not send it to KVELL or use it in client-side code.

Requires the package: pip install cryptography.

import base64

from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding

with open('private_key.pem', 'rb') as key_file:
    private_key = serialization.load_pem_private_key(key_file.read(), password=None)

# value of the pan_hash_encrypted field from the method response
encrypted_base64 = 'EPNS42BPO6yazHvd6BKd...ObPg6EKv7mYKUy7zc8s='

pan_hash = private_key.decrypt(
    base64.b64decode(encrypted_base64),
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None,
    ),
).decode('ascii')

print(pan_hash)

Uses the built-in node:crypto module.

const crypto = require('node:crypto');
const fs = require('node:fs');

const privateKeyPem = fs.readFileSync('private_key.pem', 'utf8');

// value of the pan_hash_encrypted field from the method response
const encryptedBase64 = 'EPNS42BPO6yazHvd6BKd...ObPg6EKv7mYKUy7zc8s=';

const panHash = crypto.privateDecrypt(
  {
    key: privateKeyPem,
    padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
    oaepHash: 'sha256',
  },
  Buffer.from(encryptedBase64, 'base64'),
).toString('ascii');

console.log(panHash);