Date d'échéance du contrat (Contracts)

v2.7.0 · CRUD · Contrats et abonnements

Contrats

Lire, créer, modifier et supprimer des contrats et des abonnements avec leurs attributs, leurs domaines, l'historique de leur statut et les factures associées. Le statut est modifié via un sous-point de terminaison dédié.

GET    /Export/JSON/contracts
GET    /Export/JSON/contract/{id}
POST   /Import/JSON/contracts
PUT    /Import/JSON/contracts
PUT    /Import/JSON/contracts/{id}/status
DELETE /Delete/JSON/contract/{id}

Points de terminaison

MéthodePoint de terminaisonPortéeDescription
GET/Export/JSON/contractscontracts:readTous les contrats, classés par ordre croissant d'ID (avec filtres)
GET/Export/JSON/contract/{id}contracts:readUn contrat ; en l'absence de résultats, réponse HTTP 200 avec un corps vide data
GET/Export/JSON/contracts/{von}:{bis}, /contracts/{von}:contracts:readplage d'ID ou plage ouverte
GET/Export/JSON/contracts/{id}contracts:read⚠️ Pluriel : tous les contrats à partir de cet ID, pas seulement un seul
POST/Import/JSON/contractscontracts:writeCréer de nouveaux contrats
PUT/Import/JSON/contractscontracts:writeModifier des contrats existants
PUT/Import/JSON/contracts/{id}/statuscontracts:writeModifier uniquement le statut, avec enregistrement dans l'historique des statuts
DELETE/Delete/JSON/contract/{id}contracts:deleteSupprimer un contrat, y compris ses attributs, ses domaines et son historique des statuts
DELETE/Delete/JSON/contractscontracts:deleteSupprimer plusieurs contrats via un JSON tronqué {"data": [{"contracts_id": …}]}
Modification dans la v2.122.12 : une plage s'écrit /contracts/{von}:{bis}. Jusqu'à la v2.122.11, /contracts/10/20 renvoyait toujours les contrats à partir de 10 sans limite supérieure ; désormais, cela renvoie une erreur HTTP 400 INVALID_RANGE, tout comme tout autre segment qui n'est ni un nombre entier ni une plage (/contract/10x, /contract/10.0). Les espaces vides à la fin de l’ID ne sont pas pris en compte. Règles : API d’exportation, section « Segment de chemin ».

Filtre de requête (GET)

ParamètresDescriptionExemple
?status=ID d’un statut de contrat ; les statuts de la boutique figurent dans contracts_statuses[] de chaque réponse?status=1
?customer_id=ID client?customer_id=42
?product_id=ID du produit?product_id=100
?interval=Fréquence de facturation par an : 1 annuellement, 2 semestriellement, 3 trois fois par an, 4 trimestriellement, 6 six fois par an, 12 mensuellement?interval=12
?date_from=, ?date_to=Date du contrat (contracts_date) à partir de ou jusqu’au?date_from=2025-01-01
?active=1uniquement les contrats sans date de résiliation indiquée (contracts_cancellation_date_effective vide) ; une date de résiliation future exclut également le contrat?active=1

Plusieurs filtres agissent conjointement. Une valeur invalide génère un code HTTP 400 INVALID_FILTER_VALUE, par exemple ?interval=5 (depuis la v2.122.10). Depuis la v2.122.12, les espaces en bordure ne sont pas pris en compte ; une valeur vide signifie « non défini ».


Structure des données

Chaque contrat comporte ces champs. Les noms des statuts et des articles sont en allemand.

DomaineChamps
Identifiant et statutcontracts_id, contracts_status_id, contracts_status_name
Clientcontracts_customers_id, customer_name, customer_email, contracts_customers_address_book_id, contracts_shipping_address_book_id
Article et prixcontracts_products_id, product_name, product_model, contracts_products_quantity, contracts_products_tax, contracts_price, contracts_final_price, contracts_discount, contracts_use_current_price
Facturationcontracts_interval et contracts_period_interval (valeurs telles que ?interval=, accompagnées chacune d’un champ …_text portant cette désignation), contracts_months, contracts_next_order_date, contracts_payment_class
Périodescontracts_date; durée contracts_current_term_date_begin/_end, période de facturation contracts_current_period_date_begin/_end, dernière facturation contracts_last_billed_term_date_begin/_end et contracts_last_billed_period_date_begin/_end
Résiliationcontracts_cancellation_date_received, contracts_cancellation_date_effective
Domaine et licencecontracts_domain_id, contracts_domain_name, contracts_overwrite_domain, contracts_xolic_id
Diverscontracts_comment, contracts_template, contracts_error_flag, contracts_is_standalone

Données imbriquées

ListeContenu par entrée
attributes[]Caractéristiques de l'article du contrat : contracts_products_attributes_id, contracts_products_id, products_options, products_options_values, options_values_price, detail_id
domains[]contracts_domains_id, contracts_domains_domain_name, contracts_domains_date_paid, contracts_domains_date_termination, contracts_domains_provider, contracts_domains_exclusive, contracts_domains_cost
status_history[]les plus récents en premier : status_history_id, status_id, status_name, date_added, customer_notified, comments, user_id
orders[]factures associées, les plus récentes en premier : orders_id, date_created, date_purchased, orders_status

En plus de data, chaque réponse contient contracts_statuses[] avec les statuts contractuels de la boutique, qui portent une désignation en allemand (status_id, status_name, status_label, status_active, status_is_default).


Importation (POST/PUT)

  • POST crée un enregistrement et nécessite les identifiants « contracts_customers_id » et « contracts_products_id » d’un client ou d’un article existant. Si l’identifiant « contracts_id » transmis existe déjà, l’enregistrement échoue.
  • PUT modifie et nécessite l’ contracts_id d’un contrat existant. Seuls les champs envoyés sont mis à jour.
  • Les champs de la structure de données peuvent être modifiés, à l’exception des noms et des textes (contracts_status_name, customer_name, customer_email, product_name, product_model, contracts_domain_name, …_text). Si l’on renvoie une exportation, l’importation signale ces champs comme inconnus (UNKNOWN_FIELD), de même que les listes status_history[] et orders[] ainsi que les identifiants contracts_products_attributes_id et contracts_products_id dans attributes[] et contracts_domains_id dans domains[]. L'importation les ignore et remplit les autres champs ; supprimez ces champs avant de renvoyer la requête, la réponse ne contiendra alors aucun avertissement. Les champs de date sous la forme YYYY-MM-DD, les champs des périodes de facturation (…_period_date_…) sous la forme YYYY-MM-DD HH:MM:SS; null ou vides les laisse vides.
  • contracts_interval N'accepte que 1, 2, 3, 4, 6 ou 12; sinon, l'enregistrement échoue.
  • attributes[] products_options, , , et utilisent la clé sans . Dans le cas de , une liste transmise remplace toutes les entrées existantes. products_options_values options_values_price detail_id domains[] contracts_domains_… contracts_domains_id PUT
  • status En tant qu’objet {"status_id": …, "comments": "…", "customer_notified": 0}, cette commande définit le statut et ajoute une entrée dans l’historique des statuts. v2.122.13 Si status_id n’est pas un statut de contrat de la boutique (section « Statuts de contrat » ci-dessous), n’est pas un nombre entier ou est manquant, l’importation enregistre l’enregistrement sans le statut et génère l’avertissement CONTRACT_STATUS_UNKNOWN; l’enregistrement est considéré comme créé ou modifié.
  • contracts_status_id La modification directe du contrat définit le statut sans enregistrement dans l’historique des statuts. v2.122.13 Une valeur qui n’est pas un statut de contrat n’est pas enregistrée, contrairement aux autres champs, avec le même avertissement. Pour un changement de statut avec historique, utilisez status ou le sous-point de terminaison /status.
  • ⚠️ Un statut « contracts_cancellation_date_effective » défini peut avoir un effet lors de la prochaine exécution de la résiliation automatique du contrat.

Exemple

curl -s -X POST "https://shop.de/xpanel/xoport/Import/JSON/contracts" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "contracts",
    "data": [{
      "contracts_customers_id": 42,
      "contracts_products_id": 100,
      "contracts_interval": 12,
      "contracts_price": "29.90",
      "contracts_date": "2026-11-01",
      "attributes": [
        {"products_options": "Lizenztyp", "products_options_values": "Enterprise"}
      ],
      "domains": [
        {"contracts_domains_domain_name": "kunde.de"}
      ],
      "status": {"status_id": 1, "comments": "Angelegt per API"}
    }]
  }'

Modification du statut (PUT)

PUT /Import/JSON/contracts/{id}/status avec la portée contracts:write. Le champ status_id est obligatoire, ainsi que, au choix, comments et customer_notified. Sans status_id, la réponse est HTTP 400 MISSING_FIELD; pour une valeur status_id qui n’est pas un statut de contrat de la boutique ou un nombre entier, la réponse est HTTP 400 INVALID_STATUS avec les identifiants valides indiqués dans error.valid_status_ids (depuis la v2.122.13, section « Statut du contrat ») et, pour un contrat inconnu, HTTP 404 NOT_FOUND. Dans les trois cas, le contrat reste inchangé.

curl -s -X PUT "https://shop.de/xpanel/xoport/Import/JSON/contracts/1/status" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status_id": 3, "comments": "Kündigung per API", "customer_notified": 0}'

# Antwort
{
  "success": true,
  "type": "contracts_status",
  "contracts_id": 1,
  "previous_status_id": 1,
  "new_status_id": 3,
  …
}

Si le contrat comporte une date de résiliation effective, la réponse contient en outre warning: la résiliation automatique du contrat peut ultérieurement remplacer ce statut.


Statut du contrat v2.122.13

Pour status_id, status.status_id et contracts_status_id, tous les statuts de contrat de la boutique sont valables : les statuts du tableau status avec type = 'contracts', à partir desquels le backend effectue également ses lectures, dans toutes les langues, y compris ceux qui sont désactivés (status_active = 0) ; cela masque simplement la sélection dans le backend. Les statuts de commande (/order_statuses) ne sont pas des statuts contractuels. Les identifiants applicables varient d’une boutique à l’autre : consultez-les sur contracts_statuses[], sur GET /Export/JSON/contracts ou sur error.valid_status_ids. contracts_statuses[] ne mentionne que les statuts avec une désignation en allemand ; un statut sans désignation en allemand est néanmoins valable.

L’identifiant est un nombre entier, sous forme de chiffre ou de suite de chiffres (54, "54"; les espaces en marge ne comptent pas). true, [65], "54abc", "1e3", 54.9 ou 54.0 ne sont pas des identifiants.

Adresse postaleStatut de contrat non valide
PUT /Import/JSON/contracts/{id}/statusHTTP 400 INVALID_STATUS, error.valid_status_ids indique les identifiants valides ; le contrat reste inchangé
Importation de contrat, status.status_id (également status sans status_id, par exemple "status": 54 ou "status": {})Enregistrement créé, statut et historique des statuts inchangés, avertissement « CONTRACT_STATUS_UNKNOWN » avec field = status.status_id ou status; "status": null est considéré comme non envoyé
Importation de contrat, contracts_status_idchamp non renseigné (il reste vide lors de la création), autres champs renseignés, avertissement « CONTRACT_STATUS_UNKNOWN » avec « field » = contracts_status_id
HTTP 400
{
  "success": false,
  "api_version": "2.122.13",
  "error": {
    "code": "INVALID_STATUS",
    "message": "status_id 1 is not a contract status. Valid: 7, 3, 12 (…)",
    "valid_status_ids": [7, 3, 12]
  }
}

Dans l’importation de contrats, l’enregistrement continue à être compté comme inserted ou updated. L’avertissement figure dans warnings[] de la réponse et dans l’entrée de l’enregistrement dans records[]:

"warnings": [
  {
    "type": "CONTRACT_STATUS_UNKNOWN",
    "field": "status.status_id",
    "entity": "contracts",
    "contracts_id": 42,
    "message": "Contract 42: status.status_id ignored — status.status_id 1 is not a contract status. Valid: 7, 3, 12 (…)",
    "first_occurrence": "record 1"
  }
]
Modifié dans la version v2.122.13 : jusqu’à la version v2.122.12, le sous-point de terminaison /status et l’objet status effectuaient une vérification par rapport aux statuts de commande (orders_status). Un statut de contrat qui n’existait pas également en tant que statut de commande générait un code HTTP 400 au niveau du sous-point de terminaison et était ignoré en silence lors de l’importation ; un identifiant n'existant qu'en tant que statut de commande était enregistré en tant que statut de contrat, au sous-point de terminaison portant le nom du statut de commande dans new_status_name. contracts_status_id était enregistré sans vérification ("abc" était considéré comme 0, true comme 1), et des valeurs telles que "1e3" ou 54.9 étaient considérées respectivement comme 1000 et 54. ⚠️ Si vous envoyez des identifiants de statut de commande en tant que statuts de contrat, vous recevrez désormais une réponse HTTP 400 ou l'avertissement suivant : veuillez utiliser les identifiants disponibles sur contracts_statuses[]. new_status_name indique désormais le nom du statut de contrat.

Suppression (DELETE)

Le contrat, ses attributs et ses domaines, l’historique de son statut ainsi que ses liens vers les factures et les projets sont supprimés. Les factures et les projets eux-mêmes sont conservés.

Pas de blocage en cas de factures en cours : le contrat est également supprimé si des factures y sont associées ou sont encore en cours, comme lors d’une suppression dans le backend. Par la suite, le lien est rompu et un prélèvement via un mandat SEPA ne pourra plus atteindre ces factures. Vérifiez les factures au préalable : celles qui sont associées figurent à l’adresse orders[] de GET /Export/JSON/contract/{id}, chacune avec son statut de commande orders_status. Pour savoir si un statut est considéré comme « payé », consultez is_paid à l’adresse GET /Export/JSON/order_statuses (Commandes, section « Données de base du statut de commande »).
# Ein Vertrag
curl -s -X DELETE "https://shop.de/xpanel/xoport/Delete/JSON/contract/1" \
  -H "Authorization: Bearer $TOKEN"

# Mehrere Verträge
curl -s -X DELETE "https://shop.de/xpanel/xoport/Delete/JSON/contracts" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"data": [{"contracts_id": 1}, {"contracts_id": 2}]}'
  • Un contrat inconnu génère une erreur HTTP 404 lors d’un appel individuel : NOT_FOUND. En traitement par lots, les ID supprimés se trouvent dans deleted_ids, tandis que ceux introuvables apparaissent dans errors[].
  • Un corps de requête sans data[].contracts_id, par exemple {"ids": [1, 2]}, ne supprime rien et renvoie un code HTTP 400 MISSING_DATA.

Portées OAuth2

PortéeAutorisation
contracts:readLire les contrats
contracts:writeCréer et modifier des contrats, y compris leur statut
contracts:deleteSupprimer des contrats