This section gathers endpoints for managing notifications by the receiving PSP to the receiving end user.
mTLS ValidationTo use the Account Opening API, your server must have a minimum version of TLS 1.2 and comply with the mTLS communication standard.
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 are intended to exemplify the settings required on your server so that it is possible 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.
Configures webhook for the Account Opening API.
POST /v1/webhook
Requires authorization for the scope: gn.registration.webhook.write
Request
{
"webhookUrl": "https://sejaefi.com.br/meu-webhook"
}
{
"url": "https://sejaefi.com.br/meu-webhook",
"chave": "92ecc0a8-9631-4601-a188-feacf8288c13"
}
Responses The responses below represent Success(200) and consumption failures/errors.
{
"identificadorWebhook": "92ecc0a8-9631-4601-a188-feacf8288c13"
}
URL inválido
{
"nome": "url_webhook",
"mensagem": "A URL informada para webhook é inválida."
}
Or
Invalid protocol
{
"nome": "protocolo_da_url",
"mensagem": "A URL do webhook deve usar o protocolo HTTPS."
}
Or
mTLS not configured
{
"nome": "mtls_nao_configurado",
"mensagem": "A autenticação TLS mútua não está configurada no URL fornecido."
}
Or
Inaccessible URL
{
"nome": "url_inacessivel",
"mensagem": "A URL informada está inacessível."
}
Or
URL timeout
{
"nome": "limite_de_tempo_atingido_para_url",
"mensagem": "A URL informada atingiu o tempo limite de resposta."
}
Or
URL failed with error
{
"nome": "url_falhou_com_erro",
"mensagem": "A requisição na URL informada falhou com o erro."
}
Or
Non-HTTPS protocol
{
"nome": "protocolo_da_url",
"mensagem": "A URL do webhook deve usar o protocolo HTTPS."
}
Or
URL responds to HTTP
{
"nome": "erro_de_codigo",
"mensagem": "A URL informada respondeu com o código HTTP."
}
Or
URL not responding
{
"nome": "url_informada_nao_existe",
"mensagem": "Não foi possível receber uma resposta da URL informada."
}
Or
Webhook not found
{
"nome": "chave_invalida",
"mensagem": "Não foi possível encontrar um webhook com a chave informada."
}
Or
Registered URL
{
"nome": "url_cadastrada",
"mensagem": "A URL já foi cadastrada."
}
This error occurs in the following situation:
* Integrator does not have permission for the service scope required to consume this endpoint.
{
"nome": "erro_aplicacao",
"mensagem": "Ocorreu um erro na aplicação."
}
Detail webhook
Details the webhook configured for the Account Opening API.
GET /v1/webhook/:identificadorWebhook
Requires authorization for the scope: gn.registration.webhook.read
Responses The responses below represent Success(200) and consumption failures/errors.
{
"webhookUrl": "https://sejaefi.com.br/meu-webhook",
"identificadorWebhook": "92ecc0a8-9631-4601-a188-feacf8288c13",
"criacao": "2021-10-26T11:23:35.000Z"
}
{
"nome": "identificador_webhook",
"mensagem": "Não foi possível encontrar um webhook com o identificador webhook informado."
}
Unauthorized. This error occurs in the following situation:
* Integrator does not have permission for the service scope required to consume this endpoint.
{
"nome": "erro_aplicacao",
"mensagem": "Ocorreu um erro na aplicação."
}
List webhooks
Lists webhooks configured for the Account Opening API.
GET /v1/webhooks
Requires authorization for the scope: gn.registration.webhook.read
Request
The request sent to this endpoint doesn't need a body, just the OAuth authorization headers, parameters and the account certificate.
Responses The responses below represent Success(200) and consumption failures/errors.
Success
{
"parametros": {
"inicio": "2020-04-01T00:00:00.000Z",
"fim": "2020-04-01T23:59:59.000Z",
"paginacao": {
"paginaAtual": 0,
"itensPorPagina": 100,
"quantidadeDePaginas": 1,
"quantidadeTotalDeItens": 1
}
},
"webhooks": [
{
"webhookUrl": "https://sejaefi.com.br/meu-webhook",
"identificadorWebhook": "92ecc0a8-9631-4601-a188-feacf8288c13",
"criacao": "2021-10-26T11:23:35.000Z"
}
]
}
{
"nome": "data_invalida",
"mensagem": "Campo de data fim deve ser maior ou igual ao campo de data inicio."
}
Unauthorized. This error occurs in the following situation:
* Integrator does not have permission for the service scope required to consume this endpoint.
{
"nome": "erro_aplicacao",
"mensagem": "Ocorreu um erro na aplicação."
}
Cancel webhook
Cancels the webhook configured for the Account Opening API.
DELETE /v1/webhook/:identificadorWebhook
Requires authorization for the scope: gn.registration.webhook.write
Request
The request sent to this endpoint doesn't need a body, just the authorization headers, OAuth, parameter and the account certificate.
Responses The responses below represent Success(204) and consumption failures/errors.
No content
* Webhook configured for registration has been canceled.
{
"nome": "identificador_webhook",
"mensagem": "Não foi possível encontrar um webhook com o identificador webhook informado."
}
Unauthorized. This error occurs in the following situation:
* Integrator does not have permission for the service scope required to consume this endpoint.
{
"nome": "erro_aplicacao",
"mensagem": "Ocorreu um erro na aplicação."
}
Receiving Callbacks
This service is protected by an mTLS authentication layer. Callbacks are sent by Efí via POST url-webhook-registered.
Request
When opening an account or if the end customer declines the account opening, Efí will send 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 terminated if there is no response within 60 seconds.
Examples:
Below are some examples of the JSON object sent.
- Open account
- The final customer refused to open an account
{
"contaSimplificada": {
"identificador": string,
},
"evento": "conta_aberta"
}
{
"contaSimplificada": {
"identificador": string,
},
"evento": "conta_recusada_pelo_cliente_final"
}
ResponsesCallback requests wait for a response with HTTP status 2XX. If the client's server returns a different status, Efí will make up to 10 new notification attempts. The first new attempt will be made 5 minutes after the failure to send the callback. If the error persists, subsequent attempts will be sent at increasingly longer intervals, as shown in the table below.
| N° of attempts | Time (in minutes) |
|---|
| 1 | 5 |
| 2 | 10 |
| 3 | 20 |
| 4 | 40 |
| 5 | 80 |
| 6 | 160 |
| 7 | 320 |
| 8 | 640 |
| 9 | 1280 |
| 10 | 52560 |