Appearance
SMS Integrations (Yuboto, Routee, Plivo, Bulker, Apifon, OmniMessaging)
Flow ID: IN-14 | Module(s):
ecommercen/helpers/,src/Yuboto/,src/Bulker/,src/Apifon/,src/Omni/| Complexity: High
Business Overview
Ecommercen sends SMS (and optionally Viber) notifications to customers for order status updates, primarily when an order is ready for pickup ("from store" status). The platform supports six SMS providers through a unified dispatcher function, allowing each client to select their preferred provider via configuration.
The SMS system handles two distinct use cases:
- Order-triggered SMS (
sendSms()) -- Sends a predefined order notification message with special Friday/Saturday delivery handling. Records the message in customer communication history. - Generic SMS dispatch (
sendSmsMsg()) -- Sends arbitrary SMS/Viber messages through the configured provider. Used by jobs, gift card notifications, and other subsystems.
All providers share a common phone validation and normalization layer optimized for Greek mobile numbers.
API Reference
Unified Dispatcher Functions
| Function | Parameters | Description |
|---|---|---|
sendSms($orderSerial, $phone, $provider, $extra) | string, string, string, array | Order notification SMS with history logging |
sendSmsMsg($msg, $viberMsg, $phone, $provider, $extra) | string, string, string, string, array | Generic SMS/Viber dispatch |
isValidPhoneForSms($phone) | string | Validates Greek mobile number format |
convertToValidPhoneForSms($phone) | string | Normalizes to 30xx format |
getFirstValidPhoneForSms($phones) | array | Returns first valid phone from array |
getFirstValidPhoneForSmsFormatted($phones) | array | Returns first valid phone, normalized |
getValidPhoneForPayway($payway, ...) | string, strings | Returns valid phone for specific payment methods |
Provider-Specific APIs
| Provider | Class | Channels | Features |
|---|---|---|---|
| Yuboto | Legacy helper functions | SMS | Callback URL support |
| YubotoOmni | Advisable\Yuboto\YubotoOmni | SMS + Viber | Priority-based channel selection |
| OmniMessaging | Advisable\Omni\OmniMessaging | SMS + Viber | Transaction ID tracking, validity periods |
| Routee | Legacy helper functions | SMS, SMS+Viber | DLR callback URL |
| Plivo | Legacy helper functions | SMS only | Basic send |
| Bulker | Advisable\Bulker\BulkerSms | SMS only | Status polling, delivery reports |
| Apifon | Advisable\Apifon\Apifon | SMS + Viber | OAuth2, TTL-based Viber-to-SMS fallback |
Code Flow
Order Notification SMS
Order status changes to "from_store"
-> AdvSendEmailAndSMSBasedOnFromStoreStatus job (or controller)
-> sendSms($orderSerial, $phone, $provider, $extra)
-> Determine message based on day of week:
- Friday: Saturday delivery message (t('sms.order.message.saturday.*'))
- Other days: Standard message (t('sms.order.message.*'))
-> Build Viber message variant (t('viber.order.from_store.message*'))
-> Log to customer_message_history_model (type: ORDER_ON_STORE, channel: SMS)
-> Route to provider via switch/caseProvider Dispatch Flow
sendSmsMsg($msg, $viberMsg, $phone, $provider, $extra)
switch ($provider):
'plivo' -> load plivo_helper -> sendSmsPlivo($msg, $phone)
'routee' -> load routee_helper -> sendSmsRoutee($msg, $phone, $callbackUrl)
'routeeViber' -> load routee_helper -> sendViberSmsRoutee($msg, $phone, $callbackUrl)
'yuboto' -> load yuboto_helper -> sendSmsYuboto($msg, $phone, $callbackUrl)
'yuboto_omni' -> load yuboto_omni config -> YubotoOmni::sendMessage($msg, $viberMsg, $phone, $callbackUrl)
'omni_messaging'-> load omni config -> OmniMessaging::sendMessage($msg, $viberMsg, $phone, $orderId)
'bulker' -> load bulker config -> BulkerSms::sendMessage($msg, $phone, $orderId)
'apifon' -> (registry-based) -> Apifon::sendMessage($phone, $msg, $viberMsg)YubotoOmni Channel Priority
YubotoOmni::sendMessage($msg, $viberMsg, $phone, $callbackUrl)
-> Build JSON payload:
- phonenumbers: $phone
- dlr: true if callbackUrl provided
-> If Viber message provided:
- viber: { sender, text, priority: 0 } (primary)
-> If SMS message provided:
- sms: { sender, text, priority: 1 if Viber exists, else 0 } (fallback)
-> POST to {apiBaseUrl}/omni/v1/Send
-> Auth: Basic (base64-encoded API key)
-> Check response: ErrorCode == 0, Message[0].status not Error/FailedOmniMessaging Dual Channel
OmniMessaging::sendMessage($msg, $viberMsg, $phone, $orderId)
-> Build JSON payload:
- transaction_id: $orderId
- channels: [
{ viber: { message: { text }, validity_period: 10 } },
{ sms: { from: sender_name, text, encoding: 0, validity_period: 120 } }
]
- destinations: [{ phone_number }]
-> POST to {apiBaseUrl}/sendings
-> Auth: Basic (base64-encoded userID:authKey)Apifon SMS with Viber Fallback
Apifon::sendMessage($to, $msg, $viberMsg)
-> authenticate() if not already authenticated (OAuth2 client_credentials)
-> If enabledIm (Viber enabled):
- POST to /services/api/v1/im/send
- Include im_channels with TTL for Viber-to-SMS fallback
-> Else:
- POST to /services/api/v1/sms/send
-> Body: message (text, sender_id), subscribers (number)Bulker Status Polling
AdvGetBulkerSmsStatus job (cron)
-> Query orders with sms_status = 4 (buffered)
-> For each order:
-> BulkerSms::getSmsStatus($orderId)
-> GET {apiBaseUrl}/dlr.php?auth_key={key}&id={orderId}
-> Map response: 1 -> delivered(2), 2 -> undelivered(3), 4 -> buffered(4)
-> Update shop_order.sms_statusArchitecture
ecommercen/helpers/
sms_helper.php # Unified dispatcher (sendSms, sendSmsMsg, phone validation)
yuboto_helper.php # Yuboto legacy SMS helper
routee_helper.php # Routee SMS/Viber helper
plivo_helper.php # Plivo SMS helper
src/Yuboto/
YubotoOmni.php # YubotoOmni SMS+Viber client (priority-based channels)
src/Omni/
OmniMessaging.php # OmniMessaging SMS+Viber client (dual channel)
src/Bulker/
BulkerSms.php # Bulker SMS client with delivery status polling
src/Apifon/
Apifon.php # Apifon OAuth2 SMS+Viber client
ecommercen/job/libraries/
AdvSendEmailAndSMSBasedOnFromStoreStatus.php # Job: SMS on from_store status
AdvSendEmailAndSMSBasedOnSentStatus.php # Job: SMS on sent status
AdvGetBulkerSmsStatus.php # Job: Bulker delivery status polling
ecommercen/gift_cards/jobs/
GiftCardSendSms.php # Gift card SMS notification
application/libraries/
Yuboto.php # Client-overridable Yuboto library
Routee.php # Client-overridable Routee library
Bulker.php # Client-overridable Bulker library
application/config/
yuboto.php # Yuboto legacy config
yuboto_omni.php # YubotoOmni config
routee.php # Routee config
plivo.php # Plivo config
bulker.php # Bulker configProvider Authentication
| Provider | Auth Method | Details |
|---|---|---|
| Yuboto (legacy) | Config-based | API key in helper config |
| YubotoOmni | Basic Auth | Base64-encoded API key |
| OmniMessaging | Basic Auth | Base64-encoded userID:authKey |
| Routee | Config-based | Via routee_helper config |
| Plivo | Config-based | Via plivo_helper config |
| Bulker | Query parameter | auth_key in URL |
| Apifon | OAuth2 Bearer | client_credentials grant, Bearer token |
Data Model
Tables
| Table | Column | Description |
|---|---|---|
shop_order | sms_status | SMS delivery status: 1=sent, 2=delivered, 3=undelivered, 4=buffered |
customer_message_history | Various | Communication log: customer_id, type (ORDER_ON_STORE), channel (SMS), subject, body |
SMS Status Values
| Value | Meaning | Context |
|---|---|---|
| 1 | Sent | Initial state after successful send |
| 2 | Delivered | Confirmed delivery (Bulker polling) |
| 3 | Undelivered | Failed delivery (Bulker polling) |
| 4 | Buffered | In transit, pending confirmation |
Configuration
Provider Config Files
| Provider | Config File | Key Settings |
|---|---|---|
| Yuboto | application/config/yuboto.php | API URL, sender name, API key |
| YubotoOmni | application/config/yuboto_omni.php | apiBaseUrl, apiKey, smsSender, viberSender, timeout |
| Routee | application/config/routee.php | API credentials, sender |
| Plivo | application/config/plivo.php | Auth ID, auth token, sender |
| Bulker | application/config/bulker.php | apiBaseUrl, auth_key, sender_name |
Apifon (Registry)
| Group | Key | Description |
|---|---|---|
APIFON | ENABLED | Enable/disable Apifon |
APIFON | SENDER_ID | SMS sender name |
APIFON | TTL | Viber TTL before SMS fallback (seconds) |
APIFON | ENABLE_IM | Enable Viber channel |
APIFON | API_TOKEN | OAuth2 client_id |
APIFON | API_KEY | OAuth2 client_secret |
Cron Jobs
| Job | Schedule | Description |
|---|---|---|
AdvSendEmailAndSMSBasedOnFromStoreStatus | Event-driven / cron | Sends SMS when order reaches from_store status |
AdvSendEmailAndSMSBasedOnSentStatus | Event-driven / cron | Sends SMS when order is shipped |
AdvGetBulkerSmsStatus | Periodic | Polls Bulker API for delivery status of buffered messages |
Client Extension Points
Provider Selection
The SMS provider is selected per client via the $provider parameter passed to sendSms() / sendSmsMsg(). The calling controller or job determines the provider based on client configuration.
Phone Validation
The isValidPhoneForSms() and convertToValidPhoneForSms() functions are Greece-specific (3069/003069/69 prefix patterns). Clients outside Greece must override these functions in application/helpers/sms_helper.php. The source code contains TODO comments acknowledging this limitation.
Message Templates
SMS and Viber message content is managed through the language system:
sms.order.message.prefix/sms.order.message.suffix-- Standard SMSsms.order.message.saturday.prefix/sms.order.message.saturday.suffix-- Saturday delivery SMSviber.order.from_store.message/viber.order.from_store.message.saturday-- Viber messages
Override these in application/language/{lang}/ for custom message wording.
Custom Providers
Add a new provider by:
- Adding a new
caseinsendSmsMsg()switch statement (client repo: overridesms_helper.php) - Creating the provider class in
application/libraries/orcustom/ - Adding config file in
application/config/
Business Rules
- Friday/Saturday handling: If the SMS is triggered on a Friday (
dayOfWeek == 5), a different message template is used to inform the customer about Saturday delivery. - Phone normalization: Greek mobile numbers are accepted in three formats and normalized to the international
30xxformat:3069xxxxxxxx(12 digits) -- already normalized003069xxxxxxxx(14 digits) -- strip leading0069xxxxxxxx(10 digits) -- prepend30
- Communication history: Every order SMS is logged in
customer_message_historyfor audit and customer service reference. - Viber priority: YubotoOmni sends Viber first (priority 0), with SMS as fallback (priority 1). Apifon uses a TTL-based approach: if Viber delivery is not confirmed within the TTL window, SMS is sent automatically.
- Payment-specific SMS:
getValidPhoneForPayway()only returns a valid phone forpaid_at_storeandpaybybankpayment methods, ensuring SMS is only sent for relevant order types. - Bulker status polling: Orders sent via Bulker have their delivery status polled by a cron job. The
sms_statusfield tracks the delivery lifecycle (sent -> delivered/undelivered/buffered). - Fire-and-forget: Most providers (except Bulker) operate on a fire-and-forget model -- the SMS is sent and no delivery confirmation is tracked beyond the initial success/failure response.
- Gift card SMS: Gift card activations can trigger SMS notifications via
GiftCardSendSms, using the same unified dispatcher.
Related Flows
- IN-13 Newsletter Integrations -- Apifon subscriber management
- CF-07 Order Confirmation -- Order status changes that trigger SMS
- SY-01 Cron Framework -- Cron scheduling for SMS jobs
- AD-13 Settings -- Admin configuration for SMS providers