Implémentation d’un webhook sur le service SaaS

Lors de la création d’une offre SaaS payante dans Espace partenaires, le partenaire fournit l’URL du webhook de connexion à utiliser comme point de terminaison HTTP. Ce webhook est appelé par Microsoft à l’aide de l’appel POST HTTP pour notifier le côté éditeur des événements suivants qui se produisent côté Microsoft :

Événement webhook 1. Lorsqu’il est reçu 2. S’il est accepté 3. S’il est rejeté
ChangePlan Répondre avec HTTP 200 PATCH avec succès (cet événement est facultatif et autoaccepté en 10 secondes) PATCH avec échec OU répondre avec 4xx (dans les 10 secondes)
ChangeQuantity Répondre avec HTTP 200 PATCH avec succès (cet événement est facultatif et autoaccepté en 10 secondes) PATCH avec échec OU répondre avec 4xx (dans les 10 secondes)
Renew Répondre avec HTTP 200 Non applicable Non applicable
Suspend Répondre avec HTTP 200 Non applicable Non applicable
Unsubscribe Répondre avec HTTP 200 Non applicable Non applicable
Reinstate Répondre avec HTTP 200 Non applicable Non applicable (l’API de suppression d’appel pour déclencher la suppression si la restauration ne peut pas être acceptée)

L’éditeur doit implémenter un webhook dans le service SaaS pour garantir la cohérence de l’état de l’abonnement SaaS avec le côté Microsoft. Le service SaaS doit appeler l’API Obtenir l’opération pour valider et autoriser l’appel du webhook et les données de la charge utile avant de prendre des mesures basées sur la notification de webhook. L’éditeur doit retourner HTTP 200 à Microsoft dès que l’appel de webhook est traité. Cette valeur confirme que l’éditeur a bien reçu l’appel du webhook.

Important

Le service d’URL du webhook doit être opérationnel 24 x 7 et prêt à recevoir de nouveaux appels de Microsoft à tout moment. Microsoft a une stratégie de nouvelle tentative pour l’appel de webhook (500 nouvelles tentatives sur huit heures), mais si l’éditeur n’accepte pas l’appel et retourne une réponse, l’opération sur laquelle le webhook avertit échouera finalement côté Microsoft.

Important

Les éditeurs de logiciels indépendants doivent éviter une désérialisation stricte du schéma webhook. Microsoft se réserve le droit d’étendre le schéma à l’avenir.

Important

Les éditeurs de logiciels indépendants doivent valider le jeton Microsoft Entra (jeton JWT) sur leur point de terminaison webhook à partir de l’en-tête de requête. Il s’agit d’un jeton de porteur standard et donne des détails sur la personne de l’appelant. Découvrez comment valider le jeton dans cet article. learn.microsoft.com/azure/active-directory/develop/access-tokens

Exemple de charge utile Webhook de ChangePlan :

{
    "id": "<guid>",
    "activityId": "<guid>",
    "publisherId": "XXX",
    "offerId": "YYY",
    "planId": "plan2",
    "quantity": 10,
    "subscriptionId": "<guid>",
    "timeStamp": "2023-02-10T18:48:58.4449937Z",
    "action": "ChangePlan",
    "status": "InProgress",
    "operationRequestSource": "Azure",
    "subscription":
    {
      "id": "<guid>",
      "name": "Test",
      "publisherId": "XXX",
      "offerId": "YYY",
      "planId": "plan1",
      "quantity": 10,
      "beneficiary":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "purchaser":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "allowedCustomerOperations": ["Delete", "Update", "Read"],
      "sessionMode": "None",
      "isFreeTrial": false,
      "isTest": false,
      "sandboxType": "None",
      "saasSubscriptionStatus": "Subscribed",
      "term":
        {
          "startDate": "2022-02-10T00:00:00Z",
          "endDate": "2022-03-12T00:00:00Z",
          "termUnit": "P1M",
          "chargeDuration": null,
        },
      "autoRenew": true,
      "created": "2022-01-10T23:15:03.365988Z",
      "lastModified": "2022-02-14T20:26:04.5632549Z",
    },
    "purchaseToken": null
}

Exemple de charge utile Webhook d’événement ChangeQuantity :


{
    "id": "<guid>",
    "activityId": "<guid>",
    "publisherId": "XXX",
    "offerId": "YYY",
    "planId": "plan1",
    "quantity": 20,
    "subscriptionId": "<guid>",
    "timeStamp": "2023-02-10T18:54:00.6158973Z",
    "action": "ChangeQuantity",
    "status": "InProgress",
    "operationRequestSource": "Azure",
    "subscription": {
        "id": "<guid>",
        "name": "Test",
        "publisherId": "XXX",
        "offerId": "YYY",
        "planId": "plan1",
        "quantity": 10,
        "beneficiary":
            {
            "emailId": XX@outlook.com,
            "objectId": "<guid>",
            "tenantId": "<guid>",
            "puid": "1234567890",
            },
        "purchaser":
            {
            "emailId": XX@outlook.com,
            "objectId": "<guid>",
            "tenantId": "<guid>",
            "puid": "1234567890",
            },
        "allowedCustomerOperations": ["Delete", "Update", "Read"],
        "sessionMode": "None",
        "isFreeTrial": false,
        "isTest": false,
        "sandboxType": "None",
        "saasSubscriptionStatus": "Subscribed",
        "term":
            {
            "startDate": "2022-02-10T00:00:00Z",
            "endDate": "2022-03-12T00:00:00Z",
            "termUnit": "P1M",
            "chargeDuration": null,
            },
        "autoRenew": true,
        "created": "2022-01-10T23:15:03.365988Z",
        "lastModified": "2022-02-14T20:26:04.5632549Z",
    },
    "purchaseToken": null
}

Exemple de charge utile webhook d’un événement de rétablissement de l’abonnement :

// end user's payment instrument became valid again, after being suspended, and the SaaS subscription is being reinstated


{
    "id": "<guid>",
    "activityId": "<guid>",
    "publisherId": "XXX",
    "offerId": "YYY",
    "planId": "plan1",
    "quantity": 100,
    "subscriptionId": "<guid>",
    "timeStamp": "2023-02-11T11:38:10.3508619Z",
    "action": "Reinstate",
    "status": "InProgress",
    "operationRequestSource": "Azure",
    "subscription":
    {
      "id": "<guid>",
      "name": "Test",
      "publisherId": "XXX",
      "offerId": "YYY",
      "planId": "plan1",
      "quantity": 100,
      "beneficiary":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "purchaser":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "allowedCustomerOperations": ["Delete", "Update", "Read"],
      "sessionMode": "None",
      "isFreeTrial": false,
      "isTest": false,
      "sandboxType": "None",
      "saasSubscriptionStatus": "Suspended",
      "term":
        {
          "startDate": "2022-02-10T00:00:00Z",
          "endDate": "2022-03-12T00:00:00Z",
          "termUnit": "P1M",
          "chargeDuration": null,
        },
      "autoRenew": true,
      "created": "2022-01-10T23:15:03.365988Z",
      "lastModified": "2022-02-14T20:26:04.5632549Z",
    },
    "purchaseToken": null
}
 

Exemple de charge utile webhook d’un événement de renouvellement :

// end user's subscription renewal
 
{
    "id": "<guid>",
    "activityId": "<guid>",
    "publisherId": "XXX",
    "offerId": "YYY",
    "planId": "plan1",
    "quantity": 100,
    "subscriptionId": "<guid>",
    "timeStamp": "2023-02-10T08:49:01.8613208Z",
    "action": "Renew",
    "status": "Succeeded",
    "operationRequestSource": "Azure",
    "subscription":
    {
      "id": "<guid>",
      "name": "Test",
      "publisherId": "XXX",
      "offerId": "YYY",
      "planId": "plan1",
      "quantity": 100,
      "beneficiary":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "purchaser":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "allowedCustomerOperations": ["Delete", "Update", "Read"],
      "sessionMode": "None",
      "isFreeTrial": false,
      "isTest": false,
      "sandboxType": "None",
      "saasSubscriptionStatus": "Subscribed",
      "term":
        {
          "startDate": "2022-02-10T00:00:00Z",
          "endDate": "2022-03-12T00:00:00Z",
          "termUnit": "P1M",
          "chargeDuration": null,
        },
      "autoRenew": true,
      "created": "2022-01-10T23:15:03.365988Z",
      "lastModified": "2022-02-14T20:26:04.5632549Z",
    },
  "purchaseToken": null,
}

Exemple de charge utile webhook d’un événement de suspension :


{
    "id": "<guid>",
    "activityId": "<guid>",
    "publisherId": "XXX",
    "offerId": "YYY",
    "planId": "plan1",
    "quantity": 100,
    "subscriptionId": "<guid>",
    "timeStamp": "2023-02-10T08:49:01.8613208Z",
    "action": "Suspend",
    "status": "Succeeded",
    "operationRequestSource": "Azure",
    "subscription":
    {
      "id": "<guid>",
      "name": "Test",
      "publisherId": "XXX",
      "offerId": "YYY",
      "planId": "plan1",
      "quantity": 100,
      "beneficiary":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "purchaser":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "allowedCustomerOperations": ["Delete", "Update", "Read"],
      "sessionMode": "None",
      "isFreeTrial": false,
      "isTest": false,
      "sandboxType": "None",
      "saasSubscriptionStatus": "Suspended",
      "term":
        {
          "startDate": "2022-02-10T00:00:00Z",
          "endDate": "2022-03-12T00:00:00Z",
          "termUnit": "P1M",
          "chargeDuration": null,
        },
      "autoRenew": true,
      "created": "2022-01-10T23:15:03.365988Z",
      "lastModified": "2022-02-14T20:26:04.5632549Z",
    },
  "purchaseToken": null,
}

Exemple de charge utile webhook d’un événement de désabonnement :

Il s’agit d’un événement de notification uniquement. Il n’y a pas d’envoi à ACK pour cet événement.


{
    "id": "<guid>",
    "activityId": "<guid>",
    "publisherId": "XXX",
    "offerId": "YYY",
    "planId": "plan1",
    "quantity": 100,
    "subscriptionId": "<guid>",
    "timeStamp": "2023-02-10T08:49:01.8613208Z",
    "action": "Unsubscribe",
    "status": "Succeeded",
    "operationRequestSource": "Azure",
    "subscription":
    {
      "id": "<guid>",
      "name": "Test",
      "publisherId": "XXX",
      "offerId": "YYY",
      "planId": "plan1",
      "quantity": 100,
      "beneficiary":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "purchaser":
        {
          "emailId": XX@outlook.com,
          "objectId": "<guid>",
          "tenantId": "<guid>",
          "puid": "1234567890",
        },
      "allowedCustomerOperations": ["Delete", "Update", "Read"],
      "sessionMode": "None",
      "isFreeTrial": false,
      "isTest": false,
      "sandboxType": "None",
      "saasSubscriptionStatus": "Unsubscribed",
      "term":
        {
          "startDate": "2022-02-10T00:00:00Z",
          "endDate": "2022-03-12T00:00:00Z",
          "termUnit": "P1M",
          "chargeDuration": null,
        },
      "autoRenew": true,
      "created": "2022-01-10T23:15:03.365988Z",
      "lastModified": "2022-02-14T20:26:04.5632549Z",
    },
  "purchaseToken": null,
}

Sécurisation de vos webhooks

Vous devez sécuriser vos Webhooks afin que personne d’autre que les points de terminaison Microsoft n’effectue de tels appels webhook. Vous pouvez utiliser n’importe quelle technologie pour implémenter vos Webhooks, mais votre implémentation de Webhook doit suivre les instructions de sécurité suivantes.

  • Microsoft appelle vos Webhooks avec des en-têtes d’autorisation qui contiennent les informations nécessaires pour valider les appels. Vous devez autoriser vos Webhooks à recevoir les en-têtes d’autorisation. (N’ajoutez pas de détails d’autorisation ou de jetons de sécurité tels que des jetons SAP directement dans les URL webhook. Ces Webhooks peuvent ne pas récupérer les en-têtes d’autorisation que Microsoft envoie lors de l’appel de vos Webhooks).

  • Le jeton du porteur JWT transmis dans l’en-tête d’autorisation contient les données suivantes dans la charge utile que vous pouvez utiliser pour sécuriser vos points de terminaison.

  • « aud » : « il s’agit de l’ID d’application Microsoft Entra Identity que vous ajoutez à la configuration technique de votre offre dans l’Espace partenaires Microsoft »

  • « appid » ou « azp » : il s’agit de l’ID de ressource que vous utilisez lorsque vous créez un jeton d’autorisation d’éditeur pour appeler les API de traitement SaaS. En fonction de la configuration de l’application, vous pouvez voir cette valeur d’ID de ressource dans « appid » ou « azp ». Le jeton a l’une des deux revendications et vous devez réagir en conséquence dans votre code.

  • « tid » : « il s’agit de l’ID de locataire Microsoft Entra que vous ajoutez à la configuration technique de votre offre dans l’Espace partenaires Microsoft »

  • Vous pouvez case activée par rapport aux champs passés ci-dessus pour vous assurer que l’appel webhook est valide.

Important

Microsoft commence à exiger que les éditeurs de logiciels indépendants créent leurs webhooks de manière sécurisée et acceptent les en-têtes d’autorisation. Si votre implémentation de Webhook actuelle ne peut pas accepter d’en-têtes d’autorisation, vous devez mettre à jour vos Webhooks et sécuriser ces points de terminaison (à l’aide des instructions ci-dessus) pour éviter toute interruption.

Développement et test

Pour démarrer le processus de développement, nous vous recommandons de créer des réponses d’API factices côté éditeur. Ces réponses peuvent reposer sur des exemples de réponses fournis dans cet article.

Lorsque l’éditeur est prêt pour le test de bout en bout :

  • Publiez une offre SaaS sur une audience en préversion limitée et conservez-la en phase de préversion.
  • Définissez le prix du plan sur zéro pour éviter de déclencher les dépenses de facturation réelles lors du test. Une autre option consiste à définir un prix différent de zéro et à annuler tous les achats de test dans les 24 heures.
  • Veillez à ce que tous les flux soient appelés de bout en bout, afin de simuler un scénario client réel.
  • Si le partenaire souhaite tester le flux complet d’achat et de facturation, utilisez une offre dont le prix est supérieur à $0. L’achat est facturé et une facture sera générée.

Un flux d’achat peut être déclenché à partir du portail Azure ou de Microsoft AppSource, selon l’endroit où l’offre est publiée.

Les actions de modification du plan, modification de quantité et de désabonnement sont testées côté éditeur. Côté Microsoft, l’opération de désabonnement peut être déclenchée à partir du portail Azure ou du Centre d’administration (le portail où les achats Microsoft AppSource sont gérés). Les opérations de modification de quantité et de plan peuvent être déclenchées uniquement à partir du Centre d’administration.

Obtenir du support

Pour afficher les options de support pour les éditeurs, consultez Support technique pour le programme commercial de la Place de marché dans l’Espace partenaires.

Étapes suivantes