# Decrypt callback

{% hint style="info" %}
TIP

In order to ensure security, tokenpay payment encrypts the key information in the callback notification interface with AES-256-GCM. After the merchant receiving the message, and needs to decrypt the plaintext. The key of APP is used for decryption. This chapter describes the format of the encrypted message and how to decrypt it.
{% endhint %}

### Encrypt message format <a href="#encrypt-message-format" id="encrypt-message-format"></a>

First, the merchant get the following information from the answer.

* AES-GCM is a NIST-standardized [authenticated encryption](https://en.wikipedia.org/wiki/Authenticated_encryption)algorithm, a mode of encryption that simultaneously guarantees the confidentiality, integrity, and authenticity of data. It is most widely used in TLS.
* The key used for the callback message is the APP key.
* For the encrypted data, we use a separate JSON object to represent it. In order to read conveniently, the example uses Pretty formatting and adds comments.

```json
{
    "original_type": "transaction", // Object types before encryption
    "algorithm": "AEAD_AES_256_GCM", // Encryption algorithm

    // After encoding with Base6
    "ciphertext": "...",

    // Random string initialization vector used for encryption
    "nonce": "...",
}
```

Note: The encrypted random string has nothing to do with the random string used for signing. They're not the same.

### Decryption <a href="#decryption" id="decryption"></a>

* Algorithmic interface details, you can refer to [rfc5116](https://datatracker.ietf.org/doc/html/rfc5116).
* Most of the programming languages (newer version) both support AEAD\_AES\_256\_GCM. Developers can refer to the following example, and learn how to implement decryption using your programming language.

### Code example <a href="#code-example" id="code-example"></a>

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

```java
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AesUtil {
  static final int KEY_LENGTH_BYTE = 32;
  static final int TAG_LENGTH_BIT = 128;
  private final byte[] aesKey;

    public AesUtil(byte[] key) {
      if (key.length != KEY_LENGTH_BYTE) {
        throw new IllegalArgumentException("无效的AppSecret，长度必须为32个字节");
        }
      this.aesKey = key;
    }

  public String decryptToString(byte[] nonce, String ciphertext)
  throws GeneralSecurityException, IOException {
    try {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
        GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

        cipher.init(Cipher.DECRYPT_MODE, key, spec);

        return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
       } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
         throw new IllegalStateException(e);
       } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
         throw new IllegalArgumentException(e);
        }
    }
}

```

<br>
{% endtab %}

{% tab title="PHP" %}

````php
class AesUtil {
  /**
  * AES key
  *
  * @var string
  */
  private $aesKey;

  const KEY_LENGTH_BYTE = 32;
  const AUTH_TAG_LENGTH_BYTE = 16;

 /**
  * Constructor
  */
  public
  function __construct($aesKey) {
    if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {
      throw new InvalidArgumentException('无效的AppSecret，长度应为32个字节');
    }
     $this - > aesKey = $aesKey;
    }

   /**
    * Decrypt AEAD_AES_256_GCM ciphertext
    *
    * @param string    $associatedData     AES GCM additional authentication data
    * @param string    $nonceStr           AES GCM nonce
    * @param string    $ciphertext         AES GCM cipher text
    *
    * @return string|bool      Decrypted string on success or FALSE on failure
    */
  public
  function decryptToString($associatedData, $nonceStr, $ciphertext) {
    $ciphertext = \base64_decode($ciphertext);
    if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
      return false;
    }

    // ext-sodium (default installed on >= PHP 7.2)
    if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) {
        return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this - > aesKey);
      }

    // ext-libsodium (need install libsodium-php 1.x via pecl)
    if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
      return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this - > aesKey);
    }

    // openssl (PHP >= 7.1 support AEAD)
    if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
    $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
    $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);

    return \openssl_decrypt($ctext, 'aes-256-gcm', $this - > aesKey, \OPENSSL_RAW_DATA, $nonceStr,$authTag, $associatedData);

    }
    throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
  }
}

  </CodeGroupItem>

  <CodeGroupItem title="Java">

```java:no-line-numbers
OkHttpClient client = new OkHttpClient().newBuilder()
   .build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "<body data here>");
Request request = new Request.Builder()
   .url("https://api.ttpay.io/v1/wallet/update")
   .method("POST", body)
   .addHeader("Authorization", "<Authorization>")
   .addHeader("User-Agent", "TTPay API (https://ttpay.io)")
   .addHeader("Content-Type", "application/json")
   .build();
Response response = client.newCall(request).execute();
````

{% endtab %}

{% tab title="Aspnet" %}

```
public class AesGcm {
  private static string ALGORITHM = "AES/GCM/NoPadding";
  private static int TAG_LENGTH_BIT = 128;
  private static int NONCE_LENGTH_BYTE = 12;
  private static string AES_KEY = "yourkeyhere";

  public static string AesGcmDecrypt(string associatedData, string nonce, string ciphertext)
  {
    GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
    AeadParameters aeadParameters = new AeadParameters(
      new KeyParameter(Encoding.UTF8.GetBytes(AES_KEY)),
      128,
      Encoding.UTF8.GetBytes(nonce),
      Encoding.UTF8.GetBytes(associatedData));
    gcmBlockCipher.Init(false, aeadParameters);

    byte[] data = Convert.FromBase64String(ciphertext);
    byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];
    int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
    gcmBlockCipher.DoFinal(plaintext, length);
    return Encoding.UTF8.GetString(plaintext);
  }
}
```

{% endtab %}

{% tab title="Python" %}

```python
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
  import base64
  def decrypt(nonce, ciphertext, associated_data):

    key = "Your32Apiv3Key"

    key_bytes = str.encode(key)
    nonce_bytes = str.encode(nonce)
    ad_bytes = str.encode(associated_data)
    data = base64.b64decode(ciphertext)

    aesgcm = AESGCM(key_bytes)
    return aesgcm.decrypt(nonce_bytes, data, ad_bytes)
```

{% endtab %}
{% endtabs %}


---

# 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://apidoc.tokenpay.me/description/decrypt-callback.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.
