SaaS サービスでの Webhook の実装
パートナー センターで取引可能な SaaS オファーを作成する場合、パートナーは HTTP エンドポイントとして使用する Connection webhook URL を提供します。 この Webhook は、POST HTTP 呼び出しを使用して Microsoft 側で発生する次のイベントをパブリッシャー側に通知することで、Microsoft によって呼び出されます。
Webhook イベント | 1. 受信した場合 | 2. 承諾された場合 | 3. 拒否された場合 |
---|---|---|---|
ChangePlan |
HTTP 200 で応答する | 成功した PATCH (このイベントは省略可能で、10 秒で自動受け取り) | PATCH with failure OR respond with 4xx (within 10 seconds) (PATCH with failure OR respond with 4xx (10 秒以内) |
ChangeQuantity |
HTTP 200 で応答する | 成功した PATCH (このイベントは省略可能で、10 秒で自動受け取り) | PATCH with failure OR respond with 4xx (within 10 seconds) (PATCH with failure OR respond with 4xx (10 秒以内) |
Renew |
HTTP 200 で応答する | 該当なし | 該当なし |
Suspend |
HTTP 200 で応答する | 該当なし | 該当なし |
Unsubscribe |
HTTP 200 で応答する | 該当なし | 該当なし |
Reinstate |
HTTP 200 で応答する | 該当なし | 適用できません (再開を受け入れることができない場合に削除をトリガーする delete API を呼び出す) |
発行元は、SaaS サブスクリプションの状態を Microsoft 側と一致させるために、SaaS サービスに Webhook を実装する必要があります。 SaaS サービスは、Webhook 通知に基づいてアクションを実行する前に、Get Operation API を呼び出して Webhook 呼び出しとペイロード データを検証および承認する必要があります。 発行元は、Webhook 呼び出しが処理されるとすぐに HTTP 200 を Microsoft に返す必要があります。 この値は、Webhook 呼び出しがパブリッシャーによって正常に受信されたことを確認します。
大事な
Webhook URL サービスは、24 x 7 で稼働しており、常に Microsoft から新しい呼び出しを受け取る準備ができている必要があります。 Microsoft には Webhook 呼び出しの再試行ポリシーがあります (8 時間にわたって 500 回の再試行)、発行元が呼び出しを受け入れて応答を返さない場合、Webhook が通知する操作は最終的に Microsoft 側で失敗します。
大事な
ISV では、Webhook スキーマの厳密な逆シリアル化を回避する必要があります。 Microsoft は、将来スキーマを拡張する権利を留保します。
大事な
ISV は、要求ヘッダーから Webhook エンドポイント上の Microsoft Entra Token (JWT トークン) を検証する必要があります。 これは標準的なベアラー トークンであり、呼び出し元のユーザーに関する 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 のセキュリティ保護
Microsoft エンドポイント以外のユーザーがこのような Webhook 呼び出しを行っていないように、Webhook をセキュリティで保護する必要があります。 任意のテクノロジを使用して Webhook を実装できますが、Webhook の実装は次のセキュリティ ガイドラインに従う必要があります (チュートリアルを参照してください)。
Microsoft は、呼び出しを検証するために必要な情報を含む Authorization ヘッダーを使用して Webhook を呼び出します。 承認ヘッダーを受信できるようにするには、Webhook を有効にする必要があります。 (Webhook URL に直接、承認の詳細や SAS トークンなどのセキュリティ トークンを追加しないでください。このような Webhook は、Webhook を呼び出すときに Microsoft が送信する Authorization ヘッダーの取得に失敗する可能性があります)。
Authorization ヘッダーで渡される JWT Bearer トークンには、エンドポイントをセキュリティで保護するために使用できるペイロードに次のデータが含まれています。
"aud": "これは、Microsoft パートナー センターでオファーの技術構成に追加する Microsoft Entra Identity アプリケーション ID です"
"appid" または "azp": これは、SaaS フルフィルメント API を呼び出す発行元承認トークンを作成するときに使用するリソース ID です。 また、アプリケーションのセットアップによっては、"appid" または "azp" のいずれかにこのリソース ID 値が表示される場合があります。 トークンには 2 つの要求のいずれかが含まれており、コード内でそれに応じて対応する必要があります。
"tid": "これは、Microsoft パートナー センターでオファーの技術構成に追加する Microsoft Entra テナント ID です"
上記の渡されたフィールドに対して確認して、Webhook 呼び出しが有効であることを確認できます。
大事な
Microsoft では、セキュリティで保護された方法で Webhook を作成し、承認ヘッダーを受け入れるように ISV に要求を開始します。 現在の Webhook 実装で承認ヘッダーを受け入れることができない場合は、中断を回避するために、Webhook を更新し、(上記のガイドラインを使用して) そのようなエンドポイントをセキュリティで保護する必要があります。
開発とテスト
開発プロセスを開始するには、パブリッシャー側でダミー API 応答を作成することをお勧めします。 これらの応答は、この記事で提供されるサンプル応答に基づくことができます。
パブリッシャーがエンド ツー エンドテストの準備ができたら:
- SaaS オファーを限定されたプレビュー対象ユーザーに発行し、プレビュー 段階に維持します。
- テスト中に実際の請求費用が発生しないように、プラン価格を 0 に設定します。 もう 1 つのオプションは、0 以外の価格を設定し、24 時間以内にすべてのテスト購入をキャンセルすることです。
- 実際の顧客シナリオをシミュレートするために、すべてのフローがエンド ツー エンドで呼び出されるようにします。
- パートナーが完全な購入と課金フローをテストする場合は、$0 を超える価格のオファーでテストします。 購入が課金され、請求書が生成されます。
オファーの発行場所に応じて、Azure portal または Microsoft AppSource サイトから購入フローをトリガーできます。
プランの変更、数量の変更、および サブスクリプション解除 アクションはパブリッシャー側からテストされます。 Microsoft 側から、サブスクリプション解除 は、Azure portal と管理センター (Microsoft AppSource の購入が管理されているポータル) の両方からトリガーできます。 数量とプランの の変更は、管理センターからのみトリガーできます。
サポートを受ける
発行元のサポート オプションについては、パートナー センター のコマーシャル マーケットプレース プログラムの サポートを参照してください。
関連コンテンツ
- コマーシャル マーケットプレースでの SaaS オファーのその他のオプションについては、コマーシャル マーケットプレース使用状況測定サービス API を参照してください。
- のさまざまなプログラミング言語とサンプルのクライアントを確認して使用します。
- 次の ビデオ チュートリアルのをご覧ください。
- SaaS Webhook の概要
- .NET での単純な SaaS Webhook の実装
- Microsoft Entra アプリケーションの登録