Webhooks para pagos

Actualizaciones en tiempo real sobre tus transacciones.

Webhooks para pagos (anteriormente conocido como "Actualizaciones en tiempo real") es un método imprescindible con el que te puedes mantener al corriente de los cambios que se realizan en los pedidos, a través de los pagos de Facebook, en la app.

Información general

Los webhooks son un sistema basado en suscripciones que funciona entre Facebook y tu servidor. Tu app se suscribe para recibir actualizaciones de Facebook a través de un punto de conexión HTTPS específico. Cuando se actualice un pedido hecho con la app, enviaremos una solicitud POST HTTPS al punto de conexión, con la que informaremos al servidor del cambio.

Existen tres casos principales en los que se envían actualizaciones al servidor del desarrollador:

Suscribirse a Webhooks

Para suscribirte a Webhooks para pagos, crea primero una URL de punto de conexión pública que reciba las solicitudes GET HTTPS para la verificación de la suscripción y POST para las solicitudes de datos del cambio. A continuación, se describe la estructura de estos tipos de solicitud. Luego, configura las suscripciones al objeto payment de la app. Hay dos formas de hacerlo:

En cualquier caso, el punto de conexión recibirá los mismos datos de la misma manera. Consulta El servidor de devolución de llamada para obtener más información sobre lo que recibirá el servidor.

Suscribirse a través del panel de apps

La forma más fácil de configurar la app para recibir actualizaciones de Webhooks es a través de la pestaña de pagos ubicada en el panel de apps. Busca la app en el panel y haz clic en la pestaña Payments. La sección "Webhooks" se encuentra justo debajo de la sección "Configuración" de la empresa.

Webhooks para pagos

En esta pantalla, luego aparecerá el estado de la suscripción de la app, ya sea que se haya agregado mediante el panel o la API. Aquí es posible cambiar la URL de devolución de llamada de la suscripción y probarla.

En el campo "Devolución de llamada", debes proporcionar un punto de conexión válido del servidor de acceso público. Esta es la dirección que usaremos para verificar la suscripción y enviar las actualizaciones, por lo que debe responder según lo descrito en la sección El servidor de devolución de llamada.

Por último, suministra un "Token de verificación". Este token solo se enviará durante la fase de inscripción para verificar que la suscripción se origine en una ubicación segura. El token no se enviará en las actualizaciones normales de Webhooks.

Probar la configuración

Es necesario que pruebes la configuración de devolución de llamada antes de guardar la suscripción. Se enviará una solicitud "GET" de verificación al punto de conexión, que contiene los parámetros hub.mode, hub.challenge y hub.verify_token, y se asegurará de que los administres correctamente. Por ejemplo, debes asegurarte de que el punto de conexión responda repitiendo el valor hub.challenge en Facebook:

Probar la configuración

Una vez que hayas ingresado los detalles de la suscripción, asegúrate de hacer clic en el botón "Guardar cambios", en la parte inferior de la página. Editar una suscripción implica simplemente modificar el contenido de los campos, volver a realizar la prueba y, luego, guardar de nuevo el formulario.

Suscribirse a través de la API Graph

También es posible configurar y visualizar las suscripciones de manera programática mediante la API Graph. Necesitarás el access token de la app, que está disponible en la herramienta de tokens de acceso o usando el punto de conexión /oauth de la API Graph.

La API de suscripción está disponible en el punto de conexión https://graph.facebook.com/[APP_ID]/subscriptions.

Con ella, puedes realizar estas tres tareas:

  • Agregar o modificar una suscripción (enviando una solicitud POST HTTPS)
  • Visualizar cada una de las suscripciones actuales (enviando una solicitud GET HTTPS)

Agregar y modificar suscripciones

Para configurar una suscripción, envía una solicitud POST con los siguientes parámetros. Ten en cuenta que estos parámetros corresponden a los campos del formulario descrito anteriormente:

  • object: según lo anterior, el tipo del objeto sobre el que quieres recibir actualizaciones. Especifica payments.
  • fields: una lista separada por comas de las propiedades del tipo de objeto sobre cuyos cambios quieres recibir actualizaciones. Especifica "actions" y "disputes".
  • callback_url: un punto de conexión del servidor válido y de acceso público.
  • verify_token: una cadena arbitraria que se envía al punto de conexión cuando se verifica la suscripción.

Cuando recibamos esta solicitud, al igual que la configuración del formulario anterior, enviaremos una solicitud GET a tu devolución de llamada para asegurarnos de que sea válida y esté lista para recibir actualizaciones. En particular, debes asegurarte de que el punto de conexión responda repitiendo el valor hub.challenge en Facebook.

Ten en cuenta que, como una app solo puede tener una suscripción para cada tipo de objeto, si hay una suscripción para este tipo en particular, los nuevos datos publicados reemplazan los datos anteriores.

Visualizar las suscripciones

Si emitimos una solicitud GET HTTP a la API de suscripción, se devuelve contenido con codificación JSON que muestra las suscripciones. Por ejemplo:

[
  {
    "object": "payments",
    "callback_url": "https://www.friendsmash.com/rtu.php",
    "fields": ["actions", "disputes"],
    "active": true
  }
]

Puedes usar el explorador de la API Graph para experimentar directamente con esta API, pero no te olvides de usar el token de acceso de la app.

El servidor de devolución de llamada

El servidor de devolución de llamada debe administrar dos tipos de solicitud. Asegúrate de que esté en una URL pública para que podamos hacer correctamente estas solicitudes.

Verificación de la suscripción

En primer lugar, los servidores de Facebook harán una sola solicitud GET HTTPS a la URL de devolución de llamada cuando trates de agregar o modificar una suscripción. Se agregará una cadena de consulta a la URL de devolución de llamada con los siguientes parámetros:

Parámetro Descripción

hub.mode

Se pasa la cadena "subscribe" en este parámetro.

hub.challenge

Una cadena aleatoria.

hub.verify_token

El valor verify_token que especificaste cuando creaste la suscripción.

El punto de conexión debe verificar primero el hub.verify_token. Así, se garantiza que el servidor sepa que Facebook está haciendo la solicitud y la relacione con la suscripción que acabas de configurar.

Luego, deberá responder repitiendo solo el valor hub.challenge, que le confirma a Facebook que este servidor está configurado para aceptar devoluciones de llamada y evita vulnerabilidades por denegación de servicio (DDoS).

Nota para los desarrolladores de PHP: En PHP, los puntos y espacios en los nombres de los parámetros de consulta se convierten automáticamente en guiones bajos. Por lo tanto, deberás acceder a estos parámetros usando $_GET['hub_mode'], $_GET['hub_challenge'] y $_GET['hub_verify_token'] si escribes el punto de conexión de devolución de llamada en PHP. Consulta esta nota en el manual de lenguaje PHP para obtener más información.

Recibir actualizaciones

Si la suscripción es correcta, procederemos a enviar una solicitud POST HTTPS al punto de conexión del servidor cada vez que se produzcan cambios (en las conexiones o en los campos seleccionados). Es necesario que respondas a esta solicitud con el código HTTP 200.

Nota: Consideramos que cualquier otra respuesta distinta de 200 es un error. En este caso, seguiremos intentando enviar la actualización de webhooks. Por esta razón, si no envías la respuesta correcta, es posible que recibas varias veces la misma actualización.

La solicitud tendrá el tipo de contenido application/json y el cuerpo incluirá una cadena con codificación JSON que contendrá uno o varios cambios.

Nota para los desarrolladores de PHP: En PHP, para obtener los datos cifrados, se debería usar el siguiente código:

$data = file_get_contents("php://input");
$json = json_decode($data);`

Ten en cuenta que los parámetros hub.mode, hub.challenge y hub.verify_token no se vuelven a enviar una vez que se confirma la suscripción.

Este es un ejemplo típico de una devolución de llamada realizada para la suscripción de un objeto payments:

{
  "object": "payments",
  "entry": [
    {
      "id": "296989303750203",
      "time": 1347996346,
      "changed_fields": [
        "actions"
      ]
    }
  ]
}

Debes tener presente que las actualizaciones de Webhooks solo te informan que se cambió un pago determinado, identificado por el campo id. Después de recibir la actualización, se te solicitará que consultes la API Graph para obtener detalles de la transacción, con el fin de administrar el cambio adecuadamente.

Nota: Las actualizaciones de pagos nunca se procesan por lotes, a diferencia de los webhooks de otros tipos de objeto.

Recibirás una nueva actualización cada vez que se actualice una transacción, ya sea por la acción de un usuario o de un desarrollador.

Si falla una actualización enviada al servidor, volveremos a intentar realizar la operación inmediatamente y, luego, unas cuantas veces más, con una disminución de la frecuencia, durante las siguientes 24 horas.

Con cada solicitud, enviamos un encabezado HTTP sha256= que contiene la firma SHA256 de la carga de solicitud con la clave secreta de la app como clave y el prefijo X-Hub-Signature-256. El punto de conexión de devolución de llamada puede verificar esta firma para validar la integridad y el origen de la carga.

Responder a las actualizaciones

Después de que el servidor recibe una actualización, deberás consultar la API Graph usando el campo id para obtener información sobre el nuevo estado de la transacción. A continuación, deberás actuar en función de este estado.

En las siguientes secciones, se enumeran todos los posibles cambios de estado que activan el envío de una actualización. En general, se dividen en:

  • Cambios a la matriz de acciones, que ocurren cuando se realiza un pago de manera asincrónica, se emite un reembolso (de tu parte o de parte de Facebook) o se produce un contracargo.
  • Cambios a la matriz de disputas, que ocurren cuando el consumidor inicia una disputa por un pedido.

Acciones

Los objetos payment incluyen una matriz titulada actions, que contienen la colección de cambios de estado que experimentó la transacción. Las entradas en la matriz actions tienen una propiedad llamada type, que describe el tipo de acción que se llevó a cabo. type puede tener los siguientes valores: charge, refund, chargebackchargeback_reversal y decline, que se explican aquí en detalle.

Este es un ejemplo de respuesta de la API Graph de un objeto de pago con acciones asociadas:

{
   "id": "3603105474213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      },
      {
         "type": "refund",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-23T21:18:54+0000",
         "time_updated": "2013-03-23T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.00"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,}`

Como te suscribiste al campo de acciones al registrarte en Webhooks, emitiremos una actualización cuando se produzcan los siguientes cambios en la matriz:

Cargo

En un principio, todos los pedidos contienen una entrada de cargo con "status": "initiated". Un pago iniciado es aquel que solo se inició y todavía no se completó. No enviaremos actualizaciones para los pagos en estado iniciado.

Cuando se realice correctamente un pago, se cambiará "status": "initiated" a "status": "completed" y emitiremos una actualización. Al ver este cambio, deberás comprobar los registros de pago para verificar si es una transacción nueva o preexistente y responder de la siguiente manera:

  • Si ya conoces el pedido y la devolución de llamada de JavaScript lo procesó (preferiblemente como primera opción), es seguro ignorar la actualización, o bien puedes usarla como confirmación adicional.
  • Si conoces el pedido, pero se encuentra en estado initiated, puedes procesarlo emitiendo al consumidor el artículo virtual o la divisa asociados. Este pago se puede marcar como completo de manera segura.
  • Si el pedido es desconocido, el cliente no completó el proceso, lo que muy probablemente se deba a un problema de conectividad o a que el consumidor cerró el navegador mientras hacía el pago. De todas maneras, puedes procesar y completar este pedido, ya que Facebook sigue siendo la fuente más confiable en lo que respecta a facturación del usuario.

También recibirás actualizaciones para los pagos con "status": "failed", que no se deberán procesar.

Reembolso

Cada vez que emitas un reembolso mediante la API Graph, recibirás una actualización. Al igual que con "type": "charge", un reembolso también puede tener un estado variable que debes conocer. En particular, es posible que un reembolso presente un error, lo que suele deberse a un problema de procesamiento o conectividad. En tal caso, deberás volver a emitir el reembolso.

Contracargo, anulación de contracargo y rechazos

Al igual que con los reembolsos, también se te notificará cuando se emita un contracargo, una anulación de contracargo o un rechazo. Un objeto de contracargo, anulación de contracargo o rechazo se agregará a la matriz de acciones de los datos de retorno de la API Graph para el pago.

Disputas

Cuando se inicie una disputa, te lo notificaremos emitiendo una actualización. En este caso, verás una nueva matriz "disputes" como parte del objeto payment. La matriz contendrá la hora en que se inició la disputa, la razón por la que el cliente inició la respuesta y la dirección de correo electrónico de este, que puedes usar para contactarlo directamente a fin de resolver la disputa.

Este es un ejemplo de respuesta completa de la API Graph para una transacción en disputa:

{
   "id": "990361254213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.99"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,
   "disputes": [
      {
         "user_comment": "I didn't receive my item! I want a refund, please!",
         "time_created": "2013-03-24T18:21:02+0000",
         "user_email": "email\u0040domain.com",
         "status": "resolved", 
         "reason": "refunded_in_cash"
      }
   ]
}

Para obtener más información sobre cómo responder a las disputas y emitir reembolsos, consulta la sección Procedimientos de pago: manejo de disputas y reembolsos.