Přeskočit obsah

Bleskový hovor

Flash Call je metoda telefonického ověření, která k ověření telefonních čísel používá místo SMS zmeškaný hovor. Je to rychlejší, bezpečnější a cenově výhodnější.

Přehled

Ověření Flash Call funguje takto:

  1. Uživatel požaduje ověření
  2. Systém zahájí hovor na telefon uživatele
  3. Hovor je automaticky ukončen po 1-2 zazvoněních
  4. Aplikace uživatele zachytí ID volajícího
  5. ID volajícího je ověřeno podle očekávaného vzoru
  6. Uživatel je ověřen

Výhody

Nákladově efektivní

  • Až 10x levnější než SMS
  • Žádné poplatky za doručení zpráv
  • Snížené náklady na velkoobjemové ověřování

Rychlejší

  • Okamžité ověření (1-3 sekundy)
  • Žádné čekání na doručení SMS
  • Lepší uživatelský zážitek

Bezpečnější

  • Je těžší zachytit než SMS
  • Žádné OTP viditelné v oznámeních
  • Odolné vůči útokům s výměnou SIM

Globální dosah

  • Funguje v zemích s omezeními SMS
  • Žádné problémy s filtrováním SMS
  • Univerzální kompatibilita s telefony

Základní Flash Call

Žádost

{
  "from": "YourApp",
  "to": "+380XXXXXXXXX",
  "type": "flashcall",
  "messageData": {
    "callerId": "+380123456789"
  }
}

Parametry

Parametr Typ Povinné Popis
"od" řetězec Ano Váš identifikátor odesílatele
do řetězec Ano Telefonní číslo příjemce (E.164)
"typ" řetězec Ano Nastavit na "flashcall"
"ID volajícího" řetězec Ano Telefonní číslo, které bude volat uživateli
ttl celé číslo Ne Doba životnosti v sekundách (výchozí: 60)

Jak to funguje

1. Uživatel zadá telefonní číslo

Uživatel poskytne své telefonní číslo ve vaší aplikaci:

Phone: +380XXXXXXXXX

2. Vyžádejte si Flash Call

Váš server požaduje ověření bleskovým voláním:

curl -X POST https://restapi.smsbat.com/bat/messagelist \
  -H "X-Authorization-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{
      "from": "YourApp",
      "to": "+380XXXXXXXXX",
      "type": "flashcall",
      "messageData": {
        "callerId": "+380123456789"
      },
      "ttl": 60
    }]
  }'

3. Odpověď API

API vrací očekávaný vzor ID volajícího:

{
  "messagelistId": 123456,
  "messages": [
    {
      "messageId": "abc123def456",
      "status": "accepted",
      "callerId": "+380123456789",
      "pattern": "***456789",
      "to": "+380XXXXXXXXX"
    }
  ]
}

4. Zahajte hovor

Systém zahájí hovor na telefon uživatele a ukončí jej po 1-2 zazvoněních.

5. Zachyťte ID volajícího

Uživatelská aplikace zachytí ID volajícího příchozího hovoru:

// Android example
val cursor = contentResolver.query(
    CallLog.Calls.CONTENT_URI,
    arrayOf(CallLog.Calls.NUMBER),
    null, null,
    CallLog.Calls.DATE + " DESC"
)

6. Ověřte vzor

Porovnejte zachycené ID volajícího s očekávaným vzorem:

// JavaScript example
function verifyFlashCall(callerId, pattern) {
  // Remove non-digits
  const callerDigits = callerId.replace(/\D/g, '');
  const patternDigits = pattern.replace(/\*/g, '.');

  // Check if matches pattern
  const regex = new RegExp(patternDigits);
  return regex.test(callerDigits);
}

Příklady implementace

Android

class FlashCallVerification {
    fun requestFlashCall(phoneNumber: String) {
        // 1. Request flash call from API
        val response = api.requestFlashCall(phoneNumber)
        val pattern = response.pattern

        // 2. Wait for incoming call
        val callReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                if (intent.action == TelephonyManager.ACTION_PHONE_STATE_CHANGED) {
                    val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE)

                    if (state == TelephonyManager.EXTRA_STATE_RINGING) {
                        val callerId = intent.getStringExtra(
                            TelephonyManager.EXTRA_INCOMING_NUMBER
                        )

                        // 3. Verify caller ID against pattern
                        if (verifyPattern(callerId, pattern)) {
                            onVerificationSuccess()
                        }
                    }
                }
            }
        }

        // Register receiver
        context.registerReceiver(
            callReceiver,
            IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED)
        )
    }

    private fun verifyPattern(callerId: String?, pattern: String): Boolean {
        if (callerId == null) return false

        val regex = pattern.replace("*", "\\d").toRegex()
        return regex.matches(callerId)
    }
}

iOS

class FlashCallVerification {
    func requestFlashCall(phoneNumber: String) {
        // 1. Request flash call from API
        api.requestFlashCall(phoneNumber) { response in
            let pattern = response.pattern

            // 2. Use CallKit to detect incoming call
            let provider = CXProvider(configuration: providerConfiguration)
            provider.setDelegate(self, queue: nil)

            // Store pattern for verification
            self.expectedPattern = pattern
        }
    }

    // CallKit delegate
    func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
        // Capture caller ID
        let callerId = action.callUUID.uuidString

        // Verify against pattern
        if verifyPattern(callerId: callerId, pattern: expectedPattern) {
            onVerificationSuccess()
        }

        action.fulfill()
    }

    private func verifyPattern(callerId: String, pattern: String) -> Bool {
        let regex = try! NSRegularExpression(
            pattern: pattern.replacingOccurrences(of: "*", with: "\\d")
        )
        let range = NSRange(location: 0, length: callerId.count)
        return regex.firstMatch(in: callerId, range: range) != nil
    }
}

Web (na straně serveru)

// Node.js example
const express = require('express');
const app = express();

app.post('/request-verification', async (req, res) => {
  const { phoneNumber } = req.body;

  // 1. Request flash call
  const response = await fetch('https://restapi.smsbat.com/bat/messagelist', {
    method: 'POST',
    headers: {
      'X-Authorization-Key': process.env.SMSBAT_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      messages: [{
        from: 'YourApp',
        to: phoneNumber,
        type: 'flashcall',
        messageData: {
          callerId: process.env.FLASH_CALL_NUMBER
        },
        ttl: 60
      }]
    })
  });

  const data = await response.json();
  const { messageId, pattern } = data.messages[0];

  // 2. Store pattern for verification
  await redis.setex(`flashcall:${messageId}`, 60, pattern);

  // 3. Return pattern to client
  res.json({ messageId, pattern });
});

app.post('/verify-flashcall', async (req, res) => {
  const { messageId, callerId } = req.body;

  // 1. Get expected pattern
  const pattern = await redis.get(`flashcall:${messageId}`);

  if (!pattern) {
    return res.status(400).json({ error: 'Verification expired' });
  }

  // 2. Verify caller ID
  const regex = new RegExp(pattern.replace(/\*/g, '\\d'));
  const isValid = regex.test(callerId);

  if (isValid) {
    // Mark phone as verified
    await markPhoneVerified(callerId);
    res.json({ verified: true });
  } else {
    res.status(400).json({ error: 'Invalid caller ID' });
  }
});

Formát odpovědi

Úspěšná odpověď

{
  "messagelistId": 123456,
  "messages": [
    {
      "messageId": "abc123def456",
      "status": "accepted",
      "callerId": "+380123456789",
      "pattern": "***456789",
      "to": "+380XXXXXXXXX",
      "ttl": 60
    }
  ]
}

Pole odpovědí

Pole Typ Popis
ID zprávy řetězec Jedinečné ověřovací ID
"stav" řetězec Stav: přijato, zamítnuto
"ID volajícího" řetězec Celé číslo volajícího
"vzor" řetězec Vzor, který se má shodovat (číslice + hvězdičky)
do řetězec Telefonní číslo příjemce
ttl celé číslo Doba platnosti v sekundách

Shoda vzorů

API vrací vzor s hvězdičkami maskujícími některé číslice:

Full number: +380123456789
Pattern:     ***456789

Vaše aplikace by měla:

  1. Zachyťte ID příchozího volajícího
  2. Extrahujte číslice z ID volajícího
  3. Porovnejte se vzorem (hvězdičky = libovolná číslice)
  4. Ověřte shodu v období TTL

Záloha na SMS

Pokud selže Flash Call, automaticky se vraťte k SMS:

{
  "from": "YourApp",
  "to": "+380XXXXXXXXX",
  "type": "flashcall",
  "messageData": {
    "callerId": "+380123456789"
  },
  "fallback": {
    "type": "sms",
    "text": "Your verification code is: 123456"
  },
  "ttl": 60
}

Případy použití

Registrace účtu

Ověřte telefonní čísla během registrace bez poplatků za SMS.

Ověření přihlášení

Dvoufaktorová autentizace pomocí bleskového volání.

Aktualizace telefonního čísla

Ověřte nové telefonní číslo, když uživatel aktualizuje profil.

Potvrzení transakce

Potvrďte transakce s vysokou hodnotou pomocí bleskového volání.

Nejlepší postupy

TTL

  • ✅ Nastavte TTL na 60-90 sekund
  • ✅ Umožněte uživateli opakovat po vypršení platnosti
  • ❌ Nepoužívejte TTL déle než 120 sekund

Uživatelská zkušenost

  • Zobrazit zprávu "Čekání na hovor..."
  • Displej odpočítávacího časovače (60 sekund)
  • Poskytněte možnost "Použít místo toho SMS"
  • Automatická detekce a ověření ID volajícího

Zpracování chyb

  • Řešit chybějící oprávnění telefonu
  • Časový limit po vypršení TTL
  • Poskytněte možnost záložní SMS
  • Zobrazit jasné chybové zprávy

Oprávnění

Před bleskovým hovorem si vyžádejte oprávnění k telefonu:

Android:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />

iOS:

<key>NSPhoneCallUsageDescription</key>
<string>We need phone access to verify your number</string>

Testování

  • Test na různých zařízeních
  • Test s různými dopravci
  • Testovat scénáře zamítnutí povolení
  • Testujte scénáře časového limitu sítě

Omezení

Podpora platformy

  • Funguje na všech mobilních zařízeních
  • Vyžaduje schopnost telefonovat – Vyžaduje oprávnění READ_PHONE_STATE
  • Nemusí fungovat na tabletech bez telefonu

Síť

  • Vyžaduje aktivní telefonní připojení
  • Může selhat ve špatných podmínkách sítě
  • Mohou platit omezení dopravce
  • Mezinárodní sazby se mohou lišit

Soukromí

  • Uživatelé mohou blokovat neznámá čísla
  • Některá zařízení mají blokování hovorů
  • Vyžaduje explicitní oprávnění
  • Zvažte obavy o soukromí uživatelů

Odstraňování problémů

Hovor nepřijat

  • Zkontrolujte, zda má telefon signál
  • Ověřte formát čísla (E.164)
  • Zkontrolujte omezení operátora
  • Zkuste záložní SMS

Vzor neodpovídá

  • Zajistěte zachycení správného ID volajícího
  • Odstraňte nečíselné znaky
  • Zkontrolujte formát vzoru
  • Ověřte během období TTL

Oprávnění odepřeno

  • Řádně vyžádejte oprávnění
  • Vysvětlete, proč jsou potřebná oprávnění
  • Poskytněte alternativu (SMS)
  • Zacházejte elegantně

Další kroky