1.2.2. Tutoriel pour l’API Taler Merchant#

1.2.2.1. Introduction#

1.2.2.1.1. À propos de GNU Taler#

GNU Taler est un protocole ouvert pour un système de paiement électronique avec une implémentation de référence en logiciel libre. GNU Taler offre un traitement sécurisé, rapide et facile des paiements en utilisant des techniques cryptographiques bien connues. GNU Taler permet aux clients de rester anonymes, tout en garantissant que les marchands peuvent être tenus pour responsables par les gouvernements. GNU Taler est donc compatible avec les réglementations relatives à la lutte contre le blanchiment d’argent (AML) et à la connaissance du client (KYC), ainsi qu’avec les réglementations relatives à la protection des données (telles que le GDPR).

1.2.2.1.2. A propos de ce tutoriel#

Ce tutoriel explique comment traiter les paiements en utilisant le backend marchand de GNU Taler. Les destinataires de ce tutoriel sont les développeurs de commerçants (tels que les boutiques en ligne) qui travaillent à l’intégration de GNU Taler avec le Frontend orienté vers le client et le Backoffice orienté vers le personnel.

Ce chapitre explique quelques concepts de base. Dans le deuxième chapitre, vous apprendrez à effectuer des paiements de base.

Cette version du tutoriel contient des exemples pour Python3. Elle utilise la bibliothèque requests pour les requêtes HTTP. Des versions pour d’autres langages/environnements sont également disponibles.

Si vous voulez voir des exemples simples et concrets, jetez un coup d’œil à ceux qui suivent :

1.2.2.1.3. Aperçu de l’architecture#

La pile logicielle Taler pour un commerçant se compose des principaux éléments suivants :

  • Une interface qui interagit avec le navigateur du client. Il permet au client de constituer un panier d’achat et de passer une commande. Au moment du paiement, il déclenche la logique commerciale correspondante pour satisfaire la commande. Ce composant n’est pas inclus dans Taler, mais il est supposé exister chez le commerçant. Ce tutoriel décrit comment développer un frontend Taler.

  • Un backend de paiement spécifique à Taler qui facilite le traitement des transactions financières par le frontend avec Taler. Pour ce tutoriel, vous utiliserez un backend public. Pour une utilisation en production, vous devez soit configurer votre propre backend, soit demander à une autre personne de le faire pour vous.

L’image suivante illustre les différentes interactions de ces composants clés :

../../_images/arch-api.png

Le backend fournit le support du protocole cryptographique, stocke les informations financières spécifiques à Taler et communique avec l’échange GNU Taler sur Internet. Le frontend accède au backend via une API RESTful. Par conséquent, le frontend n’a jamais à communiquer directement avec l’échange et ne traite pas non plus de données sensibles. En particulier, les clés de signature du commerçant et les informations relatives à son compte bancaire sont encapsulées dans le backend Taler.

Certaines fonctionnalités du backend (l“« interface publique ») sont exposées directement au navigateur du client. Dans l’API HTTP, tous les points de terminaison privés (pour le Backoffice) sont préfixés par /private/. Ce tutoriel se concentre sur les points d’extrémité /private/. L’interface publique est directement utilisée par le portefeuille et n’est pas pertinente pour le marchand (à part le fait que l’API doit être exposée).

1.2.2.1.4. Backend et authentification de l’environnement de test public#

La façon dont le frontend s’authentifie auprès du backend Taler dépend de la configuration. Voir Manuel de l’opérateur du backend marchand.

Le backend public sandbox https://backend.demo.taler.net/instances/sandbox/ utilise une clé API dans l’en-tête Authorization. La valeur de cet en-tête doit être Bearer secret-token:sandbox pour le backend public sandbox.

>>> import requests
>>> requests.get("https://backend.demo.taler.net/instances/sandbox/private/orders",
...              headers={"Authorization": "Bearer secret-token:sandbox"})
<Response [200]>

Si un code d’état HTTP autre que 200 est renvoyé, c’est que quelque chose n’a pas fonctionné. Vous devez déterminer quel est le problème avant de poursuivre ce tutoriel.

Le backend sandbox https://backend.demo.taler.net/instances/sandbox/ utilise le KUDOS comme monnaie imaginaire. Des pièces libellées en KUDOS peuvent être retirées de https://bank.demo.taler.net/.

1.2.2.1.5. Merchant Instances#

Un seul serveur de backend marchand Taler peut être utilisé par plusieurs marchands qui sont des entités commerciales distinctes. Chacune de ces entités commerciales distinctes se voit attribuer une instance marchand qui est identifiée par un identifiant d’instance alphanumérique. Si l’instance est omise, l’identifiant d’instance admin est supposé.

Les instances marchandes suivantes sont configurées sur https://backend.demo.taler.net/ :

Note

Il s’agit de marchands fictifs utilisés pour nos démonstrateurs et qui ne sont ni affiliés ni officiellement approuvés par les projets respectifs.

Tous les points d’extrémité des instances offrent la même API. Ainsi, l’instance à utiliser est simplement incluse dans l’URL de base du backend du commerçant.

1.2.2.2. Traitement des paiements des commerçants#

1.2.2.2.1. Création d’une commande de paiement#

Les paiements dans Taler s’articulent autour d’une commande, qui est une description lisible par machine de la transaction commerciale pour laquelle le paiement doit être effectué. Avant d’accepter un paiement dans Taler en tant que commerçant, vous devez créer une telle commande.

Cela se fait en envoyant un objet JSON au point d’arrivée /private/orders de l’API du backend. Au moins les champs suivants doivent être fournis dans le champ order :

  • amount : Le montant à payer, sous forme de chaîne au format CURRENCY:DECIMAL_VALUE, par exemple EUR:10 pour 10 Euros ou KUDOS:1.5 pour 1.5 KUDOS.

  • summary : Un résumé lisible par l’homme de l’objet du paiement. Le résumé doit être suffisamment court pour être inséré dans les titres, bien qu’aucune limite stricte ne soit imposée.

  • fulfillment_url : URL qui sera affichée une fois le paiement effectué. Pour les biens numériques, il doit s’agir d’une page qui affiche le produit qui a été acheté. Lors d’un paiement réussi, le portefeuille ajoute automatiquement le order_id en tant que paramètre de requête, ainsi que le session_sig pour les paiements liés à une session (voir ci-dessous).

Les commandes peuvent avoir beaucoup plus de champs, voir Le format de commande Taler. Lorsque vous postez une commande, vous pouvez également spécifier des détails supplémentaires tels qu’une dérogation pour la durée de remboursement et des instructions pour la gestion de l’inventaire. Ces détails sont rarement nécessaires et ne sont pas abordés dans ce tutoriel ; veuillez consulter le manuel de référence pour plus de détails.

Un extrait Python minimal pour la création d’une commande ressemblerait à ceci :

>>> import requests
>>> body = dict(order=dict(amount="KUDOS:10",
...                        summary="Donation",
...                        fulfillment_url="https://example.com/thanks.html"),
...             create_token=False)
>>> response = requests.post("https://backend.demo.taler.net/instances/sandbox/private/orders",
...               json=body,
...               headers={"Authorization": "Bearer secret-token:sandbox"})
<Response [200]>

Le backend complétera certains détails manquants dans la commande, tels que l’adresse de l’instance marchande. Les détails complets sont appelés les conditions du contrat.

Note

La requête ci-dessus désactive l’utilisation des jetons de réclamation en mettant l’option create_token à false. Si vous avez besoin de jetons de réclamation, vous devez ajuster le code pour construire l’URI taler://pay/ donné ci-dessous pour inclure le jeton de réclamation.

Après avoir réussi à POST vers /private/orders, un JSON avec juste un champ order_id avec une chaîne de caractères représentant l’ID de la commande sera retourné. Si vous obtenez également un jeton de réclamation, vérifiez que vous avez utilisé la requête comme décrit ci-dessus.

Avec l”instance du marchand, l’identifiant de la commande identifie de manière unique la commande au sein du backend du marchand. En utilisant l’identifiant de la commande, vous pouvez trivialement construire l’URI taler://pay/ qui doit être fourni au portefeuille. Prenons exemple.com comme nom de domaine où les terminaux publics de l’instance sont accessibles. L’URI de Taler pay est alors simplement taler://pay/example.com/$ORDER_ID/$ORDER_ID doit être remplacé par l’ID de la commande qui a été retournée.

Vous pouvez placer l’URI taler:// comme cible d’un lien pour ouvrir le portefeuille Taler via le schéma taler://, ou l’insérer dans un code QR. Cependant, pour une boutique en ligne, le plus simple est de rediriger le navigateur vers https://example.com/orders/$ORDER_ID. Cette page déclenchera alors le portefeuille Taler. Ici, le backend génère la bonne logique pour déclencher le portefeuille, en prenant en charge les différents types de portefeuilles Taler existants. Au lieu de construire l’URL ci-dessus à la main, il est également possible de l’obtenir en vérifiant l’état du paiement comme décrit dans la section suivante.

Lorsque vous construisez manuellement cette URL, assurez-vous de fournir le jeton de réclamation (à moins qu’il n’ait été désactivé) et si le backend fonctionne sans TLS, utilisez taler+http:// (notez que ce dernier n’est supporté que par les portefeuilles fonctionnant en mode débogage).

Note

Une façon triviale d’obtenir le bon payment_redirect_url est de vérifier le statut du paiement (voir ci-dessous). Donc, si vous n’êtes pas sûr de la façon de la construire, vous pouvez simplement demander au backend de le faire pour vous. Cependant, en production, vous devriez probablement le construire manuellement et éviter la requête supplémentaire au backend.

1.2.2.2.2. Vérification de l’état des paiements et demande de paiement#

Compte tenu de l’identifiant de la commande, le statut d’un paiement peut être vérifié avec le point de terminaison /private/orders/$ORDER_ID`. Si le paiement n'a pas encore été effectué par le client, ``/private/orders/$ORDER_ID donnera au frontend une URL (sous le nom payment_redirect_url) qui déclenchera l’exécution du paiement par le portefeuille du client. C’est en fait l’URL https://example.com/orders/$ORDER_ID dont nous avons parlé plus haut.

>>> import requests
>>> r = requests.get("https://backend.demo.taler.net/instances/sandbox/private/orders/" + order_id,
...                  headers={"Authorization": "Bearer secret-token:sandbox"})
>>> print(r.json())

Si le champ order_status de la réponse est paid, vous n’obtiendrez pas de payment_redirect_url mais des informations sur le statut du paiement, notamment :

  • contract_terms : Les conditions contractuelles complètes de la commande.

  • refunded : vrai si un remboursement (éventuellement partiel) a été accordé pour cet achat.

  • refunded_amount : Montant remboursé

Une fois que le frontend a confirmé que le paiement a été effectué avec succès, il doit généralement déclencher la logique de gestion du commerçant pour que ce dernier remplisse ses obligations contractuelles.

Note

Vous n’avez pas besoin de continuer à faire des requêtes pour remarquer les changements dans le statut de la transaction de la commande. Les points de terminaison supportent l’interrogation longue, il suffit de spécifier un paramètre de requête timeout_ms avec le temps d’attente maximum pour que le statut de la commande passe à payé.

1.2.2.3. Remboursements#

Dans GNU Taler, un remboursement est un moyen d“« annuler » un paiement. Il doit être autorisé par le commerçant. Les remboursements peuvent correspondre à n’importe quelle fraction du montant original payé, mais ils ne peuvent pas dépasser le paiement original. Les remboursements sont limités dans le temps et ne peuvent avoir lieu que pendant que la bourse détient les fonds d’un paiement particulier en dépôt fiduciaire. Le temps pendant lequel un remboursement est possible peut être contrôlé en définissant le refund_deadline dans une commande. La valeur par défaut de ce délai de remboursement est spécifiée dans la configuration du backend du marchand.

Le frontend peut demander au backend du commerçant d’autoriser un remboursement en envoyant un POST au point de terminaison private/orders/$ORDER_ID/refund.

L’objet JSON de la demande de remboursement ne comporte que deux champs :

  • refund : Montant à rembourser. Si un remboursement précédent a été autorisé pour la même commande, le nouveau montant doit être plus élevé, sinon l’opération n’a pas d’effet. La valeur indique le montant total à rembourser, pas une augmentation du remboursement.

  • reason : Justification lisible par l’homme du remboursement. La raison n’est utilisée que par le Back Office et n’est pas exposée au client.

Si la requête est réussie (indiquée par le code de statut HTTP 200), la réponse inclut un taler_refund_uri. Le frontend doit rediriger le navigateur du client vers cette URL pour permettre au remboursement d’être traité par le portefeuille.

Cet extrait de code illustre l’octroi d’un remboursement :

>>> import requests
>>> refund_req = dict(refund="KUDOS:10",
...                   reason="Customer did not like the product")
>>> requests.post("https://backend.demo.taler.net/instances/sandbox/private/orders/"
...               + order_id + "/refund", json=refund_req,
...               headers={"Authorization": "Bearer secret-token:sandbox"})
<Response [200]>

Note

Après avoir accordé un remboursement, le point de terminaison public https://example.com/orders/$ORDER_ID changera son interaction avec le portefeuille, passant d’une demande de paiement à une offre de remboursement. Ainsi, les frontends peuvent à nouveau rediriger les navigateurs vers ce point d’accès. Cependant, pour ce faire, un champ h_contract doit être ajouté (?h_contract=$H_CONTRACT) car le point de terminaison public en a besoin pour authentifier le client. La valeur requise $H_CONTRACT est retournée dans la réponse de remboursement sous le champ h_contract.

1.2.2.4. Détection des rachats et URL d’exécution#

Un problème possible pour les commerçants qui vendent l’accès à des articles numériques est qu’un client peut avoir payé pour un article sur un appareil, mais peut ensuite vouloir le lire sur un autre appareil, peut-être un appareil qui n’a même pas de porte-monnaie Taler installé.

Naturellement, à ce stade, le client sera encore invité à payer à nouveau pour l’article. Si le client ouvre ensuite le lien taler:// dans le portefeuille qui a précédemment payé l’article (par exemple en scannant le code QR sur le bureau avec l’application Android), le portefeuille réclamera le contrat, détectera que l’URL d’exécution est identique à une URL pour laquelle il a déjà effectué un paiement dans le passé, et lancera une réorientation d’achat : Le portefeuille contacte alors le commerçant et rejoue le paiement précédent, mais cette fois en utilisant l’identifiant de session (actuel) du navigateur (il apprend l’identifiant de session à partir du code QR).

Le backend du commerçant met alors à jour l’ID de session de la commande existante en fonction de l’ID de session actuel du navigateur. Lorsque le statut de paiement de la « nouvelle » commande non payée est vérifié (ou déjà en interrogation longue), le backend détecte que pour les session ID et fulfillment URL du navigateur, il existe un contrat payé existant. Il demande alors au navigateur de rediriger immédiatement vers l’URL d’exécution où l’article déjà payé est disponible.

Pour que ce mécanisme fonctionne comme prévu, les marchands doivent veiller à ne pas utiliser le même URL de traitement pour différents produits ou pour des produits physiques pour lesquels les clients sont susceptibles d’acheter l’article à plusieurs reprises. De même, il est essentiel que les commerçants utilisent systématiquement la même URL de traitement pour le même produit numérique lorsque la détection des achats répétés est souhaitée.

Il est à noter que la modification de l’identifiant de session vers un autre appareil nécessite l’intervention du portefeuille qui a effectué le paiement, ce qui limite raisonnablement la possibilité de partager les achats numériques à grande échelle. La détection des rachats est également uniquement effectuée pour les URL d’exécution HTTP(S). En particulier, cela signifie que les URI d’exécution comme taler://fulfillment-success/$MESSAGE ne sont pas considérés comme identifiant une ressource pour laquelle vous pouvez payer et n’ont donc pas besoin d’être uniques.

1.2.2.5. Thèmes avancés#

1.2.2.5.1. Paiements liés à une session#

Parfois, il ne suffit pas de vérifier si une commande a été payée. Par exemple, lors de la vente d’un accès à des médias en ligne, l’éditeur peut vouloir être payé pour exactement le même produit par chaque client. Taler prend en charge ce modèle en permettant au marchand de vérifier si le « reçu de paiement » est disponible sur l’appareil actuel de l’utilisateur. Cela empêche les utilisateurs de partager facilement l’accès aux supports en transmettant un lien vers la page de paiement. Bien sûr, les utilisateurs avertis pourraient également partager les reçus de paiement, mais ce n’est pas aussi facile que de partager un lien et, dans ce cas, il est plus probable qu’ils partagent directement les médias.

Pour utiliser cette fonctionnalité, le marchand doit d’abord attribuer au navigateur actuel de l’utilisateur un sessionid éphémère, généralement par le biais d’un cookie de session. Lors de l’exécution ou de la relecture d’un paiement, le portefeuille recevra une signature supplémentaire (session_sig). Cette signature certifie que le portefeuille a montré un reçu de paiement pour la commande concernée dans la session en cours.

Les paiements liés à une session sont déclenchés en passant le paramètre session_id au point de terminaison check-payment. Le portefeuille redirigera alors vers la page de paiement, mais inclura un paramètre supplémentaire session_sig. Le frontend peut interroger /check-payment avec les deux paramètres session_id et session_sig pour vérifier que la signature est correcte.

Le dernier identifiant de session qui a été utilisé avec succès pour prouver que le reçu de paiement se trouve dans le portefeuille de l’utilisateur est également disponible en tant que last_session_id dans la réponse à /check-payment.

1.2.2.5.2. Identification des produits#

Dans certaines situations, l’utilisateur peut avoir payé pour un bien numérique, mais le frontend ne connaît pas l’identifiant exact de la commande et ne peut donc pas demander au portefeuille de révéler le reçu de paiement existant. Cette situation est fréquente dans les magasins simples qui ne disposent pas d’un système de connexion. Dans ce cas, l’utilisateur est invité à payer à nouveau, même s’il a déjà acheté le produit.

Pour permettre au portefeuille de trouver le reçu de paiement existant, la boutique doit utiliser une URL unique pour chaque produit. Ensuite, le frontend doit fournir un paramètre resource_url supplémentaire à /check-payment. Il doit identifier cette URL unique pour le produit. Le portefeuille vérifiera alors s’il a déjà payé pour un contrat avec la même resource_url, et si c’est le cas, il rejouera le paiement précédent.

1.2.2.5.3. Le format de commande Taler#

Une commande Taler peut spécifier de nombreux détails sur le paiement. Cette section décrit en détail chacun des champs.

Les montants financiers sont toujours spécifiés sous la forme d’une chaîne de caractères au format « CURRENCY:DECIMAL_VALUE ».

montant

Indique le montant total à payer au commerçant par le client.

max_fee

Il s’agit du montant total maximum des frais de dépôt que le commerçant est prêt à payer. Si les frais de dépôt pour les pièces dépassent ce montant, le client doit l’inclure dans le total du paiement. Les frais sont spécifiés en utilisant le même format que celui utilisé pour amount.

max_wire_fee

Frais de virement maximum acceptés par le commerçant (la part du client doit être divisée par le facteur wire_fee_amortization, et réduite si les frais de dépôt sont inférieurs à max_fee). La valeur par défaut en cas d’absence est zéro.

Amortissement de la taxe sur les fils

Sur combien de transactions le commerçant s’attend-il à amortir les frais de virement en moyenne ? Si les frais de virement de l’échange sont supérieurs au « max_wire_fee », la différence est divisée par ce nombre pour calculer la contribution attendue du client aux frais de virement. La contribution du client peut être réduite par la différence entre max_fee et la somme des frais de dépôt réels. Facultatif, la valeur par défaut si elle est manquante est 1. Les valeurs nulles et négatives ne sont pas valides et sont également interprétées comme 1.

pay_url

URL qui accepte les paiements. Il s’agit de l’URL où le portefeuille POST les pièces.

fulfillment_url

À quelle URL le portefeuille doit-il se rendre pour obtenir l’exécution, par exemple le code HTML ou le PDF d’un article acheté, ou un système de suivi des commandes pour les expéditions, ou une simple page web lisible par l’homme indiquant l’état d’avancement du contrat.

order_id

Identifiant alphanumérique, librement défini par le commerçant. Utilisé par le commerçant pour identifier la transaction de manière unique.

résumé

Résumé succinct du contrat, lisible par l’homme. À utiliser lorsque le contrat est affiché sur une seule ligne, par exemple dans l’historique des transactions du client.

horodatage

Heure à laquelle l’offre a été générée.

pay_deadline

Horodatage de l’heure à laquelle le commerçant souhaite que la bourse transfère définitivement l’argent dû au titre de ce contrat. Une fois ce délai expiré, la bourse regroupera tous les dépôts pour lesquels les contrats ont dépassé la date limite de remboursement et effectuera un seul virement important. Les montants seront arrondis à l’unité de virement ; si le montant total est encore inférieur à l’unité de virement, il ne sera pas déboursé.

délai_de_remboursement

Date limite jusqu’à laquelle le marchand souhaite (et peut) effectuer des remboursements pour le contrat en utilisant Taler. Notez que l’échange Taler conservera le paiement en dépôt fiduciaire au moins jusqu’à cette date limite. Jusqu’à cette date, le marchand pourra signer un message pour déclencher un remboursement au client. Passé ce délai, il ne sera plus possible de rembourser le client. Doit être plus petit que le pay_deadline.

produits

Tableau des produits vendus au client. Chaque entrée contient un tuple avec les valeurs suivantes :

description

Description du produit.

quantité

Quantité d’articles à expédier. Peut spécifier une unité (par exemple 1 kg) ou simplement le nombre.

prix

Prix pour une quantité d’unités de ce produit expédiées à la delivery_location donnée. Notez que la somme de tous les prix devrait normalement correspondre au montant total du contrat, mais elle peut être différente en raison de remises ou parce que les prix individuels ne sont pas disponibles.

produit_id

ID unique du produit dans le catalogue du marchand. Il peut généralement être choisi librement car il n’a de sens que pour le marchand, mais doit être un nombre compris dans l’intervalle [0,2^{51}).

impôts

Carte des taxes applicables à payer par le commerçant. L’étiquette est le nom de la taxe, c’est-à-dire la TVA, la taxe sur les ventes ou l’impôt sur le revenu, et la valeur est le montant de la taxe applicable. Il convient de noter que les étiquettes arbitraires sont autorisées, à condition qu’elles soient utilisées pour identifier le régime fiscal applicable. Des détails peuvent être spécifiés par l’autorité de régulation. Cette information est utilisée pour déclarer au client les taxes que le commerçant a l’intention de payer, et peut être utilisée par le client comme un reçu. Ces informations sont également susceptibles d’être utilisées dans le cadre d’un contrôle fiscal du commerçant.

date_de_livraison

Heure à laquelle le produit doit être livré au lieu de livraison.

lieu de livraison

Cela devrait donner une étiquette dans la carte locations, spécifiant l’endroit où l’article doit être livré.

Les valeurs peuvent être omises si elles ne sont pas applicables. Par exemple, si un achat concerne un ensemble de produits qui n’ont pas de prix individuels ou d’identifiants de produits, le product_id ou le price peuvent ne pas être spécifiés dans le contrat. De même, pour les produits virtuels livrés directement via l’URI d’exécution, il n’y a pas de delivery_location.

merchant
adresse

Cela devrait donner une étiquette dans la carte locations, spécifiant où le marchand est situé.

nom

Il s’agit d’un nom lisible par l’homme pour l’entreprise du commerçant.

juridiction

Cela devrait donner une étiquette dans la carte locations, spécifiant la juridiction sous laquelle ce contrat doit être arbitré.

lieux

Carte associative des lieux utilisés dans le contrat. Les étiquettes des lieux figurant sur cette carte peuvent être choisies librement et utilisées chaque fois qu’un lieu est requis dans d’autres parties du contrat. Ainsi, si le même lieu est requis à plusieurs reprises (comme l’adresse professionnelle du client ou du commerçant), il ne doit être répertorié (et transmis) qu’une seule fois et peut être mentionné par l’intermédiaire de l’étiquette. Voici une liste non exhaustive d’attributs de localisation :

nom

Nom du destinataire de la livraison, qu’il s’agisse d’une entreprise ou d’une personne.

pays

Nom du pays de livraison, tel qu’il figure sur un colis postal, par exemple « France ».

État

Nom de l’État de livraison, tel qu’il figure sur un colis postal, par exemple « NY ».

région

Nom de la région de livraison, tel qu’il figure sur un colis postal.

provinces

Nom de la province de livraison, tel qu’il figure sur un colis postal.

ville

Nom de la ville de livraison, tel qu’il figure sur un colis postal.

code postal

Code postal pour la livraison, tel qu’il figure sur un colis postal.

rue

Nom de rue pour la livraison, tel qu’il figure sur un colis postal.

numéro de rue

Numéro de rue (numéro de la maison) pour la livraison, tel qu’il figure sur un colis postal.

Note

Les lieux ne sont pas obligés de spécifier tous ces champs, et ils sont également autorisés à en avoir d’autres. Les moteurs de rendu des contrats doivent rendre au moins les champs énumérés ci-dessus et doivent rendre les champs qu’ils ne comprennent pas sous la forme d’une liste clé-valeur.