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:
- Korisnik zahtijeva provjeru
- Sustav inicira poziv na korisnikov telefon
- Poziv se automatski prekida nakon 1-2 zvona
- Korisnička aplikacija bilježi ID pozivatelja
- ID pozivatelja se provjerava prema očekivanom uzorku
- 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:
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:
Vaša bi aplikacija trebala:
- Snimite ID dolaznog pozivatelja
- Izdvojite znamenke iz ID-a pozivatelja
- Usporedi s uzorkom (zvjezdice = bilo koja znamenka)
- 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:
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
- Viber OTP - Alternativna OTP dostava
- SMS poruke - SMS zamjena
- Provjeri status - Pratite status flash poziva