在 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關於呼叫者是誰的詳細數據。 深入瞭解如何驗證本文中的令牌。 zcusa.951200.xyz/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 購買的入口網站)。 變更數量和方案 只能從系統管理中心觸發。
取得支援
如需發行者支援選項,請參閱 合作夥伴中心 的商業市集計劃支援。
相關內容
- 如需商業市集中 SaaS 供應專案的更多選項,請參閱商業市集計量服務 API。
- 檢閱和使用不同程式設計語言和範例的用戶端。
- 觀看下列 影片教學課程: