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 mostram os exemplos abaixo:

curl -X POST https://api.pagar.me/1/transactions -H 'content-type: application/json' -d '{
    "amount": "100", 
    "api_key": "SUA_API_KEY", 
    "card_id": "card_ci6l9fx8f0042rt16rtb477gj", 
    "customer": {
        "address": {
            "neighborhood": "Jardim Paulistano", 
            "street": "Avenida Brigadeiro Faria Lima", 
            "street_number": "1811", 
            "zipcode": "01451001"
        }, 
        "document_number": "18152564000105", 
        "email": "[email protected]", 
        "name": "Aardvark Silva", 
        "phone": {
            "ddd": "11", 
            "ddi": "55", 
            "number": "99999999"
        }
    }, 
    "metadata": {
        "idProduto": "13933139"
    }, 
    "postback_url": "http://requestb.in/pkt7pgpk"
}'
import java.util.HashMap;
import java.util.Map;

import me.pagar.model.Address;
import me.pagar.model.Customer;
import me.pagar.model.PagarMe;
import me.pagar.model.PagarMeException;
import me.pagar.model.Phone;
import me.pagar.model.Transaction;
import me.pagar.model.Transaction.PaymentMethod;

PagarMe.init("SUA_API_KEY");

Phone phone = new Phone();
phone.setDdd("11");
phone.setDdi("55");
phone.setNumber("99999999");

String street = "Avenida Brigadeiro Faria Lima";
String streetNumber = "1811";
String neighborhood = "Jardim Paulistano";
String zipcode = "01451001";
Address address = new Address(street, streetNumber, neighborhood, zipcode);

String name = "Aardvark Silva";
String email = "[email protected]";
String documentNumber = "18152564000105";
Customer customer = new Customer(name, email);
customer.setAddress(address);
customer.setPhone(phone);
customer.setDocumentNumber(documentNumber);

Map<String, Object> metadata = new HashMap<String, Object>();
metadata.put("IdProduto", 13933139);

Transaction tx = new Transaction();
tx.setCustomer(customer);
tx.setAmount(100);
tx.setCardId("card_ci6l9fx8f0042rt16rtb477gj");
tx.setPaymentMethod(PaymentMethod.CREDIT_CARD);
tx.setMetadata(metadata);
tx.setPostbackUrl("http://requestb.in/pkt7pgpk");
tx.save();
require 'pagarme'

PagarMe.api_key = "SUA_API_KEY";

transaction  = PagarMe::Transaction.new({
    amount: 100,
    payment_method: "credit_card",
    card_id: "card_ci6l9fx8f0042rt16rtb477gj",
    postback_url: "http://requestb.in/pkt7pgpk",
    customer: {
        name: "Aardvark Silva",
        email: "[email protected]",
        document_number: "18152564000105",
        address: {
            street: "Avenida Brigadeiro Faria Lima",
            street_number: "1811",
            neighborhood: "Jardim Paulistano",
            zipcode: "01451001"
        },
        phone: {
            ddi: "55",
            ddd: "11",
            number: "99999999"
        }
    },
    metadata: {
        idProduto: "13933139"
    }
})

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

$transaction = $pagarme->transactions()->create([
  "amount" => 100,
  "card_id" => "card_ci6l9fx8f0042rt16rtb477gj",
  "payment_method" => "credit_card",
  "postback_url" => "http://requestb.in/pkt7pgpk",
  "customer" => [
    "name" => "Aardvark Silva", 
    "document_number" => "18152564000105",
    "email" => "[email protected]",
    "address" => [
      "street" => "Avenida Brigadeiro Faria Lima", 
      "street_number" => "1811",
      "neighborhood" => "Jardim Paulistano",
      "zipcode" => "01451001"
    ],
    "phone" => [
      "ddi" => "55"
      "ddd" => "11",
      "number" => "99999999" 
    ]
  ]
]);
PagarMeService.DefaultApiKey = "SUA_API_KEY";

Transaction transaction = new Transaction();

transaction.Amount = 100;
transaction.Card = new Card() { 
    Id = "card_ci6l9fx8f0042rt16rtb477gj"
};
transaction.Customer = new Customer () {
    Name = "Aardvark Silva",
    Email = "[email protected]",
    DocumentNumber = "18152564000105",
    Address = new Address () {
        Street = "Avenida Brigadeiro Faria Lima",
        StreetNumber = "123",
        Neighborhood = "Jardim Paulistano",
        Zipcode = "01451001"
    },

    Phone = new Phone () {
        Ddi = "55",
        Ddd = "11",
        Number = "23456789"
    }
};
transaction.PostbackUrl = "http://requestb.in/pkt7pgpk";
transaction.Metadata = new Metadata() {
    IdProduto = 13933139
};

transaction.Save();
import pagarme

pagarme.authentication_key('SUA_API_KEY')

params = {
  'card_hash': 'CARD_HASH_GERADO',
    'customer': {
    'email': '[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"
}

trx = pagarme.transaction.create(params)

print(trx)

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');

$subscription = $pagarme->subscriptions()->create([
  "plan_id" => "13850",
  "payment_method" => "credit_card",
  "card_number" => "4901720080344448",
  "postback_url" => "http://requestb.in/pkt7pgpk",
  "card_holder_name" => "Jose da Silva",
  "card_expiration_month" => 12,
  "card_expiration_year" => 15,
  "card_cvv" => "123",
  'customer' => [
    'email' => '[email protected]'
  ]
]);
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': '[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)

📘

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.

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);
Não possuimos esta funcionalidade no momento :(

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();
import pagarme

pagarme.authentication_key('SUA_API_KEY')

#transaction postbacks
postback = pagarme.transaction.postbacks("TRANSACTION_ID")
print (postback)

#subscription postbacks
postback = pagarme.subscription.postbacks("SUBSCRIPTION_ID")

print (postback)

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.