Teilen über


Implementieren eines Webhooks für den SaaS-Dienst

Beim Erstellen eines transaktionsfähigen SaaS-Angebots im Partner Center stellt der Partner die Connection-Webhook- URL bereit, die als HTTP-Endpunkt verwendet werden soll. Dieser Webhook wird von Microsoft aufgerufen, indem der POST-HTTP-Aufruf verwendet wird, um die Herausgeberseite über die folgenden Ereignisse zu benachrichtigen, die auf der Microsoft-Seite auftreten:

Webhook-Ereignis 1. Wann empfangen 2. Wenn akzeptiert 3. Wenn abgelehnt
ChangePlan Antworten mit HTTP 200 PATCH mit Erfolg (dieses Ereignis ist optional und automatisch in 10 Sek.) PATCH mit Fehler ODER reagiert mit 4xx (innerhalb von 10 Sekunden)
ChangeQuantity Antworten mit HTTP 200 PATCH mit Erfolg (dieses Ereignis ist optional und automatisch in 10 Sek.) PATCH mit Fehler ODER reagiert mit 4xx (innerhalb von 10 Sekunden)
Renew Antworten mit HTTP 200 Nicht zutreffend Nicht zutreffend
Suspend Antworten mit HTTP 200 Nicht zutreffend Nicht zutreffend
Unsubscribe Antworten mit HTTP 200 Nicht zutreffend Nicht zutreffend
Reinstate Antworten mit HTTP 200 Nicht zutreffend Nicht zutreffend (Aufruflösch-API zum Auslösen des Löschens, wenn die Wiederaufnahme nicht akzeptiert werden kann)

Der Herausgeber muss einen Webhook im SaaS-Dienst implementieren, um den SaaS-Abonnementstatus mit der Microsoft-Seite konsistent zu halten. Der SaaS-Dienst ist erforderlich, um die Get Operation-API aufzurufen, um die Webhook-Aufruf- und Nutzlastdaten zu überprüfen und zu autorisieren, bevor Sie aktionen basierend auf der Webhook-Benachrichtigung ausführen. Der Herausgeber sollte HTTP 200 an Microsoft zurückgeben, sobald der Webhook-Aufruf verarbeitet wird. Dieser Wert erkennt an, dass der Webhook-Aufruf vom Herausgeber erfolgreich empfangen wurde.

Wichtig

Der Webhook-URL-Dienst muss 24 x 7 ausgeführt werden und kann jederzeit neue Anrufe von Microsoft empfangen. Microsoft verfügt über eine Wiederholungsrichtlinie für den Webhook-Aufruf (500 Wiederholungsversuche über acht Stunden), aber wenn der Herausgeber den Anruf nicht akzeptiert und eine Antwort zurückgibt, schlägt der Vorgang, über den Webhook benachrichtigt wird, schließlich fehl auf der Microsoft-Seite.

Wichtig

ISVs sollten eine strikte Deserialisierung des Webhook-Schemas vermeiden. Microsoft behält sich das Recht vor, das Schema zukünftig zu erweitern.

Wichtig

ISVs müssen das Microsoft Entra-Token (JWT-Token) auf ihrem Webhook-Endpunkt aus dem Anforderungsheader überprüfen. Dies ist ein Standard-Bearertoken und gibt ISV-Details darüber, wer der Anrufer ist. Erfahren Sie mehr darüber, wie Sie das Token in diesem Artikel überprüfen. learn.microsoft.com/azure/active-directory/develop/access-tokens

Webhook-Nutzlastbeispiel für 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
}

Webhook-Nutzlastbeispiel für das ChangeQuantity-Ereignis:


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

Webhook-Nutzlastbeispiel für ein Ereignis zum Erneuten Abrufen eines Abonnements:

// 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
}
 

Webhook-Nutzlastbeispiel für ein Verlängerungsereignis:

// 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,
}

Webhook-Nutzlastbeispiel für ein Anhalteereignis:


{
    "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,
}

Webhook-Nutzlastbeispiel für ein Abonnementereignis:

Dies ist ein Nur-Benachrichtigungsereignis. Für dieses Ereignis ist kein Senden an ACK vorhanden.


{
    "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,
}

Schützen von Webhooks

Sie müssen Ihre Webhooks schützen, damit niemand anderes als Microsoft-Endpunkte solche Webhook-Aufrufe tätigen. Sie können jede Technologie verwenden, um Ihre Webhooks zu implementieren, ihre Webhook-Implementierung muss jedoch den folgenden Sicherheitsrichtlinien entsprechen (Siehe Lernprogramm).

  • Microsoft ruft Ihre Webhooks mit Autorisierungsheadern auf, die erforderliche Informationen zum Überprüfen der Aufrufe enthalten. Sie müssen Ihre Webhooks aktivieren, um die Autorisierungsheader empfangen zu können. (Fügen Sie keine Autorisierungsdetails oder Sicherheitstoken wie SAS-Token direkt in den Webhook-URLs hinzu. Solche Webhooks können die Autorisierungsheader, die Microsoft beim Aufrufen Ihrer Webhooks sendet, nicht abrufen.

  • Das im Autorisierungsheader übergebene JWT Bearer-Token enthält die folgenden Daten in der Nutzlast, die Sie zum Sichern Ihrer Endpunkte verwenden können.

  • "aud": "Dies ist die Microsoft Entra Identity-Anwendungs-ID, die Sie zur technischen Konfiguration Ihres Angebots im Microsoft Partner Center hinzufügen"

  • "appid" oder "azp": Dies ist die Ressourcen-ID, die Sie verwenden, wenn Sie Publisher-Autorisierungstoken zum Aufrufen von SaaS-Erfüllungs-APIs erstellen. Je nach Anwendungssetup wird dieser Ressourcen-ID-Wert möglicherweise entweder in "appid" oder "azp" angezeigt. Das Token verfügt über einen der beiden Ansprüche, und Sie müssen in Ihrem Code entsprechend reagieren.

  • "tid": "Dies ist die Microsoft Entra-Mandanten-ID, die Sie der technischen Konfiguration Ihres Angebots im Microsoft Partner Center hinzufügen"

  • Sie können die oben übergebenen Felder überprüfen, um sicherzustellen, dass der Webhook-Aufruf gültig ist.

Wichtig

Microsoft erfordert, dass ISVs ihre Webhooks auf sichere Weise erstellen und Autorisierungsheader akzeptieren. Wenn Ihre aktuelle Webhook-Implementierung keine Autorisierungsheader akzeptieren kann, müssen Sie Ihre Webhooks aktualisieren und solche Endpunkte (unter Verwendung der oben genannten Richtlinien) schützen, um Unterbrechungen zu vermeiden.

Entwicklung und Tests

Um den Entwicklungsprozess zu starten, empfehlen wir das Erstellen von Dummy-API-Antworten auf der Herausgeberseite. Diese Antworten können auf Beispielantworten basieren, die in diesem Artikel bereitgestellt werden.

Wenn der Herausgeber für das Ende des Tests bereit ist:

  • Veröffentlichen Sie ein SaaS-Angebot für eine begrenzte Vorschaugruppe, und halten Sie es in der Vorschauphase.
  • Legen Sie den Planpreis auf Null fest, um zu vermeiden, dass die tatsächlichen Abrechnungsausgaben beim Testen ausgelöst werden. Eine weitere Möglichkeit besteht darin, einen Nonzero-Preis festzulegen und alle Testkäufe innerhalb von 24 Stunden zu stornieren.
  • Stellen Sie sicher, dass alle Flüsse endend aufgerufen werden, um ein echtes Kundenszenario zu simulieren.
  • Wenn der Partner den vollständigen Kauf- und Abrechnungsfluss testen möchte, führen Sie dies mit dem Angebot aus, das über 0 $ liegt. Der Kauf wird in Rechnung gestellt, und eine Rechnung wird generiert.

Je nachdem, wo das Angebot veröffentlicht wird, kann ein Kauffluss aus dem Azure-Portal oder microsoft AppSource-Websites ausgelöst werden.

Änderungsplan, Mengeändern, und Kündigen Aktionen werden von der Herausgeberseite getestet. Auf der Microsoft-Seite können Abonnement sowohl über das Azure-Portal als auch über das Admin Center (das Portal, in dem Microsoft AppSource-Einkäufe verwaltet werden) ausgelöst werden. Ändern der Menge und des Plans nur über Das Admin Center ausgelöst werden kann.

Support erhalten

Weitere Informationen finden Sie unter Support für das kommerzielle Marketplace-Programm im Partner Center für Publisher-Supportoptionen.