Implementando um webhook no serviço SaaS
Ao criar uma oferta de SaaS transacionável no Partner Center, o parceiro fornece a URL do webhook de conexão para ser usada como um ponto de extremidade HTTP. Este webhook é chamado pela Microsoft usando a chamada HTTP POST para notificar o lado do editor dos seguintes eventos que acontecem no lado da Microsoft:
Evento Webhook | 1. Quando recebido | 2. Se for aceite | 3. Em caso de rejeição |
---|---|---|---|
ChangePlan |
Responder com HTTP 200 | PATCH com sucesso (este evento é opcional e autoaceito em 10 segundos) | PATCH com falha OU responda com 4xx (dentro de 10 segundos) |
ChangeQuantity |
Responder com HTTP 200 | PATCH com sucesso (este evento é opcional e autoaceito em 10 segundos) | PATCH com falha OU responda com 4xx (dentro de 10 segundos) |
Renew |
Responder com HTTP 200 | Não aplicável | Não aplicável |
Suspend |
Responder com HTTP 200 | Não aplicável | Não aplicável |
Unsubscribe |
Responder com HTTP 200 | Não aplicável | Não aplicável |
Reinstate |
Responder com HTTP 200 | Não aplicável | Não aplicável (chamar a API de exclusão para acionar a exclusão se a reintegração não puder ser aceita) |
O editor deve implementar um webhook no serviço SaaS para manter o status da assinatura SaaS consistente com o lado da Microsoft. O serviço SaaS é necessário para chamar a API Get Operation para validar e autorizar a chamada do webhook e os dados de carga antes de executar uma ação com base na notificação do webhook. O editor deve retornar HTTP 200 para a Microsoft assim que a chamada webhook for processada. Esse valor reconhece que a chamada webhook foi recebida com êxito pelo editor.
Importante
O serviço de URL do webhook deve estar funcionando 24 horas por dia, 7 dias por semana, e pronto para receber novas chamadas da Microsoft o tempo todo. A Microsoft tem uma política de repetição para a chamada webhook (500 repetições em oito horas), mas se o editor não aceitar a chamada e retornar uma resposta, a operação sobre a qual o webhook notifica acabará falhando do lado da Microsoft.
Importante
Os ISVs devem evitar a desserialização estrita do esquema Webhook. A Microsoft reserva-se o direito de expandir o esquema no futuro.
Importante
Os ISVs devem validar o Microsoft Entra Token (JWT Token) em seu ponto de extremidade webhook a partir do cabeçalho da solicitação. Este é um token de portador padrão e dará detalhes ISV sobre quem é o chamador. Saiba mais sobre como validar o token neste artigo. learn.microsoft.com/azure/active-directory/develop/access-tokens
Exemplo de carga útil Webhook do 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
}
Exemplo de carga útil Webhook do evento 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
}
Exemplo de carga útil Webhook de um evento de restabelecimento de assinatura:
// 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
}
Exemplo de carga útil Webhook de um evento de renovação:
// 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,
}
Exemplo de carga útil Webhook de um evento suspend:
{
"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,
}
Exemplo de carga útil Webhook de um evento de cancelamento de inscrição:
Este é um evento apenas de notificação. Não há envio para ACK para este evento.
{
"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,
}
Protegendo seus Webhooks
Você deve proteger seus Webhooks para que ninguém além dos endpoints da Microsoft esteja fazendo essas chamadas Webhook. Você pode usar qualquer tecnologia para implementar seus Webhooks, no entanto, sua implementação de Webhook deve seguir as seguintes diretrizes de segurança (Consulte o Tutorial).
A Microsoft chama seus Webhooks com cabeçalhos de Autorização que contêm as informações necessárias para validar as chamadas. Você deve habilitar seus Webhooks para poder receber os cabeçalhos de Autorização. (Não adicione detalhes de autorização ou tokens de segurança, como tokens SAS, diretamente nas URLs do Webhook. Esses Webhooks podem falhar ao recuperar os cabeçalhos de autorização que a Microsoft envia ao chamar seus Webhooks).
O token JWT Bearer passado no cabeçalho Authorization contém os seguintes dados na carga útil que você pode usar para proteger seus endpoints.
"aud": "este é o ID do aplicativo Microsoft Entra Identity que você adiciona à configuração técnica da sua oferta no Microsoft Partner Center"
"appid" ou "azp": este é o ID de recurso que você usa quando cria um token de autorização do editor para chamar APIs de atendimento SaaS. E dependendo da configuração do aplicativo, você pode ver esse valor de ID de recurso em "appid" ou "azp". O token tem qualquer uma das duas declarações e você deve reagir de acordo com seu código.
"tid": "este é o ID de locatário do Microsoft Entra que você adiciona à configuração técnica da sua oferta no Microsoft Partner Center"
Você pode verificar os campos passados acima para se certificar de que a chamada Webhook é válida.
Importante
A Microsoft começará a exigir que os ISVs criem seus Webhooks de maneira segura e aceitem cabeçalhos de autorização. Se sua implementação atual do Webhook não puder aceitar cabeçalhos de Autorização, você deverá atualizar seus Webhooks e proteger esses pontos finais (usando as diretrizes acima) para evitar qualquer interrupção.
Desenvolvimento e teste
Para iniciar o processo de desenvolvimento, recomendamos a criação de respostas de API fictícias no lado do editor. Essas respostas podem ser baseadas em exemplos de respostas fornecidos neste artigo.
Quando o editor estiver pronto para o teste de ponta a ponta:
- Publique uma oferta de SaaS para um público de visualização limitado e mantenha-a no estágio de visualização.
- Defina o preço do plano como zero, para evitar acionar despesas reais de faturamento durante o teste. Outra opção é definir um preço diferente de zero e cancelar todas as compras de teste dentro de 24 horas.
- Certifique-se de que todos os fluxos sejam invocados de ponta a ponta, para simular um cenário real do cliente.
- Se o parceiro quiser testar o fluxo completo de compra e faturamento, faça-o com uma oferta com preço acima de US$ 0. A compra é faturada e será gerada uma fatura.
Um fluxo de compra pode ser acionado a partir do portal do Azure ou dos sites do Microsoft AppSource, dependendo de onde a oferta está sendo publicada.
As ações de alteração de plano, quantidade de alteração e cancelamento de inscrição são testadas do lado do editor. Do lado da Microsoft, o cancelamento de inscrição pode ser acionado no portal do Azure e no Centro de Administração (o portal onde as compras do Microsoft AppSource são gerenciadas). A alteração da quantidade e do plano só pode ser acionada a partir do Centro de Administração.
Obter suporte
Consulte Suporte para o programa de mercado comercial no Partner Center para obter opções de suporte do editor.
Conteúdos relacionados
- Consulte as APIs do serviço de medição do mercado comercial para obter mais opções de ofertas SaaS no mercado comercial.
- Analise e use os clientes para diferentes linguagens de programação e exemplos.
- Assista aos seguintes tutoriais em vídeo: