Bỏ qua

Cuộc gọi chớp nhoáng

Flash Call là phương thức xác minh số điện thoại sử dụng cuộc gọi nhỡ thay vì SMS để xác minh số điện thoại. Nó nhanh hơn, an toàn hơn và tiết kiệm chi phí hơn.

Tổng quan

Xác minh Cuộc gọi Flash hoạt động bằng cách:

  1. Người dùng yêu cầu xác minh
  2. Hệ thống thực hiện cuộc gọi đến điện thoại của người dùng
  3. Cuộc gọi tự động kết thúc sau 1-2 hồi chuông
  4. Ứng dụng của người dùng ghi lại ID người gọi
  5. ID người gọi được xác minh theo mẫu dự kiến
  6. Người dùng được xác thực

Lợi ích

Tiết kiệm chi phí

  • Rẻ hơn tới 10 lần so với SMS
  • Không tính phí gửi tin nhắn
  • Giảm chi phí cho việc xác minh số lượng lớn

Nhanh hơn

  • Xác minh tức thì (1-3 giây)
  • Không phải chờ gửi SMS
  • Trải nghiệm người dùng tốt hơn

An toàn hơn

  • Khó chặn hơn SMS
  • Không có OTP hiển thị trong thông báo
  • Chống lại các cuộc tấn công hoán đổi SIM

Phạm vi tiếp cận toàn cầu

  • Hoạt động ở các quốc gia có hạn chế về SMS
  • Không có vấn đề với việc lọc SMS
  • Khả năng tương thích điện thoại phổ thông

Cuộc gọi flash cơ bản

Yêu cầu

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

Thông số

Tham số Loại Bắt buộc Mô tả
từ chuỗi Mã định danh người gửi của bạn
đến chuỗi Số điện thoại người nhận (E.164)
loại chuỗi Đặt thành "flashcall"
Id người gọi chuỗi Số điện thoại sẽ gọi cho người dùng
ttl số nguyên Không Thời gian tồn tại tính bằng giây (mặc định: 60)

Nó hoạt động như thế nào

1. Người dùng nhập số điện thoại

Người dùng cung cấp số điện thoại của họ trong ứng dụng của bạn:

Phone: +380XXXXXXXXX

2. Yêu cầu cuộc gọi Flash

Máy chủ của bạn yêu cầu xác minh cuộc gọi flash:

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. Phản hồi API

API trả về mẫu ID người gọi dự kiến:

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

4. Bắt đầu cuộc gọi

Hệ thống bắt đầu cuộc gọi đến điện thoại của người dùng và kết thúc sau 1-2 hồi chuông.

5. Chụp ID người gọi

Ứng dụng của người dùng ghi lại ID người gọi của cuộc gọi đến:

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

6. Xác minh mẫu

So sánh ID người gọi đã chụp với mẫu dự kiến:

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

Ví dụ triển khai

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 (Phía máy chủ)

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

Định dạng phản hồi

Phản hồi thành công

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

Trường phản hồi

Lĩnh vực Loại Mô tả
messageId chuỗi ID xác minh duy nhất
trạng thái chuỗi Trạng thái: được chấp nhận, bị từ chối
Id người gọi chuỗi Số ID người gọi đầy đủ
mẫu chuỗi Mẫu phù hợp (chữ số + dấu hoa thị)
đến chuỗi Số điện thoại người nhận
ttl số nguyên Thời hạn hiệu lực tính bằng giây

Khớp mẫu

API trả về một mẫu có dấu hoa thị che một số chữ số:

Full number: +380123456789
Pattern:     ***456789

Ứng dụng của bạn nên:

  1. Chụp ID người gọi đến
  2. Trích xuất chữ số từ ID người gọi
  3. So khớp với mẫu (dấu hoa thị = bất kỳ chữ số nào)
  4. Xác minh sự trùng khớp trong khoảng thời gian TTL

Chuyển sang SMS

Nếu Flash Call không thành công, tự động chuyển về SMS:

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

Trường hợp sử dụng

Đăng ký tài khoản

Xác minh số điện thoại trong quá trình đăng ký mà không mất phí SMS.

Xác minh đăng nhập

Xác thực hai yếu tố bằng cuộc gọi flash.

Cập nhật số điện thoại

Xác minh số điện thoại mới khi người dùng cập nhật hồ sơ.

Xác nhận giao dịch

Xác nhận các giao dịch có giá trị cao bằng cuộc gọi flash.

Các phương pháp hay nhất

TTL

  • ✅ Đặt TTL thành 60-90 giây
  • ✅ Cho phép người dùng thử lại sau khi hết hạn
  • ❌ Không sử dụng TTL dài hơn 120 giây

Trải nghiệm người dùng

  • Hiển thị thông báo “Đang chờ cuộc gọi…”
  • Hiển thị đồng hồ đếm ngược (60 giây)
  • Cung cấp tùy chọn "Sử dụng SMS thay thế"
  • Tự động phát hiện và xác minh ID người gọi

Xử lý lỗi

  • Xử lý quyền truy cập điện thoại bị thiếu
  • Hết thời gian chờ sau khi TTL hết hạn
  • Cung cấp tùy chọn dự phòng SMS
  • Hiển thị thông báo lỗi rõ ràng

Quyền

Yêu cầu quyền của điện thoại trước cuộc gọi flash:

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>

Thử nghiệm

  • Kiểm tra trên các thiết bị khác nhau
  • Thử nghiệm với các nhà mạng khác nhau
  • Kịch bản từ chối quyền kiểm tra
  • Kiểm tra các kịch bản hết thời gian mạng

Hạn chế

Hỗ trợ nền tảng

  • Hoạt động trên tất cả các thiết bị di động
  • Yêu cầu khả năng gọi điện thoại
  • Cần có quyền READ_PHONE_STATE
  • Có thể không hoạt động trên máy tính bảng mà không có điện thoại

Mạng

  • Yêu cầu kết nối điện thoại hoạt động
  • Có thể thất bại trong điều kiện mạng kém
  • Có thể áp dụng hạn chế của nhà cung cấp dịch vụ
  • Giá quốc tế có thể thay đổi

Quyền riêng tư

  • Người dùng có thể chặn các số chưa biết
  • Một số máy có tính năng chặn cuộc gọi
  • Yêu cầu quyền rõ ràng
  • Xem xét mối quan ngại về quyền riêng tư của người dùng

Khắc phục sự cố

Không nhận được cuộc gọi

  • Kiểm tra điện thoại có tín hiệu
  • Xác minh định dạng số (E.164)
  • Kiểm tra các hạn chế của nhà cung cấp dịch vụ
  • Thử dự phòng SMS

Mẫu không khớp

  • Đảm bảo chụp chính xác ID người gọi
  • Loại bỏ các ký tự không có chữ số
  • Kiểm tra định dạng mẫu
  • Xác minh trong khoảng thời gian TTL

Quyền bị từ chối

  • Yêu cầu quyền đúng cách
  • Giải thích tại sao cần có quyền
  • Cung cấp thay thế (SMS)
  • Xử lý khéo léo

Các bước tiếp theo