> For the complete documentation index, see [llms.txt](https://apidoc.tokenpay.me/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://apidoc.tokenpay.me/fr-fr/description/decrypter-le-rappel.md).

# Décrypter le rappel

{% hint style="info" %}
ASTUCE (TIP)

Afin d'assurer la sécurité, le paiement tokenpay crypte les informations clés dans l'interface de notification de rappel avec AES-256-GCM. Après avoir reçu le message, le commerçant doit décrypter le texte en clair. La clé de l'APP est utilisée pour le décryptement. Ce chapitre décrit le format du message crypté et la façon de le décrypter.
{% endhint %}

#### Format du message crypté (Encrypt message format)

Tout d'abord, le commerçant obtient les informations suivantes à partir de la réponse.

* AES-GCM est un algorithme de [chiffrement authentifié](https://en.wikipedia.org/wiki/Authenticated_encryption) normalisé par le NIST, un mode de chiffrement qui garantit simultanément la confidentialité, l'intégrité et l'authenticité des données. Il est le plus largement utilisé dans TLS.
* La clé utilisée pour le message de rappel est la clé de l'APP.
* Pour les données cryptées, nous utilisons un objet JSON distinct pour les représenter. Afin de faciliter la lecture, l'exemple utilise le formatage Pretty et ajoute des commentaires.

```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": "...",
}
```

Remarque : La chaîne aléatoire cryptée n'a rien à voir avec la chaîne aléatoire utilisée pour la signature. Ce ne sont pas les mêmes.

#### Décryptement (Decryption)

* Pour les détails de l'interface algorithmique, vous pouvez vous référer à [rfc5116](https://datatracker.ietf.org/doc/html/rfc5116).
* La plupart des langages de programmation (versions récentes) prennent en charge AEAD\_AES\_256\_GCM. Les développeurs peuvent se référer à l'exemple suivant et apprendre à implémenter le décryptement à l'aide de leur langage de programmation.

#### Exemple de code (Code example)

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