smpp-connector — Application Logic
Status: populated | Last updated: 2026-04-18
1. Use Cases
UC-01: Establish SMPP Session
Trigger: Service startup or reconnect timer fires after an UNBOUND event.
Input: operatorId (configured via environment or dynamic operator list)
Steps:
- Call
operator-management-serviceinternal API:GET /internal/operators/{operatorId}/smpp-credentials - Receive
{ host, port, systemId, password, tpsLimit, bindMode }. - Open TCP socket to
host:port. - Send
bind_transceiverPDU withsystemIdandpassword. - Wait for
bind_transceiver_resp(timeout: 10 s). - On success: transition state to
BOUND; publishoperator.healtheventBOUND. - On failure / timeout: increment
reconnectAttempts; schedule reconnect with backoff; publishoperator.healtheventUNBOUNDif first failure. - If MNO rejects
bind_transceiverwith error codeESME_RINVCMDID(not supported): retry withbind_transmitter+bind_receiverpair.
UC-02: Transmit SMS Dispatch Command
Trigger: NATS message received on smpp.operator.{operatorId}.
Input: DispatchCommand
Steps:
- Validate
DispatchCommandfields (non-emptyto,text, validoperatorId). - Check session state; if not
BOUND→ NAK the NATS message (requeue). - Check TPS: call
TpsThrottleService.isAllowed(operatorId, tpsLimit).- If rate limit exceeded → NAK with 500 ms delay (NATS
NakDelay).
- If rate limit exceeded → NAK with 500 ms delay (NATS
- Determine encoding: if text contains non-GSM7 characters → use
UCS2 (0x08); otherwiseGSM7 (0x00). - If message text requires segmentation (> 160 GSM7 chars or > 70 UCS2 chars):
- Apply
longMessageStrategy(CSMS or TLV):- CSMS: split into segments with SAR UDH headers; send multiple
submit_smPDUs. - TLV: send single
submit_smwithmessage_payloadoptional parameter.
- CSMS: split into segments with SAR UDH headers; send multiple
- Apply
- Send
submit_smPDU(s). - Wait for
submit_sm_resp(timeout: 30 s).- On
ESME_ROK: extractmessage_id; writeMessageCorrelationrecord to PostgreSQL. - On transient error (e.g.
ESME_RTHROTTLED): NAK and requeue. - On permanent error (e.g.
ESME_RINVDSTADR): ACK (do not requeue); publishsms.dlr.inboundwith stateUNDELIVERABLE.
- On
- ACK NATS message.
UC-03: Receive DLR (deliver_sm)
Trigger: MNO sends a deliver_sm PDU containing a delivery receipt.
Input: DeliverSmPdu (from SMPP session TCP buffer)
Steps:
- Parse
deliver_smPDU; extractreceipted_message_idandmessage_statefrom UDH orshort_messagetext. - Look up
MessageCorrelationbyoperatorMessageId = receipted_message_idANDoperatorId. - If correlation not found → log
warndlr.correlation.not_found; senddeliver_sm_resp ESME_RMSGQFUL; discard. - Map SMPP
message_stateenum to internalDlrMessageState. - Publish
sms.dlr.inboundNATS event with{ messageId, operatorMessageId, status, operatorId, deliveredAt }. - Send
deliver_sm_resp ESME_ROKto MNO. - Delete
MessageCorrelationrecord (or mark ascompletedif audit retention required).
UC-04: enquire_link Heartbeat
Trigger: 30 s interval timer per active SMPP session.
Input: Active SmppSession
Steps:
- Send
enquire_linkPDU. - Wait for
enquire_link_resp(timeout: 10 s). - On success: reset heartbeat timer; no action.
- On timeout / no response: transition session to
UNBOUND; publishoperator.healthUNBOUND event; initiate reconnect sequence (UC-01).
UC-05: Primary/Backup Failover
Trigger: Primary operator session transitions to UNBOUND.
Steps:
- Mark primary operator as
UNBOUNDin session map. - If a backup operator is configured for this operator:
- Initiate SMPP session with backup operator (UC-01).
- Re-route pending NATS messages to backup operator subject
smpp.operator.{backupOperatorId}— this is done bysms-orchestratoracting on theoperator.healthUNBOUND event from this service. - When primary operator recovers (successful bind): publish
operator.healthFAILBACK event;sms-orchestratorandrouting-enginewill re-evaluate routing.
UC-06: TPS Throttling (Redis Sliding Window)
Trigger: Every submit_sm attempt.
Steps:
- Compute
windowStart = Math.floor(Date.now() / 1000)(1-second window). - Redis pipeline:
INCR tps:{operatorId}:{windowStart}EXPIRE tps:{operatorId}:{windowStart} 2
- Read returned count from
INCR. - If count >
tpsLimit→ deny (caller NAKs NATS message). - If count <=
tpsLimit→ allow.