Guia do Gateway USSD
← Voltar para a Documentação Principal
Este guia cobre o Gateway USSD do OmniSS7, que conecta diálogos USSD SS7/MAP a callbacks HTTP/JSON, permitindo que desenvolvedores de terceiros construam aplicações USSD com um simples endpoint HTTP.
Índice
- Visão Geral
- Arquitetura
- Habilitando o Gateway USSD
- Configuração
- Protocolo de Callback HTTP
- USSD Originado na Rede (Push API)
- Ciclo de Vida da Sessão
- Tratamento de Erros
- Métricas e Monitoramento
- Servidor de Callback de Exemplo
- Solução de Problemas
Visão Geral
O Gateway USSD lida com duas direções de tráfego USSD:
- Originado pelo Móvel (Entrada) — Um assinante disca um código curto (por exemplo,
*100#). O gateway recebe oprocessUnstructuredSS-RequestMAP (opcode 59), o encaminha para seu callback HTTP e retransmite sua resposta de volta via SS7. - Originado na Rede (Saída) — Sua aplicação envia uma mensagem USSD para um assinante via a API REST. O gateway envia um
unstructuredSS-RequestMAP (opcode 60) via SS7 e roteia a resposta do assinante para seu callback.
Ambas as direções suportam diálogos de múltiplas interações — menus interativos onde o assinante responde e recebe prompts de acompanhamento.
Características Principais
| Propriedade | Valor |
|---|---|
| Transporte | HTTP POST síncrono por interação |
| Codificação | Alfabeto padrão GSM de 7 bits (DCS 0x0F) conforme 3GPP TS 23.038 |
| Comprimento máximo do texto | 182 caracteres (configurável) |
| Rastreamento de sessão | UUID gerado pelo gateway por diálogo |
| Autenticação | Nenhuma (confia na rede SS7) |
| Roteamento | Correspondência de prefixo de código curto aos URLs de callback |
Referências 3GPP
| Especificação | Relevância |
|---|---|
| 3GPP TS 23.090 | USSD Fase 2 — arquitetura e procedimentos |
| 3GPP TS 24.090 | USSD Fase 3 — detalhes do protocolo |
| 3GPP TS 29.002 | Protocolo MAP — USSD-Arg, USSD-Res, opcodes 59/60/61 |
| 3GPP TS 23.038 | Alfabeto padrão GSM de 7 bits e esquema de codificação de dados |
Arquitetura
Fluxo Originado pelo Móvel (Entrada)
Fluxo Originado na Rede (Push Saída)
Visão Geral dos Componentes
Habilitando o Gateway USSD
O Gateway USSD requer que o modo Cliente MAP esteja habilitado, além de sua própria flag de recurso.
config :omniss7,
map_client_enabled: true,
ussd_gateway_enabled: true
O gateway também requer uma conexão M3UA funcional (veja Guia do Cliente MAP para configuração do M3UA).
Configuração
Parâmetros do Gateway USSD
config :omniss7,
ussd_gateway_enabled: true,
ussd_gateway: %{
# Roteamento de código curto — correspondência de prefixo mais longo
routes: [
%{pattern: "*100", url: "http://balance-app:9000/ussd"},
%{pattern: "*200", url: "http://topup-app:9000/ussd"},
%{pattern: "*", url: "http://default-app:9000/ussd"}
],
# Timeouts de sessão
session_timeout_ms: 180_000, # Duração total da sessão (3 minutos)
turn_timeout_ms: 30_000, # Tempo máximo de espera pela resposta do assinante por interação (30 segundos)
# Configurações de callback HTTP
http_timeout_ms: 5_000, # Timeout para HTTP POST para seu aplicativo (5 segundos)
# Limites de texto
max_text_length: 182 # Máximo de 7 bits GSM (trunca com aviso se excedido)
}
Referência de Parâmetros
| Parâmetro | Tipo | Obrigatório | Padrão | Descrição |
|---|---|---|---|---|
ussd_gateway_enabled | Booleano | Sim | false | Interruptor mestre para o recurso Gateway USSD |
ussd_gateway.routes | Lista de Mapas | Sim | [] | Regras de roteamento de prefixo de código curto. Cada entrada tem pattern (string de prefixo) e url (URL de callback). A correspondência de prefixo mais longo vence. |
ussd_gateway.session_timeout_ms | Inteiro | Não | 180_000 | Duração total máxima da sessão em milissegundos. A sessão é encerrada com um erro se excedida. |
ussd_gateway.turn_timeout_ms | Inteiro | Não | 30_000 | Tempo máximo para esperar pela resposta do assinante em um diálogo de múltiplas interações, em milissegundos. |
ussd_gateway.http_timeout_ms | Inteiro | Não | 5_000 | Timeout de requisição HTTP para callbacks para sua aplicação, em milissegundos. Cobre tanto o tempo de conexão quanto o tempo de resposta. |
ussd_gateway.max_text_length | Inteiro | Não | 182 | Máximo de caracteres em uma string de texto USSD. Textos que excedem isso são truncados e um aviso é registrado. |
Parâmetros de Roteamento
Cada entrada na lista routes é um mapa:
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
pattern | String | Sim | Prefixo de código curto a ser correspondido. Use "*" como um fallback abrangente. Prefixos mais longos têm prioridade. |
url | String | Sim | URL do endpoint HTTP para receber POSTs de callback para códigos curtos correspondentes. |
Correspondência de Roteamento
As rotas são correspondidas por prefixo mais longo primeiro. Para a string de discagem *100#:
"*100"corresponde (comprimento 4) — selecionado"*10"corresponde (comprimento 3) — ignorado, mais curto"*"corresponde (comprimento 1) — fallback
Se nenhuma rota corresponder, o gateway retorna um erro MAP para o móvel e registra um aviso.
Protocolo de Callback HTTP
Sua aplicação recebe requisições HTTP POST do gateway e responde com instruções JSON.
Requisição do Gateway para Sua Aplicação
Content-Type: application/json
Primeira interação (iniciação da sessão):
{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"msisdn": "+254712345678",
"type": "initiation",
"text": "*100#",
"turn": 1
}
Interações subsequentes (assinante respondeu):
{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"msisdn": "+254712345678",
"type": "response",
"text": "1",
"turn": 2
}
Campos da Requisição
| Campo | Tipo | Descrição |
|---|---|---|
session_id | String | UUID gerado pelo gateway. Único por diálogo USSD. Use isso para correlacionar interações. |
msisdn | String | MSISDN do assinante (se disponível na mensagem MAP). Pode estar vazio para algumas redes. |
type | String | "initiation" para a primeira interação, "response" para respostas subsequentes do assinante. |
text | String | A string de discagem (por exemplo, *100#) na iniciação, ou a entrada do assinante (por exemplo, 1) na resposta. |
turn | Inteiro | Contador de interações começando em 1. Incrementa com cada interação do assinante. |
Resposta da Sua Aplicação
Sua aplicação deve responder com JSON contendo uma action e text:
Continuar (mostrar menu, esperar pela entrada do assinante):
{
"action": "continue",
"text": "1. Saldo\n2. Recarregar\n3. Transferir"
}
Encerrar (mostrar mensagem final, fechar sessão):
{
"action": "end",
"text": "Seu saldo é $5.00"
}
Campos da Resposta
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
action | String | Sim | "continue" para manter a sessão aberta e esperar pela entrada do assinante, ou "end" para exibir uma mensagem final e fechar a sessão. |
text | String | Sim | Texto a ser exibido no dispositivo do assinante. Comprimento máximo regido por max_text_length (padrão 182). Use \n para quebras de linha. |
USSD Originado na Rede (Push API)
Envie uma mensagem USSD para um assinante a partir de sua aplicação.
Endpoint
POST /api/ussd/send
Requisição
{
"msisdn": "+254712345678",
"text": "Você tem uma fatura pendente. Responda 1 para pagar.",
"callback_url": "http://billing-app:9000/ussd"
}
Campos da Requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
msisdn | String | Sim | MSISDN do assinante de destino no formato internacional. |
text | String | Sim | Texto inicial USSD a ser exibido. Codificado como GSM de 7 bits. |
callback_url | String | Sim | URL para receber a resposta do assinante via o protocolo de callback padrão. |
Resposta
Sucesso (200 OK):
{
"session_id": "xyz-789-abc-123",
"status": "sent"
}
Respostas de erro:
| Status HTTP | Corpo | Causa |
|---|---|---|
| 400 | {"error": "invalid request", "required": ["msisdn", "text", "callback_url"]} | Faltando um ou mais campos obrigatórios, ou o corpo não é um JSON válido |
| 500 | {"error": "{:gsm7_encode_failed, ...}"} | O texto contém caracteres que não estão no alfabeto GSM de 7 bits. A falha de codificação é apresentada através do ramo de erro genérico, então o status é 500, não 400. |
| 500 | {"error": "..."} | Qualquer outra falha de envio (por exemplo, conectividade M3UA). O campo error contém a razão interna inspecionada. |
| 503 | {"error": "USSD gateway not enabled"} | ussd_gateway_enabled é false |
Nota sobre erros de validação vs. envio: Problemas de validação de campo (chaves faltando, JSON malformado) retornam 400
invalid request. Uma vez que a validação passa, qualquer falha do caminho de envio — incluindo falhas de codificação GSM de 7 bits — é relatada como 500 com a razão inspecionada no campoerror.
Exemplo cURL
curl -X POST http://localhost:8080/api/ussd/send \
-H "Content-Type: application/json" \
-d '{
"msisdn": "+254712345678",
"text": "Você tem uma fatura pendente. Responda 1 para pagar.",
"callback_url": "http://billing-app:9000/ussd"
}'
Ciclo de Vida da Sessão
Cada diálogo USSD é rastreado como uma sessão com um session_id único.
Estados da Sessão
Sessão de Uma Interação
Se seu callback retornar "end" na primeira interação, nenhuma sessão persistente é criada. O gateway envia um MAP End com o resultado e retorna imediatamente.
Sessão de Múltiplas Interações
Se seu callback retornar "continue", o gateway:
- Cria um Session GenServer registrado em
UssdGateway.Registry - Envia um MAP Continue com opcode 60 (unstructuredSS-Request) para o móvel
- Espera pela resposta do assinante (até
turn_timeout_ms) - Encaminha a resposta para seu callback
- Repete até que seu callback retorne
"end"ou ocorra um timeout
Comportamento de Timeout
| Timeout | Padrão | Efeito |
|---|---|---|
| Timeout de interação | 30 segundos | Se o assinante não responder dentro dessa janela, a sessão é encerrada com um erro MAP. |
| Timeout de sessão | 3 minutos | Duração total da sessão. Encerra a sessão independentemente da atividade. |
| Timeout de callback HTTP | 5 segundos | Se sua aplicação não responder a tempo, o gateway envia um erro MAP para o móvel e encerra a sessão. |
Tratamento de Erros
O gateway lida com falhas de forma graciosa e sempre tenta enviar uma resposta de erro MAP para o móvel, para que o assinante veja uma mensagem significativa em vez de um timeout de rede.
| Cenário | Ação do Gateway | Código de Erro MAP |
|---|---|---|
| Timeout de callback HTTP ou 5xx | Encerrar sessão, enviar MAP End com erro | 34 (systemFailure) |
| JSON inválido do callback | Encerrar sessão, enviar MAP End com erro | 34 (systemFailure) |
Texto USSD excede max_text_length | Truncar texto, registrar aviso, continuar normalmente | N/A (truncado, não é um erro) |
| Timeout do assinante (sem resposta) | Encerrar sessão, enviar MAP End com erro | 34 (systemFailure) |
| Nenhuma rota corresponde ao código curto | Enviar MAP End com erro, registrar aviso | 34 (systemFailure) |
| Falha do Session GenServer | Sessão morre, assinante vê timeout de rede | N/A (saída do processo) |
| Gateway USSD não habilitado | Retornar Facility Not Supported | 21 (facilityNotSupported) |
Métricas e Monitoramento
O Gateway USSD expõe métricas Prometheus no endpoint padrão /metrics (porta 8080).
Métricas USSD
Métrica: ussd_requests_total
Tipo: Contador
Descrição: Total de requisições USSD processadas
Rótulos:
direction—"inbound"(originado pelo móvel) ou"outbound"(push originado na rede)
Métrica: ussd_active_sessions
Tipo: Gauge
Descrição: Declarado como um gauge para o número de sessões USSD ativas.
Caveat: Este gauge é declarado, mas nunca atualizado na versão atual — ele sempre lê
0. Não confie nele para contagens de sessões ao vivo. Acompanhe a atividade da sessão viaussd_requests_totalem vez disso.
Métrica: map_request_duration_milliseconds
Tipo: Histograma
Descrição: Duração das operações de envio USSD em milissegundos
Rótulos:
operation—"ussd_send"para requisições de push de saída
Exemplos de Consultas Prometheus
# Taxa de requisições USSD por direção (entrada vs saída)
rate(ussd_requests_total[5m])
# Latência USSD de saída (p95)
histogram_quantile(0.95, rate(map_request_duration_milliseconds_bucket{operation="ussd_send"}[5m]))
Servidor de Callback de Exemplo
Python (Flask)
from flask import Flask, request, jsonify
app = Flask(__name__)
sessions = {}
@app.route('/ussd', methods=['POST'])
def ussd():
data = request.json
session_id = data['session_id']
text = data['text']
turn = data['turn']
if data['type'] == 'initiation':
sessions[session_id] = {'state': 'main_menu'}
return jsonify({
'action': 'continue',
'text': 'Bem-vindo!\n1. Ver saldo\n2. Comprar crédito\n3. Transferir'
})
state = sessions.get(session_id, {}).get('state')
if state == 'main_menu':
if text == '1':
del sessions[session_id]
return jsonify({
'action': 'end',
'text': 'Seu saldo é $5.00'
})
elif text == '2':
sessions[session_id]['state'] = 'buy_airtime'
return jsonify({
'action': 'continue',
'text': 'Digite o valor:'
})
else:
del sessions[session_id]
return jsonify({
'action': 'end',
'text': 'Opção inválida. Adeus.'
})
elif state == 'buy_airtime':
del sessions[session_id]
return jsonify({
'action': 'end',
'text': f'Você comprou ${text} de crédito. Obrigado!'
})
return jsonify({'action': 'end', 'text': 'Sessão expirada.'})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=9000)
Node.js (Express)
const express = require('express');
const app = express();
app.use(express.json());
const sessions = new Map();
app.post('/ussd', (req, res) => {
const { session_id, text, type } = req.body;
if (type === 'initiation') {
sessions.set(session_id, { state: 'main_menu' });
return res.json({
action: 'continue',
text: 'Bem-vindo!\n1. Ver saldo\n2. Comprar crédito'
});
}
const session = sessions.get(session_id);
if (!session) {
return res.json({ action: 'end', text: 'Sessão expirada.' });
}
if (session.state === 'main_menu' && text === '1') {
sessions.delete(session_id);
return res.json({ action: 'end', text: 'Seu saldo é $5.00' });
}
sessions.delete(session_id);
return res.json({ action: 'end', text: 'Adeus.' });
});
app.listen(9000, () => console.log('Callback USSD na porta 9000'));
Solução de Problemas
O Discar USSD Retorna "Serviço Não Disponível"
Sintomas: O assinante disca um código curto e imediatamente recebe um erro de rede.
Possíveis causas:
ussd_gateway_enabledéfalse- Nenhuma rota corresponde ao código curto discado
- Conexão M3UA está inativa
Resolução:
- Verifique
ussd_gateway_enabled: truena configuração - Verifique se um padrão de rota corresponde ao código curto (lembre-se de incluir
*como catch-all) - Verifique o status do par M3UA na interface web (página de Pares)
Callback Não Recebendo Requisições
Sintomas: Os logs do gateway mostram início USSD, mas sua aplicação nunca recebe o POST HTTP.
Possíveis causas:
- URL de callback é inacessível a partir do host OmniSS7
- Firewall bloqueando HTTP de saída do OmniSS7
- Aplicação de callback não está em execução
Resolução:
- Teste a conectividade:
curl -v http://your-app:9000/ussda partir do host OmniSS7 - Verifique as regras do firewall para HTTP de saída
- Verifique se sua aplicação de callback está escutando na porta configurada
Sessões Expirando Prematuramente
Sintomas: Sessões de múltiplas interações terminam com "systemFailure" antes que o assinante possa responder.
Possíveis causas:
turn_timeout_msé muito curto para sua base de assinanteshttp_timeout_msé muito curto para o tempo de processamento da sua aplicação- Latência de rede entre OmniSS7 e seu servidor de callback
Resolução:
- Aumente
turn_timeout_ms(o padrão de 30 segundos deve ser suficiente para a maioria dos casos) - Aumente
http_timeout_msse sua aplicação precisar de mais tempo de processamento - Implemente o servidor de callback próximo ao OmniSS7 para reduzir a latência
Erros de Codificação GSM de 7 bits
Sintomas: Erros gsm7_encode_failed nos logs, ou uma resposta 500 de /api/ussd/send cujo campo error contém {:gsm7_encode_failed, ...}.
Possíveis causas:
- O texto contém caracteres fora do alfabeto padrão GSM de 7 bits (por exemplo, emoji, caracteres CJK)
Resolução:
- Restringir o texto USSD ao conjunto básico de caracteres GSM: letras ASCII, dígitos, pontuação comum e alguns caracteres gregos/nórdicos
- Veja 3GPP TS 23.038 Seção 6.2.1 para a tabela completa de caracteres
Documentação Relacionada
- Guia da API — Referência completa da API REST (todos os endpoints, incluindo
/api/ussd/send) - Guia do Cliente MAP — Configuração da conexão M3UA necessária para USSD
- Referência de Configuração — Todos os parâmetros de configuração
- Guia de Recursos Comuns — Interface web, monitoramento e configuração do Prometheus