Hyppää sisältöön

Flash Call

Flash Call on puhelimen vahvistusmenetelmä, joka vahvistaa puhelinnumerot tekstiviestien sijaan vastaamatta jääneen puhelun avulla. Se on nopeampi, turvallisempi ja kustannustehokkaampi.

Yleiskatsaus

Flash Call -vahvistus toimii:

  1. Käyttäjä pyytää vahvistusta
  2. Järjestelmä aloittaa puhelun käyttäjän puhelimeen
  3. Puhelu katkeaa automaattisesti 1-2 soiton jälkeen
  4. Käyttäjän sovellus tallentaa soittajan tunnuksen
  5. Soittajan tunnus tarkistetaan odotetun kaavan mukaan
  6. Käyttäjä on todennettu

Edut

Kustannustehokas

  • Jopa 10x halvempi kuin tekstiviesti
  • Ei viestien toimitusmaksuja
  • Pienemmät suuren volyymin tarkastuksen kustannukset

Nopeammin

  • Välitön vahvistus (1-3 sekuntia)
  • Ei odottelua tekstiviestien toimitusta
  • Parempi käyttökokemus

Turvallisempi

  • Vaikeampi siepata kuin tekstiviesti
  • OTP:tä ei näy ilmoituksissa
  • Kestää SIM-swap-hyökkäyksiä

Globaali kattavuus

  • Toimii maissa, joissa on tekstiviestirajoituksia
  • Ei ongelmia tekstiviestisuodatuksen kanssa
  • Universaali puhelinyhteensopivuus

Flash-peruspuhelu

Pyyntö

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

Parametrit

Parametri Tyyppi Pakollinen Kuvaus
"alkaen" merkkijono Kyllä Lähettäjän tunniste
"to" merkkijono Kyllä Vastaanottajan puhelinnumero (E.164)
"tyyppi" merkkijono Kyllä Aseta "flashcall"
"soittajan tunnus" merkkijono Kyllä Puhelinnumero, joka soittaa käyttäjälle
ttl kokonaisluku Ei Elinaika sekunneissa (oletus: 60)

Kuinka se toimii

1. Käyttäjä syöttää puhelinnumeron

Käyttäjä antaa puhelinnumeronsa sovelluksessasi:

Phone: +380XXXXXXXXX

2. Pyydä Flash Call

Palvelimesi pyytää flash-puhelun vahvistusta:

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

API palauttaa odotetun soittajan tunnuskuvion:

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

4. Aloita puhelu

Järjestelmä aloittaa puhelun käyttäjän puhelimeen ja lopettaa 1-2 soiton jälkeen.

5. Tallenna soittajan tunnus

Käyttäjän sovellus tallentaa saapuvan puhelun soittajan tunnuksen:

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

6. Vahvista kuvio

Vertaa tallennettua soittajan tunnusta odotettuun kuvioon:

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

Toteutusesimerkkejä

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

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

Vastausmuoto

Menestysvastaus

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

Vastauskentät

Kenttä Tyyppi Kuvaus
"messageId" merkkijono Yksilöllinen vahvistustunnus
"tila" merkkijono Tila: "hyväksytty", "hylätty"
"soittajan tunnus" merkkijono Täydellinen soittajan numero
"kuvio" merkkijono Vastaava kuvio (numerot + tähdet)
"to" merkkijono Vastaanottajan puhelinnumero
ttl kokonaisluku Voimassaoloaika sekunneissa

Kuvioiden yhteensopivuus

API palauttaa kuvion, jossa tähdet peittävät joitain numeroita:

Full number: +380123456789
Pattern:     ***456789

Sovelluksesi pitäisi:

  1. Tallenna saapuvan soittajan tunnus
  2. Poimi numerot soittajan tunnuksesta
  3. Vastaa kuvioon (tähdet = mikä tahansa numero)
  4. Tarkista vastaavuus TTL-jakson sisällä

Takaisin tekstiviestiin

Jos Flash Call epäonnistuu, palaa automaattisesti tekstiviestiin:

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

Käyttötapaukset

Tilin rekisteröinti

Vahvista puhelinnumerot rekisteröitymisen yhteydessä ilman tekstiviestikuluja.

Kirjautumisen vahvistus

Kaksivaiheinen todennus flash-puhelulla.

Puhelinnumeron päivitys

Vahvista uusi puhelinnumero, kun käyttäjä päivittää profiilia.

Tapahtuman vahvistus

Vahvista arvokkaat tapahtumat flash-puhelulla.

Parhaat käytännöt

TTL

  • ✅ Aseta TTL 60-90 sekuntiin
  • ✅ Anna käyttäjän yrittää uudelleen vanhenemisen jälkeen
  • ❌ Älä käytä TTL:ää yli 120 sekuntia

Käyttökokemus

  • Näytä "Odotetaan puhelua..." -viesti
  • Näytön ajastin (60 sekuntia)
  • Anna vaihtoehto "Käytä tekstiviestejä"
  • Tunnista ja vahvista soittajan tunnus automaattisesti

Virheiden käsittely

  • Käsittele puuttuvat puhelimen käyttöoikeudet
  • Aikakatkaisu TTL:n päättymisen jälkeen
  • Tarjoa tekstiviestien varavaihtoehto
  • Näytä selkeät virheilmoitukset

Käyttöoikeudet

Pyydä puhelimen käyttöoikeuksia ennen flash-puhelua:

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>

Testaus

  • Testaa eri laitteilla
  • Testaa eri operaattoreiden kanssa
  • Testaa lupien epäämisskenaarioita
  • Testaa verkon aikakatkaisutilanteita

Rajoitukset

Alustan tuki

  • Toimii kaikilla mobiililaitteilla
  • Edellyttää puhelukykyä
  • Tarvitsee READ_PHONE_STATE-luvan
  • Ei ehkä toimi tableteilla ilman puhelinta

Verkko

  • Vaatii aktiivisen puhelinyhteyden
  • Voi epäonnistua huonoissa verkko-olosuhteissa
  • Operaattorin rajoitukset voivat olla voimassa
  • Kansainväliset hinnat voivat vaihdella

Yksityisyys

  • Käyttäjät voivat estää tuntemattomia numeroita
  • Joissakin laitteissa on puhelun esto
  • Edellyttää nimenomaisia käyttöoikeuksia
  • Harkitse käyttäjien yksityisyyttä koskevia huolenaiheita

Vianetsintä

Puhelua ei vastaanotettu

  • Tarkista, että puhelimessa on signaali
  • Tarkista numeromuoto (E.164)
  • Tarkista operaattorin rajoitukset
  • Kokeile SMS-varaustoimintoa

Kuvio ei täsmää

  • Varmista, että otat oikean soittajan tunnuksen
  • Poista ei-numeroiset merkit
  • Tarkista kuvion muoto
  • Tarkista TTL-ajan kuluessa

Lupa evätty

  • Pyydä käyttöoikeuksia oikein
  • Selitä, miksi lupia tarvitaan
  • Tarjoa vaihtoehto (SMS)
  • Käsittele kauniisti

Seuraavat vaiheet