共用方式為


在 SaaS 服務上實作 Webhook

在合作夥伴中心建立可交易的 SaaS 供應專案時,合作夥伴會提供 要作為 HTTP 端點的連線 Webhook URL。 Microsoft會呼叫此 Webhook,方法是使用 POST HTTP 呼叫來通知發行者端下列Microsoft端發生的事件:

Webhook 事件 1.收到時 2.如果已接受 3.如果遭到拒絕
ChangePlan 使用 HTTP 200 回應 成功修補 (此事件為選擇性,並在 10 秒內自動接受) 失敗或回應 4xx 的 PATCH (在 10 秒內)
ChangeQuantity 使用 HTTP 200 回應 成功修補 (此事件為選擇性,並在 10 秒內自動接受) 失敗或回應 4xx 的 PATCH (在 10 秒內)
Renew 使用 HTTP 200 回應 不適用 不適用
Suspend 使用 HTTP 200 回應 不適用 不適用
Unsubscribe 使用 HTTP 200 回應 不適用 不適用
Reinstate 使用 HTTP 200 回應 不適用 不適用(如果無法接受恢復,請呼叫刪除 API 以觸發刪除)

發行者必須在 SaaS 服務中實作 Webhook,才能讓 SaaS 訂用帳戶狀態與Microsoft端保持一致。 需要 SaaS 服務才能呼叫取得作業 API,以驗證和授權 Webhook 呼叫和承載數據,再根據 Webhook 通知採取動作。 發行者應該在處理 Webhook 呼叫后,立即將 HTTP 200 傳回至Microsoft。 這個值會確認發行者已成功收到 Webhook 呼叫。

重要

Webhook URL 服務必須啟動並執行 24 x 7,並準備好隨時接收來自Microsoft的新呼叫。 Microsoft有 Webhook 呼叫的重試原則 (500 次重試超過 8 小時),但如果發行者不接受呼叫並傳回回應,則 Webhook 所通知的作業最終會在Microsoft端失敗。

重要

ISV 應避免對 Webhook 架構進行嚴格的還原串行化。 Microsoft保留未來擴充架構的權利。

重要

ISV 必須從要求標頭驗證其 Webhook 端點上的Microsoft Entra Token (JWT Token)。 這是標準持有人令牌,並提供ISV關於呼叫者是誰的詳細數據。 深入瞭解如何驗證本文中的令牌。 learn.microsoft.com/azure/active-directory/develop/access-tokens

ChangePlan 的 Webhook 承載範例:

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

ChangeQuantity 事件的 Webhook 承載範例:


{
    "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 承載範例:

// 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 承載範例:

// 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 承載範例:


{
    "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 承載範例:

這是僅限通知的事件。 此事件沒有傳送至 ACK。


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

保護您的 Webhook

您必須保護您的 Webhook,因此除了Microsoft端點之外,沒有人進行這類 Webhook 呼叫。 您可以使用任何技術來實作 Webhook,不過您的 Webhook 實作必須遵循下列安全性指導方針(請參閱教學課程)。

  • Microsoft使用授權標頭呼叫 Webhook,其中包含驗證呼叫的必要資訊。 您必須讓 Webhook 能夠接收授權標頭。 (請勿在 Webhook URL 中直接新增授權詳細數據或安全性令牌,例如 SAS 令牌。這類 Webhook 可能無法擷取在呼叫 Webhook 時Microsoft傳送的授權標頭。

  • 在 Authorization 標頭中傳遞的 JWT 持有人令牌包含下列數據,您可以在承載中用來保護您的端點。

  • “aud”: “這是您在 Microsoft 合作夥伴中心新增至供應專案技術設定Microsoft Entra Identity 應用程式標識符”

  • “appid” 或 “azp”:這是您在建立發行者授權令牌來呼叫 SaaS 履行 API 時所使用的資源識別符。 視應用程式設定而定,您可能會在 「appid」 或 「azp」 中看到此資源識別碼值。 令牌有兩個宣告的其中一個,您必須在程式代碼中做出相應的反應。

  • “tid”: “這是您在 Microsoft 合作夥伴中心新增至供應專案技術設定Microsoft Entra 租使用者標識符”

  • 您可以檢查上述傳遞的欄位,以確定 Webhook 呼叫有效。

重要

Microsoft會開始要求ISV以安全的方式建立其Webhook,並接受授權標頭。 如果您目前的 Webhook 實作不接受授權標頭,則必須更新 Webhook 並保護這類端點(使用上述指導方針),以避免任何中斷。

開發與測試

若要開始開發程式,建議您在發行者端建立虛擬 API 回應。 這些回應可以根據本文中提供的範例回應。

當發行者準備好進行端對端測試時:

  • 將 SaaS 供應專案發佈至有限的預覽物件,並將其保留在預覽階段。
  • 將方案價格設定為零,以避免在測試時觸發實際計費費用。 另一個選項是設定非零價格,並在24小時內取消所有測試購買。
  • 請確定所有流程都是端對端叫用,以模擬實際的客戶案例。
  • 如果合作夥伴想要測試完整的購買和計費流程,請使用價格高於 $0 的供應專案來執行此動作。 購買會計費,並會產生發票。

視發行供應專案的位置而定,可以從 Azure 入口網站 或Microsoft AppSource 網站觸發購買流程。

變更計劃變更數量取消訂閱 動作會從發行者端進行測試。 從Microsoft端,可以從 Azure 入口網站 和系統管理中心觸發取消訂閱(管理Microsoft AppSource 購買的入口網站)。 變更數量和方案 只能從系統管理中心觸發。

取得支援

如需發行者支援選項,請參閱 合作夥伴中心 的商業市集計劃支援。

下一步