Compartilhar via


Implementando um webhook no serviço SaaS

Ao criar uma oferta de SaaS transatlável no Partner Center, o parceiro fornece o webhook de conexão URL a ser usada como um ponto de extremidade HTTP. Esse webhook é chamado pela Microsoft usando a chamada HTTP POST para notificar o lado do editor dos seguintes eventos que ocorrem no lado da Microsoft:

Evento webhook 1. Quando recebido 2. Se aceito 3. Se rejeitado
ChangePlan Responder com HTTP 200 PATCH com êxito (esse evento é opcional e autoaceitado em 10 segundos) PATCH com falha OU responder com 4xx (dentro de 10 segundos)
ChangeQuantity Responder com HTTP 200 PATCH com êxito (esse evento é opcional e autoaceitado 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 (a API de exclusão de chamada para disparar 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 os dados de chamada e conteúdo do webhook antes de tomar medidas com base na notificação do webhook. O editor deve retornar HTTP 200 para a Microsoft assim que a chamada de webhook for processada. Esse valor reconhece que a chamada de webhook foi recebida com êxito pelo editor.

Importante

O serviço de URL do webhook deve estar funcionando 24 x 7 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 repetições 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 do Webhook. A Microsoft se reserva o direito de expandir o esquema no futuro.

Importante

Os ISVs devem validar o Token do Microsoft Entra (Token JWT) no ponto de extremidade do 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 do 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 do 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 conteúdo do Webhook de um evento de reintegração 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 conteúdo 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 conteúdo 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 conteúdo do Webhook de um evento de cancelamento de assinatura:

Este é um evento somente notificação. Não há envio para a 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 do Webhook deve seguir as seguintes diretrizes de segurança (Ver Tutorial).

  • 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 do Portador JWT passado no cabeçalho autorização contém os seguintes dados no conteúdo 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": essa é a ID de recurso que você usa ao criar um token de autorização do editor para chamar APIs de atendimento saaS. E, dependendo da configuração do aplicativo, você poderá ver esse valor de ID de recurso em "appid" ou "azp". O token tem uma das duas declarações e você deve reagir adequadamente 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 verificar se a chamada do 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 a implementação atual do Webhook não puder aceitar cabeçalhos de autorização, atualize os Webhooks e proteja esses pontos de extremidade (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 respostas de exemplo fornecidas 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 limitada e mantenha-a no estágio de visualização.
  • Defina o preço do plano como zero, para evitar disparar 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 dentro de 24 horas.
  • Verifique se todos os fluxos são invocados de ponta a ponta para simular um cenário real do cliente.
  • 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.

Um fluxo de compra pode ser disparado no portal do Azure ou sites do Microsoft AppSource, dependendo de onde a oferta está sendo publicada.

de plano de alteração, alterarde quantidade e cancelar a assinatura ações são testadas do lado do editor. Do lado da Microsoft, cancelar a assinatura podem ser disparados no portal do Azure e no Centro de Administração (o portal em que as compras do Microsoft AppSource são gerenciadas). Alterar quantidade e planejar só podem ser disparados do Centro de Administração.

Obter suporte

Consulte Suporte para o programa do marketplace comercial no Partner Center para obter opções de suporte do editor.