Ga naar inhoud

Flitsoproep

Flash Call is een telefoonverificatiemethode waarbij een gemiste oproep in plaats van een sms wordt gebruikt om telefoonnummers te verifiëren. Het is sneller, veiliger en kosteneffectiever.

Overzicht

Flash Call-verificatie werkt door:

  1. Gebruiker vraagt om verificatie
  2. Het systeem start een oproep naar de telefoon van de gebruiker
  3. Het gesprek wordt automatisch beëindigd na 1-2 belsignalen
  4. De app van de gebruiker legt de beller-ID vast
  5. Beller-ID wordt geverifieerd aan de hand van het verwachte patroon
  6. Gebruiker is geverifieerd

Voordelen

Kosteneffectief

  • Tot 10x goedkoper dan sms
  • Geen bezorgkosten voor berichten
  • Lagere kosten voor verificatie van grote volumes

Sneller

  • Onmiddellijke verificatie (1-3 seconden)
  • Niet wachten op sms-bezorging
  • Betere gebruikerservaring

Veiliger

  • Moeilijker te onderscheppen dan sms
  • Geen OTP zichtbaar in meldingen
  • Bestand tegen SIM-swap-aanvallen

Wereldwijd bereik

  • Werkt in landen met sms-beperkingen
  • Geen problemen met sms-filtering
  • Universele telefooncompatibiliteit

Basisflitsoproep

Verzoek

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

Parameters

Parameter Typ Vereist Beschrijving
van tekenreeks Ja Uw afzender-ID
naar tekenreeks Ja Telefoonnummer ontvanger (E.164)
type tekenreeks Ja Instellen op "flashcall"
bellerId tekenreeks Ja Telefoonnummer dat gebruiker zal bellen
ttl geheel getal Nee Time-to-live in seconden (standaard: 60)

Hoe het werkt

1. Gebruiker voert telefoonnummer in

De gebruiker geeft zijn telefoonnummer op in uw app:

Phone: +380XXXXXXXXX

2. Vraag een flitsoproep aan

Uw server vraagt om flash-oproepverificatie:

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-reactie

API retourneert het verwachte beller-ID-patroon:

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

4. Start een oproep

Het systeem start een oproep naar de telefoon van de gebruiker en beëindigt deze na 1-2 belsignalen.

5. Leg beller-ID vast

De app van de gebruiker legt de beller-ID van de inkomende oproep vast:

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

6. Controleer patroon

Vergelijk de vastgelegde beller-ID met het verwachte patroon:

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

Implementatievoorbeelden

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 (serverzijde)

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

Antwoordformaat

Succesreactie

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

Reactievelden

Veld Typ Beschrijving
berichtId tekenreeks Unieke verificatie-ID
status tekenreeks Status: geaccepteerd, afgewezen
bellerId tekenreeks Volledig beller-ID-nummer
patroon tekenreeks Passend patroon (cijfers + sterretjes)
naar tekenreeks Telefoonnummer ontvanger
ttl geheel getal Geldigheidsduur in seconden

Patroonaanpassing

De API retourneert een patroon met sterretjes die enkele cijfers maskeren:

Full number: +380123456789
Pattern:     ***456789

Uw app moet:

  1. Leg inkomende beller-ID vast
  2. Cijfers uit nummerherkenning extraheren
  3. Match met patroon (sterretjes = elk cijfer)
  4. Controleer de match binnen de TTL-periode

Terugval op sms

Als Flash Call mislukt, val dan automatisch terug naar SMS:

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

Gebruiksscenario's

Accountregistratie

Verifieer telefoonnummers tijdens het aanmelden zonder sms-kosten.

Inlogverificatie

Tweefactorauthenticatie met behulp van Flash Call.

Telefoonnummer bijwerken

Controleer nieuw telefoonnummer wanneer gebruiker profiel bijwerkt.

Transactiebevestiging

Bevestig transacties met een hoge waarde met een flash-oproep.

Beste praktijken

TTL

  • ✅ Stel TTL in op 60-90 seconden
  • ✅ Laat de gebruiker het opnieuw proberen na het verlopen
  • ❌Gebruik TTL niet langer dan 120 seconden

Gebruikerservaring

  • Toon het bericht "Wacht op oproep...".
  • Afteltimer weergeven (60 seconden)
  • Bied de optie "Gebruik in plaats daarvan SMS"
  • Automatische detectie en verificatie van nummerherkenning

Foutafhandeling

  • Behandel ontbrekende telefoonrechten
  • Time-out nadat TTL is verlopen
  • Bied een SMS-fallback-optie
  • Toon duidelijke foutmeldingen

Machtigingen

Telefoonrechten aanvragen vóór flash-oproep:

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>

Testen

  • Test op verschillende apparaten
  • Test met verschillende vervoerders
  • Test scenario's voor het weigeren van toestemming
  • Test netwerktime-outscenario's

Beperkingen

Platformondersteuning

  • Werkt op alle mobiele apparaten
  • Vereist telefoongesprekken
  • Heeft READ_PHONE_STATE toestemming nodig
  • Werkt mogelijk niet op tablets zonder telefoon

Netwerk

  • Vereist een actieve telefoonverbinding
  • Kan mislukken bij slechte netwerkomstandigheden
  • Er kunnen beperkingen van toepassing zijn op vervoerders
  • Internationale tarieven kunnen variëren

Privacy

  • Gebruikers kunnen onbekende nummers blokkeren
  • Sommige apparaten hebben oproepblokkering
  • Vereist expliciete toestemming
  • Houd rekening met zorgen over de privacy van gebruikers

Problemen oplossen

Oproep niet ontvangen

  • Controleer of de telefoon signaal heeft
  • Getalformaat verifiëren (E.164)
  • Controleer de beperkingen van de vervoerder
  • Probeer SMS-fallback

Patroon komt niet overeen

  • Zorg ervoor dat de juiste nummerherkenning wordt vastgelegd
  • Strip niet-cijferige tekens
  • Controleer het patroonformaat
  • Verifieer binnen TTL-periode

Toestemming geweigerd

  • Vraag rechten correct aan
  • Leg uit waarom machtigingen nodig zijn
  • Bied alternatief (SMS)
  • Ga er sierlijk mee om

Volgende stappen