Preskoči na sadržaj

Flash poziv

Flash Call je metoda telefonske provjere koja koristi propušteni poziv umjesto SMS-a za provjeru telefonskih brojeva. Brži je, sigurniji i isplativiji.

Pregled

Brza provjera poziva radi na sljedeći način:

  1. Korisnik zahtijeva provjeru
  2. Sustav inicira poziv na korisnikov telefon
  3. Poziv se automatski prekida nakon 1-2 zvona
  4. Korisnička aplikacija bilježi ID pozivatelja
  5. ID pozivatelja se provjerava prema očekivanom uzorku
  6. Korisnik je autentificiran

Prednosti

Isplativo

  • Do 10x jeftinije od SMS-a
  • Nema naknade za dostavu poruka
  • Smanjeni troškovi za provjeru velikog volumena

Brže

  • Trenutačna provjera (1-3 sekunde)
  • Nema čekanja na isporuku SMS-a
  • Bolje korisničko iskustvo

Sigurnije

  • Teže presresti nego SMS
  • OTP nije vidljiv u obavijestima
  • Otporan na napade zamjene SIM kartice

Globalni doseg

  • Radi u zemljama s ograničenjima SMS-a
  • Nema problema s filtriranjem SMS-ova
  • Univerzalna kompatibilnost telefona

Osnovni Flash poziv

Zahtjev

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

Parametri

Parametar Upišite Obavezno Opis
od niz Da Vaš identifikator pošiljatelja
za niz Da Telefonski broj primatelja (E.164)
tip niz Da Postavite na "flashcall"
ID pozivatelja niz Da Broj telefona koji će nazvati korisnika
ttl cijeli broj Ne Vrijeme života u sekundama (zadano: 60)

Kako to radi

1. Korisnik unosi broj telefona

Korisnik navodi svoj telefonski broj u vašoj aplikaciji:

Phone: +380XXXXXXXXX

2. Zatražite brzi poziv

Vaš poslužitelj zahtijeva brzu potvrdu poziva:

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. Odgovor API-ja

API vraća očekivani uzorak ID-a pozivatelja:

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

4. Započnite poziv

Sustav započinje poziv na korisnikov telefon i prekida nakon 1-2 zvona.

5. Snimi ID pozivatelja

Korisnička aplikacija bilježi ID pozivatelja dolaznog poziva:

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

6. Provjerite uzorak

Usporedite snimljeni ID pozivatelja s očekivanim uzorkom:

// 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);
}

Primjeri implementacije

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 strani poslužitelja)

// 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' });
  }
});

Format odgovora

Uspješan odgovor

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

Polja odgovora

Polje Upišite Opis
id poruke niz Jedinstveni ID provjere
status niz Status: prihvaćeno, odbijeno
ID pozivatelja niz Puni ID broj pozivatelja
uzorak niz Uzorak za podudaranje (znamenke + zvjezdice)
za niz Broj telefona primatelja
ttl cijeli broj Razdoblje valjanosti u sekundama

Usklađivanje uzorka

API vraća uzorak sa zvjezdicama koje maskiraju neke znamenke:

Full number: +380123456789
Pattern:     ***456789

Vaša bi aplikacija trebala:

  1. Snimite ID dolaznog pozivatelja
  2. Izdvojite znamenke iz ID-a pozivatelja
  3. Usporedi s uzorkom (zvjezdice = bilo koja znamenka)
  4. Provjerite podudaranje unutar TTL razdoblja

Povratak na SMS

Ako Flash Call ne uspije, automatski se vratite na SMS:

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

Slučajevi upotrebe

Registracija računa

Provjerite telefonske brojeve tijekom prijave bez troškova SMS-a.

Potvrda prijave

Dvofaktorska autentifikacija korištenjem flash poziva.

Ažuriranje telefonskog broja

Potvrdite novi telefonski broj kada korisnik ažurira profil.

Potvrda transakcije

Potvrdite transakcije visoke vrijednosti brzim pozivom.

Najbolji primjeri iz prakse

TTL

  • ✅ Postavite TTL na 60-90 sekundi
  • ✅ Dopustite korisniku ponovni pokušaj nakon isteka
  • ❌ Nemojte koristiti TTL duže od 120 sekundi

Korisničko iskustvo

  • Prikaži poruku "Čekanje na poziv...".
  • Prikaz brojača vremena (60 sekundi)
  • Omogućite opciju "Umjesto toga upotrijebi SMS"
  • Automatsko otkrivanje i provjera ID-a pozivatelja

Rješavanje grešaka

  • Obradite telefonske dozvole koje nedostaju
  • Vremensko ograničenje nakon isteka TTL-a
  • Omogućite zamjensku opciju SMS-a
  • Prikaži jasne poruke o pogrešci

Dopuštenja

Zatraži telefonska dopuštenja prije brzog poziva:

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>

Testiranje

  • Testirajte na različitim uređajima
  • Testirajte s različitim prijevoznicima
  • Testirajte scenarije odbijanja dopuštenja
  • Testirajte scenarije isteka vremena mreže

Ograničenja

Podrška za platformu

  • Radi na svim mobilnim uređajima
  • Zahtijeva mogućnost telefoniranja
  • Potrebno je READ_PHONE_STATE dopuštenje
  • Možda neće raditi na tabletima bez telefona

Mreža

  • Zahtijeva aktivnu telefonsku vezu
  • Može pokvariti u lošim mrežnim uvjetima
  • Moguća su ograničenja mobilnog operatera
  • Međunarodne cijene mogu varirati

Privatnost

  • Korisnici mogu blokirati nepoznate brojeve
  • Neki uređaji imaju blokiranje poziva
  • Zahtijeva izričite dozvole
  • Razmotrite brigu o privatnosti korisnika

Rješavanje problema

Poziv nije primljen

  • Provjerite ima li telefon signal
  • Provjerite format broja (E.164)
  • Provjerite ograničenja operatera
  • Pokušajte s rezervnim SMS-om

Uzorak ne odgovara

  • Osigurajte snimanje ispravnog ID-a pozivatelja
  • Skidanje znakova koji nisu znamenke
  • Provjerite format uzorka
  • Provjerite unutar TTL razdoblja

Dopuštenje odbijeno

  • Ispravno zatražite dopuštenja
  • Objasnite zašto su potrebne dozvole
  • Pružite alternativu (SMS)
  • Rukujte graciozno

Sljedeći koraci