Flash poziv
Flash poziv je metoda potvrde telefona koja koristi propušteni poziv umjesto SMS-a za provjeru telefonskih brojeva. Brži je, sigurniji i isplativiji.
Pregled
Provjera Flash poziva radi:
- Korisnik zahtjeva verifikaciju
- Sistem pokreće poziv na telefon korisnika
- Poziv se automatski prekida nakon 1-2 zvona
- Korisnička aplikacija snima ID pozivaoca
- ID pozivaoca je verifikovan u odnosu na očekivani obrazac
- Korisnik je autentificiran
Prednosti
Isplativo
- Do 10x jeftinije od SMS-a
- Bez naknade za dostavu poruka
- Smanjeni troškovi za verifikaciju velikog obima
Brže
- Trenutna verifikacija (1-3 sekunde)
- Nema čekanja na isporuku SMS-a
- Bolje korisničko iskustvo
Sigurnije
- Teže presresti nego SMS
- Nema OTP vidljivog u obavještenjima
- Otporan na napade zamjene SIM kartice
Globalni doseg
- Radi u zemljama sa ograničenjima SMS-a
- Nema problema sa filtriranjem SMS-a
- Univerzalna kompatibilnost telefona
Osnovni Flash poziv
Zahtjev
{
"from": "YourApp",
"to": "+380XXXXXXXXX",
"type": "flashcall",
"messageData": {
"callerId": "+380123456789"
}
}
Parametri
| Parametar | Vrsta | Obavezno | Opis |
|---|---|---|---|
od |
string | Da | Vaš identifikator pošiljaoca |
to |
string | Da | Broj telefona primaoca (E.164) |
tip |
string | Da | Postavite na "flashcall" |
callerId |
string | Da | Broj telefona koji će pozvati korisnika |
ttl |
cijeli broj | Ne | Vrijeme života u sekundama (zadano: 60) |
Kako radi
1. Korisnik unosi broj telefona
Korisnik daje svoj broj telefona u vašoj aplikaciji:
2. Zatražite Flash poziv
Vaš server traži provjeru flash 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. API odgovor
API vraća očekivani obrazac ID pozivatelja:
{
"messagelistId": 123456,
"messages": [
{
"messageId": "abc123def456",
"status": "accepted",
"callerId": "+380123456789",
"pattern": "***456789",
"to": "+380XXXXXXXXX"
}
]
}
4. Pokreni poziv
Sistem pokreće poziv na telefon korisnika i prekida nakon 1-2 zvona.
5. Capture Caller ID
Korisnička aplikacija snima ID pozivaoca dolaznog poziva:
// Android example
val cursor = contentResolver.query(
CallLog.Calls.CONTENT_URI,
arrayOf(CallLog.Calls.NUMBER),
null, null,
CallLog.Calls.DATE + " DESC"
)
6. Potvrdite uzorak
Uporedite snimljeni ID pozivaoca sa 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 servera)
// 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 | Vrsta | Opis |
|---|---|---|
messageId |
string | Jedinstveni ID potvrde |
status |
string | Status: prihvaćen, odbijen |
callerId |
string | Full caller ID number |
uzorak |
string | Uzorak za podudaranje (cifre + zvjezdice) |
to |
string | Broj telefona primaoca |
ttl |
cijeli broj | Period važenja u sekundama |
Pattern Matching
API vraća uzorak sa zvjezdicama koje maskiraju neke cifre:
Vaša aplikacija bi trebala:
- Snimite ID dolaznog pozivaoca
- Izdvojite cifre iz ID-a pozivaoca
- Usporedite s uzorkom (zvjezdice = bilo koja cifra)
- Provjerite podudaranje unutar TTL perioda
Povratak na SMS
Ako Flash poziv 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
Potvrdite brojeve telefona tokom registracije bez troškova SMS-a.
Potvrda prijave
Dvofaktorska autentifikacija pomoću flash poziva.
Ažuriranje telefonskog broja
Potvrdite novi broj telefona kada korisnik ažurira profil.
Potvrda transakcije
Potvrdite transakcije velike vrijednosti pomoću flash poziva.
Najbolje prakse
TTL
- ✅ Postavite TTL na 60-90 sekundi
- ✅ Dozvolite korisniku da pokuša ponovo nakon isteka
- ❌ Ne koristite TTL duže od 120 sekundi
Korisničko iskustvo
- Prikaži poruku "Čeka se poziv...".
- Prikaz tajmera za odbrojavanje (60 sekundi)
- Omogućite opciju "Umjesto toga koristite SMS"
- Automatsko otkrivanje i provjera ID-a pozivatelja
Rukovanje greškama
- Rukovati nedostajućim dozvolama za telefon
- Vremensko ograničenje nakon isteka TTL-a
- Omogućite zamjensku opciju za SMS
- Prikaži jasne poruke o grešci
Dozvole
Zatražite dozvole za telefon prije flash 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 sa različitim nosačima
- Testirajte scenarije odbijanja dozvole
- Testirajte scenarije vremenskog ograničenja mreže
Ograničenja
Podrška platformi
- Radi na svim mobilnim uređajima
- Zahtijeva mogućnost telefonskog poziva
- Potrebna je dozvola READ_PHONE_STATE
- Možda neće raditi na tabletima bez telefona
Mreža
- Zahtijeva aktivnu telefonsku vezu
- Može otkazati u lošim mrežnim uvjetima
- Moguća su ograničenja operatera
- Međunarodne stope mogu varirati
Privatnost
- Korisnici mogu blokirati nepoznate brojeve
- Neki uređaji imaju blokadu poziva
- Zahtijeva izričite dozvole
- Uzmite u obzir brige o privatnosti korisnika
Rješavanje problema
Poziv nije primljen
- Provjerite ima li telefon signal
- Potvrdite format broja (E.164)
- Provjerite ograničenja operatera
- Pokušajte zamjenski SMS
Uzorak se ne podudara
- Osigurajte snimanje ispravnog ID-a pozivaoca
- Skinite necifrene znakove
- Provjerite format uzorka
- Potvrdite unutar TTL perioda
Dozvola odbijena
- Pravilno zatražite dozvole
- Objasnite zašto su potrebne dozvole
- Dajte alternativu (SMS)
- Rukuj graciozno
Sljedeći koraci
- Viber OTP - Alternativna OTP dostava
- SMS poruke - zamjenski SMS
- Provjeri status - Pratite status flash poziva