Compartilhar via


Implementação de webhook no serviço de SaaS

Ao criar uma oferta de SaaS comercializável no Partner Center, o parceiro informa a URL do Webhook de conexão a ser usada como um ponto de extremidade de HTTP. A Microsoft chama esse webhook por meio da chamada POST de HTTP para comunicar ao lado do distribuidor a ocorrência dos seguintes eventos no lado da Microsoft:

Evento de webhook 1. Quando recebido 2. Se aceito 3. Se rejeitado
ChangePlan Responda com HTTP 200 PATCH com sucesso (este evento é opcional e aceito automaticamente em 10 segundos) PATCH com falha OU responda com 4xx (dentro de 10 segundos)
ChangeQuantity Responda com HTTP 200 PATCH com sucesso (este evento é opcional e aceito automaticamente em 10 segundos) PATCH com falha OU responda com 4xx (dentro de 10 segundos)
Renew Responda com HTTP 200 Não aplicável Não aplicável
Suspend Responda com HTTP 200 Não aplicável Não aplicável
Unsubscribe Responda com HTTP 200 Não aplicável Não aplicável
Reinstate Responda com HTTP 200 Não aplicável Não aplicável (chamar a API de exclusão para disparar a exclusão se a reintegração não puder ser aceita)

O distribuidor deve implementar um webhook no serviço de SaaS para manter o status da assinatura de SaaS consistente com o lado da Microsoft. O serviço de SaaS é necessário para chamar a API Operação Get para validar e autorizar a chamada ao webhook e os dados do conteúdo antes de tomar medidas com base na notificação do webhook. O distribuidor deve retornar HTTP 200 à Microsoft assim que a chamada de webhook for processada. Esse valor confirma que o distribuidor recebeu a chamada ao webhook.

Importante

O serviço de URL do webhook deve estar em execução 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 de webhook (500 tentativas 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 no lado da Microsoft.

Importante

Os ISVs devem evitar a desserialização estrita do esquema do Webhook. A Microsoft reserva-se o direito de expandir o esquema no futuro.

Importante

Os ISVs devem validar o Token do Microsoft Entra (Token JWT) em seu ponto de extremidade de webhook do cabeçalho da solicitação. Esse é um token de portador padrão e fornecerá detalhes do 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 conteúdo de webhook de 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 conteúdo de webhook de 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 payload de 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 do 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 do webhook de um evento de suspensão:


{
    "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 payload de webhook de um evento de cancelamento de assinatura:

Este é um evento somente 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 pontos de extremidade da Microsoft esteja fazendo essas chamadas de 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 de portador JWT passado no cabeçalho Authorization contém os seguintes dados na carga que você pode usar para proteger seus endpoints.

  • "aud": "esta é a ID do aplicativo Microsoft Entra Identity que você adiciona à configuração técnica da sua oferta no Microsoft Partner Center"

  • "appid" ou "azp": esse é o ID do recurso que você usa ao criar o token de autorização do editor para chamar APIs de atendimento de SaaS. E, dependendo da configuração do aplicativo, você pode ver esse valor de ID de recurso em "appid" ou "azp". O token tem uma das duas declarações e você deve reagir de acordo com seu código.

  • "tid": "esta é a 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 garantir que a chamada do Webhook seja 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 a implementação atual do Webhook não puder aceitar cabeçalhos de autorização, você deverá atualizar seus Webhooks e proteger esses endpoints (usando as diretrizes acima) para evitar qualquer interrupção.

Desenvolvimento e teste

Para iniciar o processo de desenvolvimento, recomendamos criar respostas de API fictícias no lado do distribuidor. As respostas podem ser baseadas nos exemplos contidos neste artigo.

Quando o distribuidor está pronto para o teste de ponta a ponta:

  • Publique uma oferta de SaaS para um público-alvo de versão prévia limitado e mantenha-a na fase de versão prévia.
  • Defina o preço do plano como zero, para evitar o acionamento de despesas reais de cobrança durante o teste. Outra opção é definir um preço diferente de zero e cancelar todas as compras de teste em 24 horas.
  • Chame todos os fluxos de ponta a ponta, para simular um cenário de cliente real.
  • Se o parceiro quiser testar o fluxo completo de compra e cobrança, faça isso com uma oferta com preço acima de US$ 0. A compra é faturada e uma fatura será gerada.

É possível iniciar o fluxo de compra no portal do Azure ou nos sites do Microsoft AppSource, dependendo da publicação da oferta.

As ações alterar plano, alterar quantidade e cancelar assinatura são testadas no lado do distribuidor. Do lado da Microsoft, cancelar assinatura pode ser iniciado no portal do Azure ou no Centro de Administração (o portal em que as compras do Microsoft AppSource são gerenciadas). Alterar a quantidade e o plano só pode ser iniciado no Centro de Administração.

Obtenha suporte

Veja as opções de suporte ao distribuidor em Suporte para o programa do marketplace comercial no Partner Center.