Implementing SMS Verification with a Singleton Pattern in Flask

The SMS sending functionality is refactored using the Singleton pattern to ensure the REST SDK is initialized only once. The sms.py module wraps the cloud communication SDK:

# -*- coding: utf-8 -*-

import logging
from CCPRestSDK import REST

# Credentials and configuration
ACCOUNT_SID = '8aaf07085f9eb021015fb58c56d906e8'
ACCOUNT_TOKEN = 'e21ca96da61c49ee97e0fdf1ba47de5a'
APP_ID = '8aaf07085f9eb021015fb58c574106ef'
SERVER_IP = 'app.cloopen.com'
SERVER_PORT = '8883'
SDK_VERSION = '2013-12-26'


class SMSManager:
    """Singleton wrapper for cloud communication REST SDK."""
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            instance = super().__new__(cls)
            instance.sdk = REST(SERVER_IP, SERVER_PORT, SDK_VERSION)
            instance.sdk.setAccount(ACCOUNT_SID, ACCOUNT_TOKEN)
            instance.sdk.setAppId(APP_ID)
            cls._instance = instance
        return cls._instance

    def dispatch_template_message(self, phone_number, payload, template_id):
        """Send a template-based SMS and return 0 on success, -1 on failure."""
        try:
            response = self.sdk.sendTemplateSMS(phone_number, payload, template_id)
        except Exception as exc:
            logging.error(exc)
            raise

        logging.debug(response)
        return 0 if response.get('statusCode') == '000000' else -1


if __name__ == '__main__':
    sender = SMSManager()
    status = sender.dispatch_template_message('17610812003', ['1234', 5], 1)
    print('Dispatch result:', status)

A response_code.py file defines standardized response codes and messages:

# -*- coding: utf-8 -*-


class StatusCode:
    SUCCESS = '0'
    DB_ERROR = '4001'
    NO_RECORD = '4002'
    RECORD_EXISTS = '4003'
    INVALID_DATA = '4004'
    SESSION_MISSING = '4101'
    AUTH_FAIL = '4102'
    BAD_PARAM = '4103'
    USER_MISSING = '4104'
    ROLE_ISSUE = '4105'
    WRONG_PASSWORD = '4106'
    BAD_REQUEST = '4201'
    IP_BLOCKED = '4202'
    THIRD_PARTY_FAIL = '4301'
    IO_FAIL = '4302'
    INTERNAL_ERROR = '4500'
    UNKNOWN = '4501'


message_map = {
    StatusCode.SUCCESS: u'Success',
    StatusCode.DB_ERROR: u'Database query error',
    StatusCode.NO_RECORD: u'No data found',
    StatusCode.RECORD_EXISTS: u'Data already exists',
    StatusCode.INVALID_DATA: u'Invalid data',
    StatusCode.SESSION_MISSING: u'User not logged in',
    StatusCode.AUTH_FAIL: u'Login failed',
    StatusCode.BAD_PARAM: u'Parameter error',
    StatusCode.USER_MISSING: u'User does not exist or is inactive',
    StatusCode.ROLE_ISSUE: u'Incorrect user role',
    StatusCode.WRONG_PASSWORD: u'Wrong password',
    StatusCode.BAD_REQUEST: u'Illegal request or rate limit exceeded',
    StatusCode.IP_BLOCKED: u'IP restricted',
    StatusCode.THIRD_PARTY_FAIL: u'Third-party service error',
    StatusCode.IO_FAIL: u'File read/write error',
    StatusCode.INTERNAL_ERROR: u'Internal server error',
    StatusCode.UNKNOWN: u'Unknown error',
}

The API endpoint for sending a verificatoin code is implemented in api_1_0/verify_code.py:

from flask import request, jsonify
from api_1_0 import api
from models import User
from sms import SMSManager
from redis_client import redis_store
from constants import SMS_CODE_REDIS_EXPIRES
import random
import logging


@api.route('/sms_codes/<regex("1[34578]\d{9}"):mobile>')
def send_verification_code(mobile):
    input_image_code = request.args.get('image_code')
    image_uuid = request.args.get('image_code_id')

    if not all([input_image_code, image_uuid]):
        return jsonify(errno=StatusCode.BAD_PARAM, errmsg='Incomplete parameters')

    try:
        stored_code = redis_store.get('image_code_' + image_uuid)
    except Exception as exc:
        logging.error(exc)
        return jsonify(errno=StatusCode.DB_ERROR, errmsg='Failed to retrieve image code')

    if stored_code is None:
        return jsonify(errno=StatusCode.NO_RECORD, errmsg='Image code expired or invalid')

    try:
        redis_store.delete('image_code_' + image_uuid)
    except Exception as exc:
        logging.error(exc)

    if stored_code.lower() != input_image_code.lower():
        return jsonify(errno=StatusCode.INVALID_DATA, errmsg='Incorrect image code')

    try:
        existing_user = User.query.filter_by(mobile=mobile).first()
    except Exception as exc:
        logging.error(exc)
    else:
        if existing_user:
            return jsonify(errno=StatusCode.RECORD_EXISTS, errmsg='Mobile already registered')

    verification_code = '%06d' % random.randint(0, 999999)

    try:
        redis_store.setex('sms_code_' + mobile, SMS_CODE_REDIS_EXPIRES, verification_code)
    except Exception as exc:
        logging.error(exc)
        return jsonify(errno=StatusCode.DB_ERROR, errmsg='Failed to save verification code')

    try:
        sms_service = SMSManager()
        result = sms_service.dispatch_template_message(
            mobile,
            [verification_code, SMS_CODE_REDIS_EXPIRES // 60],
            1
        )
    except Exception as exc:
        logging.error(exc)
        return jsonify(errno=StatusCode.THIRD_PARTY_FAIL, errmsg='SMS dispatch failed')

    if result == 0:
        return jsonify(errno=StatusCode.SUCCESS, errmsg='SMS sent successfully')
    return jsonify(errno=StatusCode.THIRD_PARTY_FAIL, errmsg='SMS sending failed')

The frontend handles the API response and manages a countdown timer:

var requestData = {
    image_code_id: imageCodeId,
    image_code: imageCode
};

$.get('/api/v1_0/sms_codes/' + mobile, requestData, function (response) {
    if (response.errno === '4004' || response.errno === '4002') {
        $('#image-code-err span').html(response.errmsg);
        $('#image-code-err').show();
        $('.phonecode-a').attr('onclick', 'sendSMSCode();');
    } else if (response.errno === '0') {
        var button = $('.phonecode-a');
        var seconds = 60;
        var timer = setInterval(function () {
            button.html(seconds + ' seconds');
            if (seconds === 1) {
                clearInterval(timer);
                button.html('Get code');
                $('.phonecode-a').attr('onclick', 'sendSMSCode();');
            }
            seconds -= 1;
        }, 1000);
    } else {
        alert(response.errmsg);
    }
});

Tags: Flask SMS Verification Singleton Pattern REST API python

Posted on Tue, 14 Jul 2026 16:32:17 +0000 by billabong0202