Webhooks para pagamentos

Atualizações em tempo real sobre as suas transações.

Os Webhooks para Pagamentos (antigamente chamados de Atualizações em tempo real) são um método essencial que informa sobre alterações nos pedidos feitos por pagamentos do Facebook no seu app.

Visão geral

Os Webhooks são um sistema baseado em assinatura entre o Facebook e o seu servidor. Seu app assina o recebimento de atualizações do Facebook por meio de um ponto de extremidade HTTPS específico. Quando um pedido feito no app é atualizado, emitimos uma solicitação POST de HTTPS para o ponto de extremidade, notificando o servidor sobre a alteração.

Estes são os 3 cenários principais em que as atualizações são enviadas para o servidor do seu desenvolvedor:

Como assinar Webhooks

Para assinar Webhooks para Pagamentos, primeiro crie uma URL de ponto de extremidade pública que receba GET de HTTPS para verificação da assinatura e POST para solicitações de alteração de dados. Veja abaixo a descrição da estrutura desses dois tipos de solicitações. Depois, configure as assinaturas para o objeto payment do app. Estas são as duas maneiras de fazer isso:

Em ambos os casos, o ponto de extremidade receberá os mesmos dados da mesma maneira. Consulte Servidor de retorno de chamada para ver mais informações sobre o que o servidor receberá.

Como assinar pelo Painel de Aplicativos

A forma mais fácil de configurar o app para receber atualizações de Webhooks é por meio do painel Pagamentos no Painel de Aplicativos. Encontre o app no painel e clique na aba Payments. A seção Webhooks estará abaixo da seção Configurações da empresa.

Webhooks para pagamentos

O status de assinatura do app será listado nessa tela, independentemente de ter sido adicionado pelo painel ou pela API. Nessa área, é possível alterar e testar a URL de retorno de chamada para a assinatura.

No campo "Retorno de chamada", forneça um ponto de extremidade de servidor válido e de acesso público. Esse é o endereço que usaremos para verificar a assinatura e enviar as atualizações. É necessário que ele responda de acordo com o que está descrito em Servidor de retorno de chamada.

Por fim, forneça um "Token de verificação". O token será enviado somente durante a fase de inscrição para verificar se a assinatura tem origem em uma localização segura. Ele não será enviado em atualizações regulares de Webhooks.

Como testar as configurações

É preciso testar as configurações de retorno de chamada antes de salvar a assinatura. Assim, uma verificação de solicitação GET será emitida para o ponto de extremidade, com os parâmetros hub.mode, hub.challenge e hub.verify_token, assegurando o gerenciamento correto deles. Por exemplo, é preciso garantir que o ponto de extremidade ecoe hub.challenge de volta ao Facebook:

Como testar as configurações

Depois de inserir os detalhes da assinatura, clique no botão "Salvar alterações" na parte inferior da página. Para editar uma assinatura, basta alterar os conteúdos dos campos, refazer o teste e salvar o formulário novamente.

Como assinar pela Graph API

Também é possível configurar e listar assinaturas de forma programática por meio da Graph API. Você precisará do access token do app, disponível na ferramenta Token de Acesso ou por meio do ponto de extremidade /oauth da Graph API.

A API de Assinatura está disponível no ponto de extremidade https://graph.facebook.com/[APP_ID]/subscriptions.

É possível realizar estas 3 tarefas com ele:

  • Adicionar ou modificar uma assinatura (por meio de uma solicitação POST de HTTPS)
  • Listar cada uma das assinaturas existentes (por meio de uma solicitação GET de HTTPS)

Como adicionar e modificar assinaturas

Para configurar uma assinatura, envie um POST com os seguintes parâmetros (correspondentes a campos do formulário descrito acima):

  • object: como descrito acima, o tipo de objeto cujas atualizações você quer receber. Especifique payments.
  • fields: a lista de propriedades, separadas por vírgula, do objeto cujas atualizações sobre alterações você quer receber. Especifique "actions" e "disputes".
  • callback_url: um ponto de extremidade de servidor válido e de acesso público.
  • verify_token: uma string arbitrária, enviada para o ponto de extremidade quando a assinatura é verificada.

Ao receber a solicitação, assim como na configuração do formulário acima, faremos um GET para o retorno de chamada. Assim, verificaremos se ele é válido e se está pronto para receber atualizações. Em especial, é preciso garantir que o ponto de extremidade ecoe hub.challenge de volta ao Facebook.

O app só pode ter uma assinatura para cada tipo de objeto. Portanto, se existir uma assinatura para determinado tipo de objeto, os novos dados publicados substituirão os anteriores.

Como listar as assinaturas

Emitir um GET de HTTP para a API de Assinatura retorna o conteúdo codificado em JSON que lista as assinaturas. Por exemplo:

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

É possível usar o Explorador da Graph API para testá-la usando o token de acesso do app.

Servidor de retorno de chamada

O servidor de retorno de chamada deve gerenciar 2 tipos de solicitações. Verifique se ele está em uma URL pública, para que possamos fazer as solicitações.

Verificação da assinatura

Para começar, os servidores do Facebook farão um GET único de HTTPS para a URL de retorno de chamada quando você tentar adicionar ou modificar uma assinatura. Uma string de consulta será adicionada à URL de retorno de chamada com os parâmetros a seguir:

Parâmetro Descrição

hub.mode

A string subscribe é passada nesse parâmetro.

hub.challenge

Uma string aleatória.

hub.verify_token

O valor verify_token especificado durante a criação da assinatura.

O ponto de extremidade verificará primeiro o hub.verify_token. Com isso, o servidor saberá que a solicitação foi feita pelo Facebook e está relacionada à assinatura que você acabou de configurar.

Então, ele deve ecoar de volta apenas o valor hub.challenge que confirma para o Facebook que o servidor está configurado para aceitar retornos de chamada e previne vulnerabilidades de negação de serviço (DDoS, pelas iniciais em inglês).

Observação para desenvolvedores de PHP: pontos e espaços em nomes de parâmetro de consulta são automaticamente convertidos em sublinhados em PHP. Portanto, se você estiver escrevendo o ponto de extremidade de retorno de chamada nessa linguagem, os parâmetros devem ser acessados usando $_GET['hub_mode'], $_GET['hub_challenge'] e $_GET['hub_verify_token']. Para ver mais detalhes, consulte esta nota do manual de linguagem.

Como receber atualizações

Depois da assinatura, emitiremos um POST de HTTPS para o ponto de extremidade do servidor sempre que ocorrerem alterações (em conexões ou campos escolhidos). É preciso responder à solicitação com o código 200 de HTTP.

Observação: todas as respostas de HTTP diferentes de 200 serão consideradas erros. Nesses casos, continuaremos tentando enviar a atualização de webhooks. Portanto, se você não responder corretamente, receberá a mesma atualização diversas vezes.

A solicitação terá o tipo de conteúdo application/json. Já o corpo incluirá uma string codificada em JSON com uma ou mais alterações.

Observação para desenvolvedores de PHP: para obter os dados codificados em PHP, use o seguinte código:

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

Os parâmetros hub.mode, hub.challenge e hub.verify_token não são reenviados depois que a assinatura é confirmada.

Veja um exemplo típico de um retorno de chamada feito para a assinatura do objeto payments:

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

É importante observar que as atualizações de Webhook informam apenas que um pagamento específico identificado pelo campo id foi alterado. Após receber a atualização, é preciso que você faça uma consulta da Graph API para obter os detalhes da transação, a fim de responder à alteração de forma adequada.

Observação: as atualizações de pagamento nunca são agrupadas em lote, embora isso possa ser feito com Webhooks em outros tipos de objeto.

Você receberá uma nova atualização sempre que uma transação for atualizada por ação do usuário ou do desenvolvedor.

Se uma atualização enviada para o servidor falhar, tentaremos outra vez imediatamente e depois mais algumas vezes, diminuindo a frequência nas 24 horas seguintes.

A cada solicitação, enviamos um cabeçalho HTTP X-Hub-Signature-256 com a assinatura SHA256 da carga da solicitação, usando a chave secreta do app como a chave, além do prefixo sha256=. O ponto de extremidade de retorno de chamada pode verificar essa assinatura para validar a integridade e a origem da carga.

Como responder a atualizações

Depois que o servidor receber uma atualização, faça uma consulta da Graph API usando o campo id para obter detalhes sobre o novo status da transação. Em seguida, tome medidas de acordo com o status.

As seções a seguir listam as possíveis alterações de estado que acionam o envio de uma atualização. Elas podem ser destes dois tipos:

  • Alterações na matriz de ações, que ocorrem quando um pagamento é finalizado de forma assíncrona, um reembolso é emitido (por você ou pelo Facebook) ou um estorno é feito.
  • Alterações na matriz de contestações, que ocorrem quando uma contestação de pedido é iniciada pelo consumidor.

Ações

Cada objeto payment contém uma matriz intitulada actions com a coleção de alterações de estado pelas quais a transação passou. Cada entrada na matriz actions tem uma propriedade chamada type que descreve o tipo de ação que ocorreu. type pode ter os valores charge, refund, chargeback, chargeback_reversal e decline, que estão explicados aqui.

Este é um exemplo de resposta da Graph API para um objeto de pagamento com as ações associadas:

{
   "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 você assinou os campos de ação quando fez o registro para Webhooks, emitiremos uma atualização quando a matriz for alterada da seguinte maneira:

Cobrança

Inicialmente, todos os pedidos contêm uma entrada de cobrança com "status": "initiated". Os pagamentos em estado iniciado ainda não foram concluídos. Não enviaremos atualizações para pagamentos nesse estado.

Quando o pagamento for concluído, "status": "initiated" será alterado para "status": "completed", e emitiremos uma atualização. Quando você receber essa alteração, confira os registros de pagamento para verificar se a transação é nova ou não e responda da seguinte maneira:

  • Se você já estiver ciente do pedido e este já tiver sido processado pelo retorno de chamada JavaScript (de preferência como primeira escolha), você poderá ignorar a atualização ou usá-la como confirmação extra.
  • Se você estiver ciente do pedido, mas ele estiver como initiated, você poderá prosseguir com o processamento e encaminhar ao consumidor a moeda ou o item virtual associado. O pagamento poderá então ser marcado como concluído.
  • Se você não estiver ciente do pedido, isso indicará que o fluxo no lado do cliente foi interrompido, provavelmente devido a um problema de conectividade ou ao fechamento do navegador antes da finalização da compra. Você ainda poderá processar e concluir o pedido, porque o Facebook permanece sendo a melhor fonte de verdade em relação à cobrança de usuário.

Você também receberá atualizações de pagamentos com "status": "failed", que não devem ser processados.

Reembolso

Sempre que emitir um reembolso pela Graph API, você receberá uma atualização. Da mesma forma que com "type": "charge", o reembolso pode apresentar um status variável, do qual você deve estar ciente. É possível que o reembolso falhe, em geral, devido a erros de processamento ou conectividade. Nesse caso, tente emiti-lo novamente.

Estornos, reversões de estornos e recusas

Assim como acontece com reembolsos, você também receberá uma notificação quando um estorno, uma reversão de estorno ou uma recusa forem emitidos. Um objeto de estorno, reversão de estorno ou recusa será adicionado à matriz de ações dos dados de retorno da Graph API para o pagamento.

Contestações

Quando uma contestação for iniciada, notificaremos você por meio da emissão de uma atualização. Nesse caso, uma nova matriz "disputes" aparecerá como parte do objeto payment. A matriz conterá o horário em que a contestação foi iniciada, o motivo de o consumidor ter iniciado a resposta e o endereço de email do consumidor, pelo qual você poderá entrar em contato para resolver a contestação.

Veja um exemplo de resposta da Graph API para uma transação contestada:

{
   "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 ver mais informações sobre como responder a contestações e emitir reembolsos, consulte Payments How-to: Handling Disputes and Refunds.