This section gathers endpoints for managing notifications by the receiving PSP to the receiving end user.
Understanding the mTLS pattern
By Central Bank regulation, it will be necessary to insert an Efí public key into your server so that communication follows the mTLS standard. In the domain representing your server, you should configure the requirement of the public key (mTLS) that we are providing, so that mutual authentication occurs.
Efí will make 2 requests to your domain (server):
- First Request: We will ensure that your server is requiring an Efí public key. To do this, we will send a request without a certificate and your server should not accept the request. If your server responds with refusal, we will send the 2nd request.
- Second Request: Your server, which must contain the provided public key, should perform the "Hand-Shake" so that communication is established.
Your server must have a minimum TLS version of 1.2.
On your server, configure a 'POST' route with a default response as a string "200". Include our production or Sandbox certificate on your server, below are some examples.
Dedicated ServersIt is recommended that you have a dedicated server to be able to perform webhook configurations, as you need access to some files to make the configurations, as in the examples below.
Examples of server settings
To configure your server, you will need the Efí public keys. Below are the addresses of the keys for the Production and Sandbox environments. These keys must be downloaded and placed on your server.
Attribute | Public Key URL |
---|
Production | https://certificados.efipay.com.br/webhooks/certificate-chain-prod.crt |
Sandbox | https://certificados.efipay.com.br/webhooks/certificate-chain-homolog.crt |
The code snippets below aim to exemplify the necessary configurations on your server to perform the hand-shake with our servers.
from flask import Flask, jsonify, request
import ssl
import json
app = Flask(__name__)
@app.route("/", methods=["POST"])
def imprimir():
response = {"status": 200}
return jsonify(response)
@app.route("/pix", methods=["POST"])
def imprimirPix():
imprime = print(request.json)
data = request.json
with open('data.txt', 'a') as outfile:
outfile.write("\n")
json.dump(data, outfile)
return jsonify(imprime)
if __name__ == "__main__":
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations('caminho-certificados/certificado-público.crt')
context.load_cert_chain(
'caminho-certificados/server_ssl.crt.pem',
'caminho-certificados/server_ssl.key.pem')
app.run(ssl_context=context, host='0.0.0.0')
server {
#
# ...
#
listen [::]:443 ssl ipv6only=on;
listen 443 ssl;
ssl_certificate server_ssl.crt.pem;
ssl_certificate_key server_ssl.key.pem;
ssl_client_certificate /root/chain-pix-webhooks-prod.crt;
ssl_verify_client optional;
ssl_verify_depth 3;
#
# ...
#
location /webhook {
if ($ssl_client_verify != SUCCESS) {
return 403;
}
proxy_pass /webhook;
}
}
#Developed by the Technical Consulting Team at Efí
const express = require("express");
const fs = require("fs");
const https = require("https");
var logger = require('morgan');
const httpsOptions = {
cert: fs.readFileSync(""),
key: fs.readFileSync("/"),
ca: fs.readFileSync(""),
minVersion: "TLSv1.2",
requestCert: true,
rejectUnauthorized: true,
};
const app = express();
const httpsServer = https.createServer(httpsOptions, app);
const PORT = 443;
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.post("/webhook", (request, response) => {
if (request.socket.authorized) {
response.status(200).end();
} else {
response.status(401).end();
}
});
app.post("/webhook/pix", (request, response) => {
if (request.socket.authorized){
response.status(200).end();
}else{
response.status(401).end();
}
});
httpsServer.listen(PORT, () =>
console.log(`Express server currently running on port ${PORT}`)
);
# ********************************************************************************* #
# Use the first example if you want to require the certificate for authentication #
# mutual on any route in the domain indicated in VirtualHost. #
# Works well for sub-domains. Example: https:
# ********************************************************************************* #
<IfModule mod_ssl.c>
<VirtualHost *:443> # Porta HTTPS
#
# ...
#
SSLCertificateFile /certificate_path/fullchain_ssl.pem #fullchain associated with your domain SSL certificate
SSLCertificateKeyFile /path_certificate/privkey_ssl.pem #privkey associated with your domain SSL certificate
#Efí public key
SSLCACertificateFile /path_certificate/chain-pix-prod.crt
# mTLS Efí
SSLVerifyClient require
SSLVerifyDepth 3
# Treating /pix, always redirecting requests to /webhook
Alias "/pix/" "/var/www/webhook/index.php"
Alias "/pix" "/var/www/webhook/index.php"
#
# ...
#
</VirtualHost>
</IfModule>
# ******************************************************************************** #
# Use the second example, if you want to require the certificate for authentication #
# mutual in only one route from the domain indicated in VirtualHost. #
# Example: https:
# ******************************************************************************** #
<IfModule mod_ssl.c>
<VirtualHost *:443> # Porta HTTPS
#
# ...
#
SSLCertificateFile /certificate_path/fullchain_ssl.pem #fullchain associated with your domain SSL certificate
SSLCertificateKeyFile /path_certificate/privkey_ssl.pem #privkey associated with your domain SSL certificate
#Efí public key
SSLCACertificateFile /path_certificate/chain-pix-prod.crt
# mTLS Efí
SSLVerifyClient none
SSLProtocol TLSv1.2
<Location "/webhook">
SSLVerifyClient require
SSLVerifyDepth 3
</Location>
# Treating /pix, always redirecting requests to /webhook
Alias "/webhook/pix/" "/var/www/webhook/index.php"
Alias "/webhook/pix" "/var/www/webhook/index.php"
#
#...
#
</VirtualHost>
</IfModule>
# ********************************************************************************** #
# For this example to work, your server must have configured #
# the mTLS certificate, with the direction to this file, and also with the #
# dealing with /pix. Just as it is done in our example of Apache servers. #
# ********************************************************************************** #
<?php
function resposta($status, $mensagem, $dados)
{
$resposta['status'] = $status;
$resposta['mensagem'] = $mensagem;
$resposta['dados'] = $dados;
$json_resposta = '<pre>' . json_encode($resposta, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . '</pre>';
header("HTTP/1.1 " . $status);
echo $json_resposta;
}
function salvar($dados)
{
$nomeArquivo = './dados.json';
$dadosGravados = json_decode(file_get_contents($nomeArquivo), true);
$arquivo = fopen($nomeArquivo, 'w');
array_push($dadosGravados, $dados);
if (fwrite($arquivo, json_encode($dadosGravados))) {
resposta(200, "Request completed successfully!", $dados);
} else {
resposta(300, "Failed to save request data.", $dados);
}
fclose($arquivo);
}
function requisicao($metodo, $body, $parametros)
{
switch ($metodo) {
case 'POST':
salvar($body);
break;
case 'GET':
resposta(200, "Request completed successfully!", $body);
break;
}
}
$metodo = $_SERVER['REQUEST_METHOD'];
$parametros = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
$body = json_decode(file_get_contents('php://input'), true);
try {
requisicao($metodo, $body, $parametros);
} catch (Exception $e) {
resposta(400, $e->getMessage(), $e);
}
To have a valid SSL, you must contact a Certificate Authority and generate the private key server_ssl.key.pem
and a public one server_ssl.crt.pem
, thus validating the integrity of the connection. You can do this for free using a utility like Certbot, for example.
Skip-mTLS
For hosting on shared servers, there may be restrictions regarding the insertion of certificates generated by another entity, such as our CA, for example. Therefore, we provide the option to skip mTLS, which allows registering the webhook without the need for mTLS validation.
Attention!It is important to note that we will always send the certificate in webhooks, whether in registration or in Pix notification. However, when skip-mTLS is used, you, the integrating party, are responsible for validating our certificate.
If you choose to use the skip mTLS attribute, that is, without mTLS validation on your server, you must implement measures to ensure that the sender of the webhooks to your server is indeed Efí.
We suggest the following two validation methods, but strongly recommend that you use them together:
- Check the communication IP: You can restrict communication to the webhook domain to only accept messages from the IP used by Efí.
IP currently used in our communications: '34.193.116.226'.
- Add a hash to the registered webhook URL: Create an hmac (a proprietary identifier) that will be appended to the end of the URL when registering the webhook. This hash will be used to validate the origin of the notification. Thus, all webhooks sent to your server will have this final identification and your application must validate its presence.
Example:
Original notification URL: https://your_domain.com.br/webhook
How it should be registered with the hash: https://your_domain.com.br/webhook?hmac=xyz&ignorar=
. The term ignorar=
will serve to handle the addition of /pix
at the end of the URL.
How to register skip-mTLS:
To configure the Pix webhook, you must use the specific endpoint and pass the parameter x-skip-mtls-checking
in the request header with the value true
or false
depending on whether you want to enable or disable this feature.
The image below shows how this parameter should be provided:
Setting Pix Webhook
Endpoint for configuring the notification service for received Pix. Pix originating from static collections will only be notified if they are associated with a txid
.
ReminderA webhook URL can be associated with multiple Pix keys.
On the other hand, a Pix key can only be linked to a single webhook URL.
InformationWhen registering your webhook, we will send a test notification to the registered URL, however when a notification is actually sent, the path /pix
will be added to the end of the registered URL. To avoid needing two distinct routes, you can add a ?ignorar=
parameter to the end of the registered URL, so that /pix
is not added to your URL route.
PUT /v2/webhook/:chave
Requires authorization for the scope: webhook.write
Request
{
"webhookUrl": "https://exemplo-pix/webhook"
}
{
"webhookUrl": "https://exemplo-pix/webhook?ignorar="
}
Response The Responses below represent Success(201) and consumption failures/errors.
Webhook for notifications about Pix received associated with a txid.
InvalidValueError
{
"nome": "valor_invalido",
"mensagem": "URL inválida"
}
Or
{
"nome": "valor_invalido",
"mensagem": "A URL do webhook deve usar o protocolo HTTPS"
}
Or
{
"nome": "webhook_invalido",
"mensagem": "A autenticação de TLS mútuo não está configurada na URL informada"
}
Or
{
"nome": "webhook_invalido",
"mensagem": "A URL informada está inacessível"
}
Or
{
"nome": "webhook_invalido",
"mensagem": "A URL informada atingiu o tempo limite de resposta"
}
Or
{
"nome": "webhook_invalido",
"mensagem": "A requisição na URL informada falhou com o erro: {{errno}}"
}
Or
{
"nome": "webhook_invalido",
"mensagem": "A URL informada respondeu com o código HTTP {{httpStatus}}"
}
Or
{
"nome": "webhook_invalido",
"mensagem": "Não foi possível receber uma resposta da URL informada"
}
Endpoint for retrieving information about the pix webhook .
GET /v2/webhook/:chave
Requires authorization for the scope: webhook.read
Response The responses below represent Consumption Success(200).
{
"webhookUrl": "https://gn-pix-webhook.gerencianet.com.br/webhook/",
"chave": "40a0932d-1918-4eee-845d-35a2da1690dc",
"criacao": "2020-11-11T10:15:00.358Z"
}
Get list of webhooks
Endpoint to retrieve webhooks associated with keys through parameters such as inicio
and fim
. Attributes are entered as query params.
GET /v2/webhook
Requires authorization for the scope: webhook.read
Request
The snippet below shows how the
inicio
and
fim
parameters(mandatory) should be passed in the request.
/v2/webhook/?inicio=2020-10-22T16:01:35Z&fim=2020-10-23T16:01:35Z
Response The responses below represent Success(200) and consumption failures/errors.
{
"parametros": {
"inicio": "2021-01-22T16:01:35.000Z",
"fim": "2022-12-30T16:01:35.000Z",
"paginacao": {
"paginaAtual": 0,
"itensPorPagina": 100,
"quantidadeDePaginas": 1,
"quantidadeTotalDeItens": 5
}
},
"webhooks": [
{
"webhookUrl": "https://seudominio.com.br/gn/webhook/",
"chave": "40a0932d-1618-4eee-845d-35a2da1590dc",
"criacao": "2021-05-05T19:52:13.000Z"
},
{
"webhookUrl": "https://.projetosseudominio.seudominio.com.br/gn/webhook/",
"chave": "40a0932d-1918-0eee-845d-35a2da1690dc",
"criacao": "2021-10-18T14:42:41.000Z"
},
{
"webhookUrl": "https://seudominio.com.br/webhook/?ignorar=",
"chave": "40a0032d-1918-45ee-845d-3562da1690dc",
"criacao": "2021-11-03T12:25:15.000Z"
}
]
}
{
"nome": "valor_invalido",
"mensagem": "Campo de data fim deve ser maior ou igual ao campo de data inicio"
}
Cancel Pix webhook
Endpoint for canceling the pix webhook.
DELETE /v2/webhook/:chave
Requires authorization for the scope: webhook.write
Response The response below represents Success (204) of consumption.
Webhook for Pix notifications has been canceled.
Resend Pix webhook
Endpoint that allows you to resend the pix webhook.
Attention!It is possible to request the resending of Webhooks for transactions that took place from 27/12 at 10:00 a.m.
The webhook resend for a transaction is available for a maximum of 30 days.
The resend attempt takes place once for each webhook, there is NO rescheduling as there is with normal sending. If the customer's server is down, the customer will have to request the resend again.
In the case of webhooks, the resend attempt is made once for each webhook.
In the case of return webhooks (received and sent), a webhook is sent with the entire return array instead of one webhook per return. For example, if you make two returns related to the same endToEndId, you will receive two separate webhooks when sending. However, when you request a resend, you will only receive one webh
POST /v2/gn/webhook/reenviar
Requires authorization for the scope: webhook.write
Request
{
"tipo": "PIX_RECEBIDO",
"e2eids": [
"E09089356202412261300API229e5352",
"E09089356202412261300API3149af57"
]
}
Responses The Responses below represent Success(202) and consumption failures/errors.
{
"type": "https://pix.bcb.gov.br/api/v2/error/Desconhecido",
"title": "Operação Inválida",
"status": 400,
"detail": "A requisição que busca reenviar os webhooks não respeita o schema ou está semanticamente errada.",
"violacoes": [
{
"razao": "O campo reenviarWebhook.tipo deve ser um dos seguintes valores: PIX_RECEBIDO, PIX_ENVIADO, DEVOLUCAO_RECEBIDA, DEVOLUCAO_ENVIADA.",
"propriedade": "reenviarWebhook.body.tipo"
}
]
Ou
{
"razao": "O array reenviarWebhook.e2eids deve conter pelo menos 1 e2eid.",
"propriedade": "reenviarWebhook.body.e2eids"
}
Ou
{
"razao": "O array reenviarWebhook.e2eids contém itens duplicados.",
"propriedade": "reenviarWebhook.body.e2eids"
}
Ou
{
"razao": "O array reenviarWebhook.e2eids deve conter no máximo 1000 e2eids.",
"propriedade": "reenviarWebhook.body.e2eids"
}
Ou
{
"razao": "O objeto reenviarWebhook.body deve conter o campo tipo.",
"propriedade": "reenviarWebhook.body"
}
Ou
{
"razao": "O objeto reenviarWebhook.body deve conter o campo e2eids.",
"propriedade": "reenviarWebhook.body"
}
Ou
{
"razao": "O objeto reenviarWebhook.body não respeita o schema.",
"propriedade": "reenviarWebhook.body"
}
}
{
"type": "https://pix.bcb.gov.br/api/v2/error/WebhooksOperacaoInvalida",
"title": "Operação Inválida",
"status": 422,
"detail": "A requisição que busca solicitar o reenvios de webhooks não respeita o schema ou está semanticamente errada.",
"violacoes": [
{
"razao": "Nenhuma devolução foi encontrada para os e2eids informados.",
"propriedade": "body.e2eIds"
}
]
}
{
"type": "https://pix.bcb.gov.br/api/v2/error/WebhooksErroInterno",
"title": "Erro Interno",
"status": 500,
"detail": "Funcionalidade desabilitada em ambiente de homologação."
}
Receiving Callbacks
This service is protected by a layer of mTLS authentication. Callbacks are sent by Efí via POST url-registered-webhook/pix
when there is a change in the Pix status.
InformationTo test Pix Cob and Pix CobV charge endpoints in the sandbox environment, you can simulate all status changes returned by our API and webhook.
Charges with values between R$ 0.01 and R$ 10.00 are confirmed, and you will receive the information via webhook.
Charges with values above R$ 10.00 remain active, without confirmation, and there are no webhooks for these cases.
Request
When there is a change in the status of a Pix transaction associated with the registered key, Efí sends a POST
request to the webhook URL you defined. A JSON object (like the examples below) will be sent to your server. Each callback request has a timeout of 60 seconds, meaning it is interrupted if there is no response within 60 seconds.
Examples:
Below, see some examples of the JSON object sent.
{
"pix": [
{
"endToEndId": "E1803615022211340s08793XPJ",
"txid": "fc9a43k6ff384ryP5f41719",
"chave": "2c3c7441-b91e-4982-3c25-6105581e18ae",
"valor": "0.01",
"horario": "2020-12-21T13:40:34.000Z",
"infoPagador": "pagando o pix"
}
]
}
{
"pix": [
{
"endToEndId": "E1803615022211340s08793XPJ",
"txid": "fc9a43k6ff384ryP5f41719",
"chave": "2c3c7441-b91e-4982-3c25-6105581e18ae",
"valor": "0.01",
"horario": "2020-12-21T13:40:34.000Z",
"infoPagador": "pagando o pix",
"gnExtras": {
"pagador": {
"nome": "GORBADOCK OLDBUCK",
"cpf": "***.123.456-**",
"codigoBanco":"00416968"
}
}
}
]
}
{
"pix": [
{
"endToEndId": "E090893562024101648554e991d24ccb",
"txid": "c547f5f498c0420a9d1db5970a0d34c3",
"chave": "ca179e79-df8a-4717-b915-549f1de4d31d",
"valor": "0.01",
"horario": "2024-10-16T18:56:36.000Z",
"gnExtras": {
"pagador": {
"nome": "EFI SA - INSTITUICAO DE PAGAMENTO",
"cnpj": "09089356000118",
"codigoBanco": "09089356"
}
}
}
]
}
{
"pix": [
{
"endToEndId": "E1803615022211340s08793XPJ",
"txid": "fc9a43k6ff384ryP5f41719",
"chave": "2c3c7441-b91e-4982-3c25-6105581e18ae",
"valor": "0.01",
"horario": "2020-12-21T13:40:34.000Z",
"infoPagador": "Teste",
"gnExtras": {
"split": {
"id": "f659e882b00440ef9f07538fb697a6b2",
"revisao": 0
}
}
}
]
}
{
"pix": [
{
"endToEndId": "E1803615022211340s08793XPJ",
"txid": "fc9a43k6ff384ryP5f41719",
"chave": "2c3c7441-b91e-4982-3c25-6105581e18ae",
"valor": "0.10",
"horario": "2020-12-21T13:40:34.000Z",
"infoPagador": "pagando o pix",
"gnExtras": {
"tarifa": "0.01"
}
}
]
}
{
"pix": [
{
"endToEndId": "E12345678202009091221syhgfgufg",
"txid": "c3e0e7a4e7f1469a9f782d3d4999343c",
"chave": "2c3c7441-b91e-4982-3c25-6105581e18ae",
"valor": "110.00",
"horario": "2020-09-09T20:15:00.358Z",
"infoPagador": "0123456789",
"devolucoes":[
{
"id": "123ABC",
"rtrId": "D12345678202009091221abcdf098765",
"valor": "110.00",
"natureza": "MED_FRAUDE",
"horario": {
"solicitacao": "2020-09-09T20:15:00.358Z"
},
"status": "DEVOLVIDO"
}
]
}
]
}
{
...
"devolucoes": [
{
...,
"status": "NAO_REALIZADO",
"motivo": "Saldo insuficiente para realizar a devolução."
}
]
...
}
{
"pix": [
{
"endToEndId": "E090893562021030PIf25a7868",
"chave": "2c3c7441-b91e-4982-3c25-6105581e18ae",
"tipo": "SOLICITACAO",
"status": "REALIZADO",
"valor": "0.01",
"horario": "2021-03-04T20:39:47.000Z",
"gnExtras": {
"idEnvio": "123ABC"
}
}
]
}
{
"pix": [
{
"endToEndId": "E09089356202501031120API37548077",
"chave": "[email protected]",
"tipo": "SOLICITACAO",
"status": "REALIZADO",
"valor": "0.01",
"horario": "2025-01-03T11:20:41.000Z",
"infoPagador": "Envio de Pix Id: t9tKDtGL7SYrp4j8NypgfnKPeIx5v",
"gnExtras": {
"favorecido": {
"nome": "NOME DO FAVORECIDO",
"cpf": "***.123.456-**",
"contaBanco": {
"codigoBanco": "60746948",
"agencia": "1234",
"conta": "1234567",
"tipoConta": "cacc"
}
},
"idEnvio": "t9tKDtGL7SYrp4j8NypgfnKPeIx5v"
}
}
]
}
{
"pix": [
{
"endToEndId": "E090893562021030PIf25a7868",
"chave": "2c3c7441-b91e-4982-3c25-6105581e18ae",
"tipo": "SOLICITACAO",
"status": "NAO_REALIZADO",
"valor": "0.01",
"horario": null,
"infoPagador": "0123456789",
"gnExtras": {
"idEnvio": "123ABC",
"erro": {
"codigo": "AC03",
"origem": "PSP do usuário recebedor",
"motivo": "Número da agência e/ou conta transacional do usuário recebedor inexistente ou inválido"
}
}
}
]
}
ResponseCallback requests wait for a response with HTTP status 2XX. If the client's server returns a different status, Efí will make up to 9 new notification attempts. The first attempt occurs immediately. The rescheduling time starts from the second attempt, following a progressive scale. After the ninth attempt, no new submissions will be made, as shown in the table below.
N° of attempts | Time (in minutes) | Time (in seconds) |
---|
1 | Immediate | 0 |
2 | 5 | 300 |
3 | 5 | 300 |
4 | 5 | 300 |
5 | 10 | 600 |
6 | 20 | 1200 |
7 | 40 | 2400 |
8 | 80 | 4800 |
9 | 160 | 9600 |
TipAfter the ninth attempt, as there will be no more automatic resends, you can use the resend Pix webhook endpoint, if necessary.