Compartir a través de


Implementación de un webhook en el servicio SaaS

Al crear una oferta de SaaS procesable en el Centro de partners, el asociado proporciona el webhook de conexión de dirección URL que se usará como punto de conexión HTTP. Microsoft llama a este webhook mediante la llamada HTTP POST para notificar al publicador los siguientes eventos que se producen en el lado de Microsoft:

Evento de webhook 1. Cuando se recibe 2. Si se acepta 3. Si se rechaza
ChangePlan Responder con HTTP 200 PATCH con éxito (este evento es opcional y autoaceptado en 10 segundos) PATCH con error O responder con 4xx (en un plazo de 10 segundos)
ChangeQuantity Responder con HTTP 200 PATCH con éxito (este evento es opcional y autoaceptado en 10 segundos) PATCH con error O responder con 4xx (en un plazo de 10 segundos)
Renew Responder con HTTP 200 No aplicable No aplicable
Suspend Responder con HTTP 200 No aplicable No aplicable
Unsubscribe Responder con HTTP 200 No aplicable No aplicable
Reinstate Responder con HTTP 200 No aplicable No aplicable (llamada a la API de eliminación para desencadenar la eliminación si no se puede aceptar la restablecimiento)

El publicador debe implementar un webhook en el servicio SaaS para mantener el estado de la suscripción de SaaS coherente con el lado de Microsoft. El servicio SaaS es necesario para llamar a Get Operation API para validar y autorizar la llamada de webhook y los datos de carga antes de tomar medidas en función de la notificación de webhook. El publicador debe devolver HTTP 200 a Microsoft tan pronto como se procese la llamada de webhook. Este valor reconoce que el publicador ha recibido correctamente la llamada de webhook.

Importante

El servicio de dirección URL de webhook debe estar en funcionamiento 24 x 7 y estar listo para recibir nuevas llamadas de Microsoft en todo momento. Microsoft tiene una directiva de reintento para la llamada de webhook (500 reintentos durante ocho horas), pero si el publicador no acepta la llamada y devuelve una respuesta, la operación de que el webhook notifica se producirá un error en el lado de Microsoft.

Importante

Los ISV deben evitar la deserialización estricta del esquema de Webhook. Microsoft se reserva el derecho de expandir el esquema en el futuro.

Importante

Los ISV deben validar el token de Microsoft Entra (token JWT) en su punto de conexión de webhook desde el encabezado de solicitud. Se trata de un token de portador estándar y proporcionará detalles del ISV sobre quién es el autor de la llamada. Obtenga más información sobre cómo validar el token en este artículo. learn.microsoft.com/azure/active-directory/develop/access-tokens

ejemplo de carga 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
}

ejemplo de carga de Webhook del 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
}

ejemplo de carga de Webhook de un evento de restablecimiento de suscripción:

// 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
}
 

ejemplo de carga de Webhook de un evento de renovación:

// 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,
}

ejemplo de carga de Webhook de un evento de suspensión:


{
    "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,
}

ejemplo de carga de Webhook de un evento de cancelación de suscripción:

Se trata de un evento de solo notificación. No hay ningún envío 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,
}

Protección de los webhooks

Debe proteger los webhooks para que ninguno de los puntos de conexión de Microsoft realice estas llamadas de webhook. Puede usar cualquier tecnología para implementar los webhooks, pero la implementación de webhook debe seguir las siguientes directrices de seguridad (Consulte tutorial).

  • Microsoft llama a los webhooks con encabezados de autorización que contienen información necesaria para validar las llamadas. Debe habilitar los webhooks para poder recibir los encabezados de autorización. (no agregue detalles de autorización ni tokens de seguridad como tokens de SAS directamente en las direcciones URL del webhook. Estos webhooks podrían no recuperar los encabezados de autorización que Microsoft envía al llamar a los webhooks).

  • El token de portador JWT pasado en el encabezado authorization contiene los siguientes datos en la carga que puede usar para proteger los puntos de conexión.

  • "aud": "este es el identificador de aplicación de Microsoft Entra Identity que agrega a la configuración técnica de la oferta en el Centro de partners de Microsoft".

  • "appid" o "azp": este es el identificador de recurso que se usa al crear el token de autorización del publicador para llamar a las API de suministro de SaaS. Y en función de la configuración de la aplicación, puede ver este valor de identificador de recurso en "appid" o "azp". El token tiene cualquiera de las dos notificaciones y debe reaccionar en consecuencia en el código.

  • "tid": "este es el identificador de inquilino de Microsoft Entra que agrega a la configuración técnica de la oferta en el Centro de partners de Microsoft"

  • Puede comprobar los campos pasados anteriores para asegurarse de que la llamada de Webhook sea válida.

Importante

Microsoft comenzará a requerir que los ISV creen sus webhooks de forma segura y acepten encabezados de autorización. Si la implementación actual de Webhook no puede aceptar encabezados de autorización, debe actualizar los webhooks y proteger dichos puntos de conexión (con instrucciones anteriores) para evitar cualquier interrupción.

Desarrollo y pruebas

Para iniciar el proceso de desarrollo, se recomienda crear respuestas ficticias de API en el lado del publicador. Estas respuestas se pueden basar en las respuestas de ejemplo proporcionadas en este artículo.

Cuando el publicador esté listo para las pruebas de un extremo a otro:

  • Publique una oferta de SaaS en una audiencia preliminar limitada y manténgala en fase de versión preliminar.
  • Establezca el precio del plan en cero para evitar desencadenar gastos de facturación reales durante las pruebas. Otra opción es establecer un precio distinto de cero y cancelar todas las compras de pruebas en un plazo de 24 horas.
  • Asegúrese de que todos los flujos se invocan de un extremo a otro para simular un escenario de cliente real.
  • Si el asociado quiere probar el flujo de compra y facturación completo, hágalo con la oferta cuyo precio es superior a 0 USD. La compra se factura y se generará una factura.

Se puede desencadenar un flujo de compra desde Azure Portal o sitios de Microsoft AppSource, en función de dónde se publique la oferta.

Cambiar plan, cambiar cantidady cancelar la suscripción acciones se prueban desde el lado del publicador. En el lado de Microsoft, cancelar la suscripción se puede desencadenar desde Azure Portal y el Centro de administración (el portal en el que se administran las compras de Microsoft AppSource). Cambiar cantidad y plan solo se puede desencadenar desde el Centro de administración.

Obtener soporte técnico

Consulte soporte técnico para el programa marketplace comercial en el Centro de partners para ver las opciones de soporte técnico del publicador.