WappSync Phone Verify - Documentação da API
Verificação por telefone em que seu usuário envia um código para nós via WhatsApp - sem digitar OTP, um toque, totalmente verificado.
Conteúdo
Como funciona
Você solicita um código
Seu backend chama nossa API com o número de telefone do usuário. Retornamos um código exclusivo e o número do WhatsApp para o qual enviá-lo.
O usuário o envia pelo WhatsApp
Seu usuário toca em um link do WhatsApp - o código está pré-preenchido. Sem digitar, sem copiar e colar. Ele apenas toca em enviar.
Verificado instantaneamente
Recebemos a mensagem, vemos de qual número do WhatsApp ela foi enviada e sua consulta retorna auth=1 com o telefone certificado. Pronto!
Obtenha suas credenciais
Cadastre-se em wappsync.com/mybusiness para receber:
| Credencial | Descrição |
|---|---|
businessid | Seu ID de empresa numérico |
businesskey | Sua chave secreta - nunca compartilhe, nunca coloque no código do lado do cliente |
businesskey é usada para assinatura HMAC e nunca deve aparecer em JavaScript do navegador, no código-fonte HTML ou no código do aplicativo móvel.Dois modos de verificação
O Phone Verify funciona em dois modos, escolhidos por solicitação. Em ambos, seu usuário envia o código para nós via WhatsApp - a única diferença é qual número de telefone é certificado.
confirm_phone (default) | verify_sender | |
|---|---|---|
| Você envia | um número de telefone N | nada (nenhum número) |
| O que é certificado | que o solicitante controla N especificamente | qualquer número que realmente devolva o código |
| Uso típico | login / recuperação em um número cadastrado, proteção contra troca de SIM | cadastro novo - "me dê um telefone real e comprovado para este cliente" |
| Entrega do código | Enviado pelo WhatsApp para N e exibido na sua interface (delivery: pushed_to_phone) | somente através de você (delivery: relay_by_business) - você entrega o código ao seu cliente |
| Resultado da consulta | sender_matches_intended informa se o remetente é N | phoneEnc é o número certificado; sender_matches_intended é omitido |
confirm_phone prova o controle de um número declarado específico; verify_sender prova o controle de algum número e informa qual. Se o seu fluxo exige que "o número nesta conta realmente pertence ao usuário", fique com confirm_phone e on_mismatch: reject.Telefone corrigido (on_mismatch): um caso comum é o cliente declarar A mas enviar o código de B (erro de digitação / dual-SIM). Em confirm_phone, reject (padrão) trata isso como uma tentativa falha; accept_sender certifica o remetente real B em vez disso (sender_matches_intended: false, phoneEnc = B), e seu backend decide se aceita o número corrigido B ou solicita um novo OTP para A.
Solicitar um OTP reverso
Chame isso do seu backend quando precisar verificar um usuário:
POSThttps://wappsync.com/phone-verify/api/getReverseOTP
Corpo da solicitação (JSON)
| Campo | Tipo | Descrição |
|---|---|---|
businessid | int | Seu ID de empresa |
businessref | string | Uma referência exclusiva que você gera para esta solicitação de autenticação (ex.: bin2hex(random_bytes(4))) |
phone | string | O número de telefone do usuário em formato internacional (+40755123456). Obrigatório no modo confirm_phone; omita ou deixe vazio para verify_sender. |
mode | string | Opcional - confirm_phone (padrão quando um telefone é fornecido) ou verify_sender (padrão quando o telefone é omitido) |
on_mismatch | string | Opcional (apenas confirm_phone) - reject (padrão) ou accept_sender (certifica o remetente real se o código chegar de um número diferente) |
ts | int | Timestamp Unix atual |
hmac | string | Assinatura HMAC-SHA256 |
Assinatura HMAC
Assine esta string com sua businesskey:
{businessid}|{businessref}|{phone}|{ts}
No modo verify_sender não há telefone, então a string assinada é {businessid}|{businessref}||{ts} (posição do meio vazia).
Exemplo:
$hmac = hash_hmac('sha256', "{$businessid}|{$businessref}|{$phone}|{$ts}", $businesskey);
Resposta
| Campo | Tipo | Descrição |
|---|---|---|
status | string | "ok" or "error" |
reverseotp_display | string | O código que seu usuário envia, ex.: "2FA-828161" |
waphone | string | O número do WhatsApp PARA o qual seu usuário envia o código |
logincode | string | Código interno a usar ao consultar |
in_ts | int | Timestamp para a solicitação de consulta |
out_ts | int | Timestamp da resposta |
otp_valid_minutes | int | Por quanto tempo o código é válido (padrão 10) |
mode | string | O modo ativo: confirm_phone ou verify_sender |
delivery | string | pushed_to_phone (confirm_phone: o código também é enviado pelo WhatsApp ao telefone declarado) ou relay_by_business (verify_sender: você mesmo deve entregar o código ao seu cliente) |
hmac | string | HMAC da resposta - verifique com sua businesskey |
Verificar o HMAC da resposta against {businessid}|{businessref}|{logincode}|{reverseotp_display}|{waphone}|{out_ts}.
O que mostrar ao seu usuário
Exiba o código e o número de telefone. Use um link whatsapp://send para que o usuário possa enviá-lo com um único toque:
<a href="whatsapp://send?phone=40725296320&text=2FA-828161">
💬 Send Code via WhatsApp
</a>
Consultar a verificação
Consultar a cada 2-3 segundos até que o usuário seja verificado:
POSThttps://wappsync.com/phone-verify/api/getReverseOTPStatus
Corpo da solicitação (JSON)
| Campo | Tipo | Descrição |
|---|---|---|
businessid | int | Seu ID de empresa |
businessref | string | A mesma referência da chamada anterior |
logincode | string | O logincode da resposta do getReverseOTP |
in_ts | int | O ts original da solicitação getReverseOTP |
hmac | string | HMAC-SHA256 de {businessid}|{businessref}|{logincode}|{in_ts} |
Resposta
| Campo | Tipo | Significado |
|---|---|---|
used | 0 | ⏳ Ainda aguardando - consulte novamente |
used | 1 | ✅ Acabou de ser verificado - a primeira consulta que vê isso o consome |
used | 2 | 🔒 Já consumido por uma consulta anterior |
auth | 1 | O usuário está verificado - faça o login dele! |
phoneEnc | string | Telefone certificado criptografado com AES-256-CBC (apenas quando auth=1) - o remetente quando um código foi recebido (idêntico ao número declarado no modo confirm_phone estrito) |
tries | int | Tentativas falhas são contadas (código errado ou telefone errado) |
expired | bool | true quando há tentativas falhas demais (tentativas ≥ 4) - solicite um novo OTP |
mode | string | O modo desta solicitação: confirm_phone ou verify_sender |
sender_matches_intended | bool | confirm_phone apenas, quando um código for recebido - true = o remetente é o número que você pediu para verificar; false = um remetente corrigido foi certificado. Omitido em verify_sender. |
out_ts | int | Timestamp da resposta |
hmac | string | HMAC - verifique com {businessid}|{businessref}|{logincode}|{used}|{phoneEnc}|{out_ts} |
Quando auth === 1, um código válido foi recebido. Descriptografe phoneEnc para obter o telefone certificado (verifique sender_matches_intended no modo confirm_phone) e então faça o login do usuário.
Descriptografar o número de telefone
Quando auth=1, phoneEnc contém o telefone certificado (o remetente que se provou) criptografado com AES-256-CBC usando sua businesskey:
function decryptPhoneAES($encryptedData, $businesskey) {
$method = 'AES-256-CBC';
$key = hash('sha256', $businesskey, true);
$enc = str_replace(['-', '_'], ['+', '/'], $encryptedData);
$enc = str_pad($enc, strlen($enc) % 4 === 0
? strlen($enc) : strlen($enc) + 4 - strlen($enc) % 4,
'=', STR_PAD_RIGHT);
$decoded = base64_decode($enc);
$iv = substr($decoded, 0, 16);
$ciphertext = substr($decoded, 16);
return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}
$phone = decryptPhoneAES($response['phoneEnc'], $businesskey);
// Store this phone - it's the user's verified identity
Exemplo de frontend
A única interface necessária - mostre-a depois que seu backend chamar getReverseOTP:
<div style="text-align:center; max-width:400px; margin:auto;">
<p>Send this exact code via WhatsApp from your phone:</p>
<div style="font-size:28px; font-weight:bold; color:#25D366;
letter-spacing:3px;">
2FA-828161
</div>
<p>to <strong>+40 725 296 320</strong></p>
<a href="whatsapp://send?phone=40725296320&text=2FA-828161"
style="display:inline-block; padding:14px 30px; background:#25D366;
color:white; font-size:18px; font-weight:bold; border-radius:8px;
text-decoration:none; margin-top:12px;">
💬 Send via WhatsApp
</a>
<p style="color:#888; font-size:13px; margin-top:12px;">
⏱ Code expires in 10 minutes - no typing required!
</p>
</div>
Exemplo completo em PHP
Pronto para copiar e colar. Substitua YOUR_BUSINESS_ID e 'your-secret-key' pelos valores de wappsync.com/mybusiness.
<?php
$businessid = YOUR_BUSINESS_ID; // from wappsync.com/mybusiness
$businesskey = 'your-secret-key'; // from wappsync.com/mybusiness - keep server-side
// ── Step 1: Request reverse OTP ──
$businessref = bin2hex(random_bytes(4));
$phone = $_POST['phone']; // user's phone from your form
$ts = time();
$hmac = hash_hmac('sha256',
"{$businessid}|{$businessref}|{$phone}|{$ts}",
$businesskey
);
$response = callAPI('https://wappsync.com/phone-verify/api/getReverseOTP', [
'businessid' => $businessid,
'businessref' => $businessref,
'phone' => $phone,
'ts' => $ts,
'hmac' => $hmac,
]);
$data = json_decode($response, true);
$code = $data['reverseotp_display']; // "2FA-828161"
$waphone = $data['waphone']; // "+40725296320"
$logincode = $data['logincode'];
$in_ts = $data['in_ts'];
// ── Show the code to your user (render frontend example above) ──
// ── Step 2: Poll until verified ──
$maxAttempts = 40; // ~2 minutes at 3s intervals
$attempt = 0;
do {
sleep(3);
$attempt++;
$hmac = hash_hmac('sha256',
"{$businessid}|{$businessref}|{$logincode}|{$in_ts}",
$businesskey
);
$response = callAPI(
'https://wappsync.com/phone-verify/api/getReverseOTPStatus',
[
'businessid' => $businessid,
'businessref' => $businessref,
'logincode' => $logincode,
'in_ts' => $in_ts,
'hmac' => $hmac,
]
);
$data = json_decode($response, true);
} while (($data['auth'] ?? 0) !== 1 && $attempt < $maxAttempts);
if (($data['auth'] ?? 0) === 1) {
// ✅ Verified - log the user in!
$phone = decryptPhoneAES($data['phoneEnc'], $businesskey);
$_SESSION['user_phone'] = $phone;
header('Location: /dashboard');
exit;
} else {
echo "Verification timed out. Please try again.";
}
// ── Helper ──
function callAPI($url, $payload) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
verify_sender: envie phone => '' (e opcionalmente mode => 'verify_sender') - nada é enviado pelo WhatsApp, então você mesmo deve repassar o reverseotp_display ao seu cliente. Com auth=1, phoneEnc é descriptografado para o número certificado do remetente; não há sender_matches_intended para verificar.
accept_sender: para confirm_phone, adicione 'on_mismatch' => 'accept_sender'. Se auth=1 e sender_matches_intended for false, o número certificado difere do solicitado - aceite-o como número corrigido ou solicite um novo OTP para o número que você realmente precisa.
Tratamento de erros
A API retorna "status": "error" com uma message legível:
| HTTP | message | Quando |
|---|---|---|
| 400 | Missing required parameters | Você esqueceu um campo obrigatório |
| 400 | A phone is required in confirm_phone mode | confirm_phone solicitado sem um telefone |
| 400 | Invalid mode / Invalid on_mismatch | Valor de enum desconhecido |
| 400 | The phone number is invalid or not a mobile number. | Formato de telefone inválido |
| 403 | Unknown or invalid business ID | businessid incorreto |
| 403 | Invalid HMAC | A assinatura não corresponde - verifique sua string de assinatura |
| 409 | This request was already used. | businessref+in_ts duplicado |
Segurança
| 🔐 HMAC-signed | Cada solicitação e resposta é verificada com sua businesskey |
| 📱 Correspondência de telefone conforme o modo | confirm_phone verifica se o remetente corresponde ao número solicitado; verify_sender / accept_sender certificam apenas o remetente real - nunca um número que não enviou nada |
| 🎯 Códigos ativos exclusivos | Dois códigos pendentes nunca compartilham o mesmo valor, então um código recebido é mapeado de forma determinística para uma única solicitação |
| 🔂 Single-use | Os códigos passam por 0→1→2, nunca reutilizáveis |
| ⏱ Time-limited | Os códigos expiram após 10 minutos, máximo de 4 tentativas de entrega |
| 🔒 Sem segredos do lado do cliente | Sua businesskey fica no seu servidor; nunca no código do navegador |
| 🛡 Criptografado com AES-256 | Os números de telefone nas respostas são criptografados com sua businesskey |
Experimente ao vivo
🎮 Fluxo de login da demonstração - uma demonstração funcional que mostra a experiência completa do usuário.
🧪 Ferramenta de teste de API - requer seu próprio businessid e businesskey de wappsync.com/mybusiness.