Gerenciando postbacks

Para utilizar os postbacks, é necessário aprender o que fazer e como fazê-lo. No próximos passos aprenderemos:

  1. Como utilizar;
  2. Formato do postback;
  3. Como validar o postback;
  4. Como verificar os postbacks;
  5. Como reenviar um postback.

1. Usando os postbacks

Os postbacks podem ser configurados no momento da criação de transações (transactions) e de assinaturas (subscriptions) através do parâmetro postback_url como mostra os exemplos abaixo

curl -X POST https://api.pagar.me/1/transactions -H 'content-type: application/json' -d '{
    "api_key": "SUA_API_KEY",
    "amount" : 151000,
    "card_number" : "4111111111111111",
    "card_expiration_date" : "022021",
    "card_holder_name" : "Joao das Neves",
    "card_cvv" : "123", 
    "postback_url": "https://requestb.in/tl0092tl",
    "customer": {
        "external_id": "#123456789",
        "name": "João das Neves",
        "type": "individual",
        "country": "br",
        "email": "[email protected]",
        "documents": [
          {
            "type": "cpf",
            "number": "00000000000"
          }
        ],
        "phone_numbers": ["+5511999999999", "+5511888888888"],
        "birthday": "1985-01-01"
      },
      "billing": {
        "name": "João das Neves",
        "address": {
          "country": "br",
          "state": "SP",
          "city": "São Paulo",
          "neighborhood": "Vila Carrao",
          "street": "Rua Lobo",
          "street_number": "999",
          "zipcode": "03424030"
        }
      },
      "shipping": {
        "name": "João das Neves",
        "fee": 1000,
        "delivery_date": "2017-12-25",
        "expedited": true,
        "address": {
          "country": "br",
          "state": "SP",
          "city": "São Paulo",
          "neighborhood": "Vila Carrao",
          "street": "Rua Lobo",
          "street_number": "999",
          "zipcode": "03424030"
        }
      },
      "items": [
        {
          "id": "a123",
          "title": "Trono de Ferro",
          "unit_price": 120000,
          "quantity": 1,
          "tangible": true
        },
        {
          "id": "b123",
          "title": "Capa Negra de Inverno",
          "unit_price": 30000,
          "quantity": 1,
          "tangible": true
        }
      ]
}'
<?php
require("vendor/autoload.php");
$pagarme = new PagarMe\Client('SUA_CHAVE_DE_API');

$transaction = $pagarme->transactions()->create([
  'amount' => 1000,
  'payment_method' => 'credit_card',
  'card_holder_name' => 'Anakin Skywalker',
  'card_cvv' => '123',
  'card_number' => '4242424242424242',
  'card_expiration_date' => '1220',
  'postback_url' => 'https://postback.url'
  'customer' => [
    'external_id' => '1',
    'name' => 'Nome do cliente',
    'type' => 'individual',
    'country' => 'br',
    'documents' => [
      [
        'type' => 'cpf',
        'number' => '00000000000'
      ]
    ],
    'phone_numbers' => [ '+551199999999' ],
    'email' => '[email protected]'
  ],
  'billing' => [
    'name' => 'Nome do pagador',
    'address' => [
      'country' => 'br',
      'street' => 'Avenida Brigadeiro Faria Lima',
      'street_number' => '1811',
      'state' => 'sp',
      'city' => 'Sao Paulo',
      'neighborhood' => 'Jardim Paulistano',
      'zipcode' => '01451001'
    ]
  ],
  'shipping' => [
    'name' => 'Nome de quem receberá o produto',
    'fee' => 1020,
    'delivery_date' => '2018-09-22',
    'expedited' => false,
    'address' => [
      'country' => 'br',
      'street' => 'Avenida Brigadeiro Faria Lima',
      'street_number' => '1811',
      'state' => 'sp',
      'city' => 'Sao Paulo',
      'neighborhood' => 'Jardim Paulistano',
      'zipcode' => '01451001'
    ]
  ],
  'items' => [
    [
      'id' => '1',
      'title' => 'R2D2',
      'unit_price' => 300,
      'quantity' => 1,
      'tangible' => true
    ],
    [
      'id' => '2',
      'title' => 'C-3PO',
      'unit_price' => 700,
      'quantity' => 1,
      'tangible' => true
    ]
  ]
]);

Veja o postback em assinaturas:

curl -X POST https://api.pagar.me/1/subscriptions -H 'content-type: application/json' -d '{
    "api_key": "SUA_API_KEY", 
    "card_id": "card_ci234fx8rr649rt16rtb11132", 
    "customer": {
        "email": "[email protected]"
    }, 
    "payment_method": "credit_card", 
    "plan_id": "12783", 
    "postback_url": "http://requestb.in/zyn5obzy"
}'
No momento não temos essa funcionalidade :(
require 'pagarme'

PagarMe.api_key = "SUA_API_KEY"

plan = PagarMe::Plan.find_by_id("12783")

subscription = PagarMe::Subscription.new({
    :payment_method => 'credit_card',
    :card_number => "4901720080344448",
    :card_holder_name => "Jose da Silva",
    :card_expiration_month => "10",
    :card_expiration_year => "15",
    :card_cvv => "314",
    :postback_url => "http://test.com/postback",
    :customer => {
        email: '[email protected]'
})
subscription.plan = plan

subscription.create
<?php
require("vendor/autoload.php");
$pagarme = new PagarMe\Client('SUA_CHAVE_DE_API');

$substription = $pagarme->subscriptions()->create([
  'plan_id' => 123456,
  'payment_method' => 'credit_card',
  'card_number' => '4111111111111111',
  'card_holder_name' => 'UNIX TIME',
  'card_expiration_date' => '0722',
  'card_cvv' => '123',
  'postback_url' => 'http://postbacj.url',
  'customer' => [
    'email' => '[email protected]',
    'name' => 'Unix Time',
    'document_number' => '75948706036',
    'address' => [
      'street' => 'Rua de Teste',
      'street_number' => '100',
      'complementary' => 'Apto 666',
      'neighborhood' => 'Bairro de Teste',
      'zipcode' => '11111111'
    ],
    'phone' => [
      'ddd' => '01',
      'number' => '923456780'
    ],
    'sex' => 'other',
    'born_at' => '1970-01-01',
  ],
  'metadata' => [
    'foo' => 'bar'
  ]
]);
PagarMeService.DefaultApiKey = "SUA_API_KEY";

Subscription subscription = new Subscription();
subscription.PaymentMethod = PaymentMethod.CreditCard;
subscription.CardNumber = "4901720080344448";
subscription.CardHolderName = "Jose da Silva";
subscription.CardExpirationDate = "1215";
subscription.CardCvv = "123";
subscription.PosbackUrl= "http://requestb.in/pkt7pgpk";

Customer customer = new Customer();
customer.Email = "[email protected]";
subscription.Customer = customer;

subscription.Save();
import pagarme

pagarme.authentication_key('SUA_API_KEY')

params = {
  'card_hash': 'CARD_HASH_GERADO',
    'customer': {
    'email': 'daenerys.targaryen[email protected]',
    'name': 'Daenerys Targaryen',
    'document_number': '18152564000105',
    'address': {
        'zipcode': '04571020',
        'neighborhood': 'Dragon Village',
        'street': 'Rua Drogon',
        'street_number': '240'
     },
    'phone': {
        'number': '987654321',
        'ddd': '11'
    }
    } ,
  "capture": "true",
  "async": "false",
  "installments": "1",
  "payment_method":"credit_card",
  "amount": "1000",
  "postback_url": "SUA_POSTBACK_URL"
}

subscription = pagarme.subscription.create(params)

print(subscription)

❗️

Eventuais novos status de transação

Novos status de transação podem ser criados e enviados dentro do payload do postback. Desta forma, a sua aplicação deve estar preparada para receber, por meio de postback, status de transação que anteriormente não existiam.

📘

Postback em localhost?

Para realizar testes, é possível utilizar o site requestBin para gerar uma URL temporária e analisar os postbacks enviados ou o programa ngrok para gerar e vincular uma URL temporária ao IP da máquina de teste / desenvolvimento. Essa URL pode ser utilizada no parâmetro postback_url.

📘

Temporal

Partindo do primeiro postback, os próximos são enviados com o seguinte padrão de intervalo de tempo, em minutos: 1 (três vezes), 5 (três vezes), 60 (25 vezes)

🚧

Postback de transações de assinaturas

Você pode configurar em sua Dashboard se gostaria de receber postbacks quando uma transação pertencente a uma assinatura acaba de ser criada.

🚧

Transações síncronas com postback

Se o parâmetro async for false e você passar uma postback_url, a transação continua sendo síncrona (isto é, a resposta é recebida na hora), mas o seu sistema mesmo assim recebe notificações para a mudança de status da transação.

2. Formato dos postbacks

Quando a postback_url é passada, a transação é retornada com status
processing, e as mudanças de status são enviadas para o seu servidor na URL
de postback. Isso é feito através de um request HTTP POST com os seguintes parâmetros:

ParâmetroDescriçãoObservação
idID da transação.
eventA qual evento o postback se refere.Transações: transaction_status_changed.

Assinatura: subscription_status_changed

Link de Pagamento:
order_status_changed

Recebedor:
recipient_status_changed
old_statusStatus anterior da transação.
desired_statusStatus ideal para objetos deste tipo, em um fluxo normal, onde autorização e captura são feitos com sucesso, por exemplo.
current_statusStatus para o qual efetivamente mudou.
objectQual o tipo do objeto referido.Transações: transaction

Assinaturas: subscription

Link de Pagamento: order

Recebedor: recipient
transactionPossui todas as informações do objeto. Para acessar objetos internos basta acessar a chave transaction[objeto1][objeto2]. Ex: para acessar o ddd: transaction[phone][ddd]

📘

Payload

Para ver os exemplos de Payload, clique aqui para ver em nossa página de referência

📘

Current transaction

Retorna a última transação relacionada à respectiva subscription. Para cartão de crédito, retorna a última cobrança feita, e para boleto, retorna o último boleto gerado

📘

Formato

Diferentemente das respostas da API Pagar.me, o postback sempre terá seus parâmetros em application/x-www-form-urlencoded

3. Como validar um postback

A validação do postback serve para verificar se realmente ele foi enviado pela Pagar.me, e é de extrema importância a sua utilização para que suas transações estejam seguras.

Para fazer a validação, são necessárias duas informações de nosso postback:

  • Valor do header 'X-Hub-Signature'. Vamos chamar esse valor de "assinatura do postback";
  • Corpo da requisição (no mesmo formato mostrado na seção anterior).

Com esses dois valores, basta realizar o HMAC-SHA1 do corpo da requisição utilizando a sua API Key e comparar com a assinatura do postback. Se os valores forem iguais, excelente. Caso contrário, não fomos nós que enviamos essa requisição!

EXPECTED_SIGNATURE=`cat postback_body | openssl dgst -sha1 -hmac "SUA_API_KEY"`
SIGNATURE=1213e67a3b34c2848f8317d29bcb8cbc9e0979b8
if [ "$SIGNATURE" = "$EXPECTED_SIGNATURE" ]; then
    echo "Valid Signature"
fi
PagarMe.api_key = 'SUA_API_KEY';

if PagarMe::Postback.valid_request_signature?(postback_body, '1213e67a3b34c2848f8317d29bcb8cbc9e0979b8')
    puts "Valid Signature"
end
<?php
require("vendor/autoload.php");
$pagarme = new PagarMe\Client('SUA_CHAVE_DE_API');

$requestBody = file_get_contents("php://input"); 
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE'];

$isValidPostback = $pagarme->postbacks()->validate($requestBody, $signature);

if ($isValidPostback) {
  echo "Postback válido";
} else {
  echo "Postback inválido";
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;

var _apiKey = "SUA_API_KEY";
var signature = Request.Headers("X-Hub-Signature");
var rawBody = "rawBody";
var isValid = false;
byte[] apiKeyBytes = Encoding.ASCII.GetBytes(_apiKey);
byte[] rawBodyBytes = Encoding.ASCII.GetBytes(rawBody);
string cleanedSignature = signature.Split('=')[1];
using (HMACSHA1 hmac = new HMACSHA1(apiKeyBytes))
{
  byte[] rawBodyHashBytes = hmac.ComputeHash(rawBodyBytes);
  String rawBodyHash = BitConverter.ToString(rawBodyHashBytes).Replace("-", String.Empty).ToLower();
  isValid = rawBodyHash.Equals(cleanedSignature);
}
Console.WriteLine (isValid);

4. Verificando os postbacks

Após um postback ser enviado, existem duas situações que podem ocorrer:

  • Recebemos um retorno com status code 2xx do postback;
  • Ocorre algum erro no recebimento do postback e recebemos um status code diferente de 2xx (ou até não recebemos uma resposta).

No primeiro caso, nada mais ocorre. Já no segundo caso, começaremos então a fazer tentativas de reenvio do postback. Por padrão, são feitas até 31 novas tentativas.

Se existir algo estranho com sua integração com os nossos postbacks, você consegue monitorar isso do lado da sua aplicação:

curl -X GET 'https://api.pagar.me/1/transactions/:transaction_id/postbacks?api_key=SUA_API_KEY'
import me.pagar.model.PagarMe;
import me.pagar.model.Postback;
import me.pagar.model.Transaction;

PagarMe.init("SUA_API_KEY");
Transaction transaction = new Transaction().find(1550691);
Collection<Postback> postbacks = transaction.postbacks();
System.out.print(postbacks);
require 'pagarme'

PagarMe.api_key = 'SUA_API_KEY'

transaction = PagarMe::Transaction.find("1550691")
postbacks = transaction.postbacks
p postbacks
<?php
require("vendor/autoload.php");
$pagarme = new PagarMe\Client('SUA_CHAVE_DE_API');

$postbacks = $pagarme->postbacks()->getList([
  'model' => 'subscription', //pode ser transaction ou subscription
  'model_id' => 'ID_DA_ASSINATURA_OU_TRANSACAO'
]);
using PagarMe;
using PagarMe.Model;

PagarMeService.DefaultApiKey = "SUA_API_KEY";
PagarMeService.DefaultEncryptionKey = "SUA_ENCRYPTION_KEY";

Transaction transaction = PagarMeService.GetDefaultService().Transactions.Find("1550691");
Postback[] postbacks = transaction.Postbacks.FindAll(new Postback()).ToArray();

Na lista de postbacks é possível visualizar as informações essenciais do postback enviado:

{
  "object": "postback",
  "status": "success",
  "model": "transaction",
  "model_id": "762919",
  "headers": "{\"Content-Type\":\"application/x-www-form-urlencoded\",\"X-PagarMe-Event\":\"transaction_status_changed\",\"X-Hub-Signature\":\"sha1=709355e496ca88b86652c52c543fc7f62a0a4b36\",\"User-Agent\":\"PagarMe-Hookshot/1.0\"}",
  "payload": "",
  "retries": 0,
  "next_retry": null,
  "deliveries": [
    {
      "object": "postback_delivery",
      "status": "success",
      "status_reason": "http_status_code",
      "status_code": "200",
      "response_time": 26,
      "response_headers": "{\"date\":\"Fri, 07 Oct 2016 11:34:13 GMT\",\"content-type\":\"text/html; charset=utf-8\",\"transfer-encoding\":\"chunked\",\"connection\":\"close\",\"set-cookie\":[\"__cfduid=d25dce8bbb2ddb97c62b11e4ddf69780f1475840053; expires=Sat, 07-Oct-17 11:34:13 GMT; path=/; domain=.requestb.in; HttpOnly\"],\"sponsored-by\":\"https://www.runscope.com\",\"via\":\"1.1 vegur\",\"server\":\"cloudflare-nginx\",\"cf-ray\":\"2ee10aeea5e32408-IAD\"}",
      "response_body": "ok",
      "date_created": "2016-10-07T11:34:13.535Z",
      "date_updated": "2016-10-07T11:34:13.569Z",
      "id": "pd_citzp29en015ka373bf04s3fr"
    }
  ],
  "date_created": "2016-10-07T11:34:13.509Z",
  "date_updated": "2016-10-07T11:34:13.577Z",
  "signature": "sha1=709355e496ca88b86652c52c543fc7f62a0a4b36",
  "id": "po_citzp29dx015ja373tk2cno13"
}

Dessas informações, os mais importantes são:

  • deliveries: lista da tentativas de (re)envio do postback relacionado;
  • response_body: qual foi a resposta de sua aplicação ao postback.

5. Reenvio de postbacks

Se necessário, há uma rota que força o reenvio de um postback. Isso para casos em que não foi possível completar o processo, ou por quaisquer outros motivos ligados à sua aplicação.

curl -X POST "https://api.pagar.me/1/transactions/:transaction_id/postbacks/:postback_id/redeliver?api_key=SUA_API_KEY"
import me.pagar.model.PagarMe;
import me.pagar.model.Postback;
import me.pagar.model.Transaction;

PagarMe.init("SUA_API_KEY");

Transaction transaction = new Transaction().find(1550696);
Postback po = transaction.postbackRedeliver("po_cj2wiybvm0cydx67347eai7h4");
Ainda não temos essa feature implementada :(
<?php
require("vendor/autoload.php");
$pagarme = new PagarMe\Client('SUA_CHAVE_DE_API');

$postbackRedeliver = $pagarme->postbacks()->redeliver([
  'model' => 'subscription',
  'model_id' => 'ID_DA_ASSINATURA',
  'postback_id' => 'po_cjlzhftd2006xg573fwelfg9y'
]);
using System;
using PagarMe;
using PagarMe.Model;
using PagarMe.Enumeration;
using System.Collections.Generic;

PagarMe.PagarMeService.DefaultApiKey = "SUA_API_KEY";
transaction = PagarMeService.GetDefaultService ().Transactions.Find ("");
var a = transaction.Postbacks.FindAll (new Postback ()).GetEnumerator();
a.MoveNext ();
Postback postback = a.Current;
postback.Redeliver ();
import pagarme

pagarme.authentication_key('SUA_API_KEY')

redeliver = pagarme.transaction.postback_redeliver("transaction_id", "postback_id")

print (redeliver)

🚧

Vale ressaltar

A chamada à rota de reenvio implica na pausa de possíveis reenvios futuros. Ou seja, se for feito um reenvio antes da 31a tentativa, não completaremos as 31 retentativas.


Próximo

Bom trabalho, o postback é uma excelente ferramenta para controlar as suas transações. A partir desse ponto você vai aprender a integrar o Pagar.me com uma maquininha ou com plataformas de e-commerce.