AVC SMS

How to add OTP verification by SMS to your app

A one-time password (OTP) sent by SMS proves that a person holds the phone number they gave you. It is used for sign-up, login, password resets and payment confirmation. This guide explains the flow and shows a safe implementation in PHP that you can adapt to any language.

Developers3 min readUpdated

How SMS verification works

  1. The customer enters a mobile number.
  2. Your server creates a random code, stores it with an expiry time, and sends it by SMS.
  3. The customer types the code into your app.
  4. Your server compares it with the stored code and, if it matches, marks the number as verified.

Your system creates and checks the code, so the secret never leaves your side. The SMS gateway only delivers the message and reports its status.

Create and store the code

Use a cryptographically secure random number, keep only a hash of it, and give it a short life. Five minutes is enough for a real user.

PHP
$code = (string) random_int(100000, 999999);

$_SESSION['otp'] = [
    'phone'   => $phone,
    'hash'    => password_hash($code, PASSWORD_DEFAULT),
    'expires' => time() + 300,   // 5 minutes
    'tries'   => 0,
];

A session works for a website. For a mobile app or an API, store the same four values in your database or cache, keyed by the phone number.

Send the code

One request to the send endpoint delivers it. Single messages are handed to the mobile network before the API replies, so the code is on its way by the time your page reloads. The send_sms() function is the one from how to send SMS from PHP.

PHP
send_sms(
    $phone,
    "Your MYSHOP code is $code. Valid for 5 minutes. Do not share it.",
    ['ref' => 'otp-' . $userId]
);

Keep the text short and plain. Name your brand, give the code, say how long it lasts, and tell the customer not to share it. That message fits in one part.

Check what the customer typed

PHP
$otp = $_SESSION['otp'] ?? null;

if (!$otp || time() > $otp['expires'] || $otp['tries'] >= 5) {
    // expired, or too many wrong attempts: ask for a new code
    unset($_SESSION['otp']);
} elseif (password_verify($typed, $otp['hash'])) {
    unset($_SESSION['otp']);
    // success: $otp['phone'] is verified
} else {
    $_SESSION['otp']['tries']++;
}

Rules that keep OTP safe

  • Limit attempts. After five wrong codes, throw the code away. A six-digit code is only safe if it cannot be guessed many times.
  • Limit resends. Allow a new code only after a short wait, for example 60 seconds, and cap the number of codes per phone number per hour. This also protects your SMS balance from abuse.
  • Use each code once. Delete it as soon as it has been accepted.
  • Do not log the code. Keep it out of log files and analytics.
  • Do not reveal whether a number is registered on a password-reset form. Answer the same way in both cases.

When the code does not arrive

Give the customer a "send again" button that appears after your waiting time. On your side, the delivery status of every message is available from the API or a webhook, and messages that the network refuses are refunded automatically. If many codes fail for one number, it is usually switched off or typed wrongly, so let the customer correct it.

For pricing, speed and sender ID details, see OTP SMS for Sri Lanka. The same send function in other languages is in the Node.js and Python tutorials.

More guides

All guides

Ready to send your first SMS?

Open an account in two minutes, or ask us for a volume quote.