Share 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 Webhook 1. Quando recebido 2. Se aceito 3. Se rejeitado
ChangePlan Responder com HTTP 200 PATCH com sucesso (este evento é opcional e aceito automaticamente em 10 segundos) PATCH com falha OU responder com 4xx (dentro de 10 segundos)
ChangeQuantity Responder com HTTP 200 PATCH com sucesso (este evento é opcional e aceito automaticamente em 10 segundos) PATCH com falha OU responder 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 (chame a API de exclusão para acionar a exclusão se a restauraçã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 de webhook deve estar funcionando 24 horas por dia, 7 dias por semana e pronto para receber novas chamadas da Microsoft em todos os momentos. A Microsoft tem uma política de repetição para a chamada webhook (500 tentativas ao longo de 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 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 de webhook a partir do cabeçalho da solicitação. Este é um token de portador padrão e fornecerá 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 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 carga útil do 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 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 do Webhook de um evento de cancelamento de inscrição:

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 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.

  • A Microsoft chama seus Webhooks com cabeçalhos de autorização que contém 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 pontos de extremidade.

  • "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 preenchimento 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 em 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 certificar-se 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 de extremidade (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 faturamento durante o teste. Outra opção é definir um preço diferente de zero e cancelar todas as compras de teste em até 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 é cobrada 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.

Obter suporte

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

Próximas etapas