Secure and Scalable Delphi SMS Sender: Best Practices for Production

Delphi SMS Sender Tutorial — From Setup to Delivery ReportsThis tutorial walks you through building a robust Delphi SMS sender: setting up your environment, choosing an SMS gateway, implementing send/receive logic, handling delivery reports, retry and error strategies, security considerations, and testing tips. It targets Delphi 10.x (Tokyo and later) with FireMonkey (cross-platform) or VCL for Windows. Examples use modern Indy (TIdHTTP) or native REST client components and demonstrate both synchronous and asynchronous patterns.


1. Overview and prerequisites

Sending SMS from a Delphi application typically involves:

  • Integrating with an SMS gateway provider (HTTP REST API, SMPP, or SMTP-to-SMS).
  • Implementing HTTP requests or SMPP client logic.
  • Tracking message IDs and processing delivery reports (DLRs) returned via callbacks or polling.
  • Logging, retrying failures, and ensuring secure credential handling.

Prerequisites:

  • Delphi 10.x or later (examples use Delphi 10.3+ syntax). VCL for Windows or FireMonkey for cross-platform.
  • An SMS gateway account (examples assume a generic REST API — adjust to your provider).
  • Basic familiarity with HTTP requests, JSON, and threading.

2. Choosing an SMS gateway

Common gateway options:

  • HTTP REST API: simplest for most apps; provider returns message IDs and supports callbacks for DLRs.
  • SMPP: lower-level protocol for high throughput; requires an SMPP client library.
  • SMTP-to-SMS: unreliable and limited — avoid for production.

Key selection criteria:

  • API documentation and SDKs
  • Delivery speed and SLA
  • Support for concatenated (long) SMS, Unicode, and binary data
  • Delivery report (DLR) support and DLR callback mechanisms
  • Pricing, throughput limits, and regional coverage
  • Security: API keys, IP allowlisting, TLS

3. Authentication & configuration

Most providers use API keys or HTTP Basic Auth. Keep credentials out of source control:

  • Use encrypted config files, OS keychains, or environment variables.
  • Configure TLS (HTTPS) endpoints only.
  • Optionally restrict API key by IP.

Example configuration structure (pseudo-JSON):

{   "api_base": "https://api.smsprovider.com/v1",   "api_key": "REDACTED",   "sender_id": "MyApp",   "max_retries": 3,   "timeout_seconds": 30 } 

4. Basic sending using HTTP REST (synchronous)

This example shows a simple synchronous send using Indy (TIdHTTP) and System.JSON. For clarity, adjust exception handling and logging for production.

uses   IdHTTP, IdSSL, IdSSLOpenSSL, System.JSON, System.Classes, System.SysUtils; function SendSMS_Sync(const BaseURL, ApiKey, FromNum, ToNum, MessageText: string): string; var   http: TIdHTTP;   ssl: TIdSSLIOHandlerSocketOpenSSL;   reqJson, respJson: TJSONObject;   respStr: TStringStream;   url: string; begin   http := TIdHTTP.Create(nil);   ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil);   respStr := TStringStream.Create('', TEncoding.UTF8);   reqJson := nil;   respJson := nil;   try     http.IOHandler := ssl;     http.Request.ContentType := 'application/json';     http.Request.CustomHeaders.Values['Authorization'] := 'Bearer ' + ApiKey;     url := BaseURL + '/messages';     reqJson := TJSONObject.Create;     reqJson.AddPair('from', FromNum);     reqJson.AddPair('to', ToNum);     reqJson.AddPair('text', MessageText);     http.Post(url, TStringStream.Create(reqJson.ToString, TEncoding.UTF8), respStr);     respJson := TJSONObject.ParseJSONValue(respStr.DataString) as TJSONObject;     // Assume provider returns {"message_id": "..."}     Result := respJson.GetValue('message_id').Value;   finally     respJson.Free;     reqJson.Free;     respStr.Free;     ssl.Free;     http.Free;   end; end; 

Notes:

  • Synchronous calls block the calling thread — use in background threads or for simple CLI/tools.
  • Parse and store the returned message_id for future delivery report correlation.

5. Asynchronous sending (non-blocking)

Use TTask (System.Threading) or background threads to avoid UI freeze.

uses   System.Threading, System.Classes, System.SysUtils; procedure SendSMS_Async(const BaseURL, ApiKey, FromNum, ToNum, MessageText: string; Callback: TProc<string, Exception>); begin   TTask.Run(     procedure     var       msgId: string;       ex: Exception;     begin       ex := nil;       try         msgId := SendSMS_Sync(BaseURL, ApiKey, FromNum, ToNum, MessageText);       except         on E: Exception do         begin           ex := E;           msgId := '';         end;       end;       if Assigned(Callback) then         TThread.Queue(nil,           procedure           begin             Callback(msgId, ex);           end         );     end   ); end; 

6. Handling message encoding and length

  • SMS default encoding is GSM 03.38. For characters outside GSM (e.g., Cyrillic, emoji) use UCS-2 (Unicode) which reduces per-segment payload.
  • Typical per-segment lengths:
    • GSM 7-bit: 160 chars (153 if concatenated)
    • UCS-2: 70 chars (67 if concatenated)
  • Providers often accept a flag or auto-detect encoding; include a “udh” or “encoding” parameter if needed.

7. Delivery reports (DLRs)

Delivery reports inform you whether a message reached the handset. Two common patterns:

  1. Callback/webhook: Provider performs HTTP POST to your endpoint with message_id, status, timestamp. Secure with IP allowlist, HMAC signature, or a token.
  2. Polling: Your app queries the provider’s status endpoint using message_id.

Webhook example (expected JSON payload):

{   "message_id": "abc123",   "to": "+1234567890",   "status": "delivered",   "timestamp": "2025-08-29T12:34:56Z",   "provider_status": "DELIVRD" } 

Server-side minimal webhook handler (Delphi using WebBroker or an MVC framework):

procedure TWebModule.SMSWebhookAction(Request: TWebRequest; Response: TWebResponse); var   body, msgId, status: string;   json: TJSONObject; begin   body := Request.Content;   json := TJSONObject.ParseJSONValue(body) as TJSONObject;   try     msgId := json.GetValue('message_id').Value;     status := json.GetValue('status').Value;     // Update DB: message status, timestamp, provider-specific fields     Response.StatusCode := 200;     Response.Content := 'OK';   finally     json.Free;   end; end; 

Security for webhooks:

  • Validate an HMAC signature header computed with your shared secret.
  • Verify the request IP against provider IPs or use mutual TLS if supported.
  • Return 200 quickly; perform heavy processing asynchronously.

8. Correlation and storage model

Keep a local table for messages:

Columns:

  • id (local UUID)
  • provider_message_id (string)
  • to_number (string)
  • from_number (string)
  • text (string)
  • encoding (string)
  • status (queued/sent/failed/delivered/undelivered)
  • sent_at (datetime)
  • status_at (datetime)
  • retries (int)
  • last_error (string)

Use provider_message_id to match incoming DLRs to local records. Always log full provider payload for troubleshooting.


9. Retries, rate limiting, and backoff

  • Apply exponential backoff for transient failures (HTTP 429, 5xx). Example backoff: base 2s, multiply by 2, cap at 1 minute.
  • Respect provider rate limits; implement client-side throttling (token bucket).
  • For permanent failures (invalid number, blacklisted), mark as failed and surface to users.

Example pseudo-code for retry:

retry := 0; repeat   try     Send();     Break;   except     on E: Exception do     begin       Inc(retry);       if retry > MaxRetries then raise;       Sleep(Min(60000, 2000 * (1 shl retry))); // exponential with cap     end;   end; until False; 

10. SMPP option (high throughput)

If you need high throughput and lower latency, SMPP is common. Delphi lacks built-in SMPP; choose a library:

  • Commercial or open-source SMPP clients exist — evaluate for maintenance and features.
  • SMPP requires more careful session and connection management (enquire_link, bind/unbind, submit_sm, deliver_sm).
  • SMPP supports TLVs and binary short message payloads (for concatenation and Unicode).

11. Monitoring & observability

  • Track metrics: messages sent, delivered, failed, average latency, and DLR lag.
  • Log request/response payloads (mask credentials).
  • Alert on rising failure rate or delivery delays.
  • Provide an administrative UI to resend, view raw DLRs, or export logs.

12. Security & compliance

  • Use HTTPS; rotate API keys regularly.
  • Mask/stash PII (phone numbers) per your privacy requirements.
  • Ensure opt-in/opt-out handling conforms to local regulations (TCPA, GDPR).
  • Store consent timestamps and message templates for audit.

13. Testing tips

  • Use provider’s sandbox/test mode when possible.
  • Test edge cases: unicode messages, long concatenated messages, invalid numbers, and DLR latencies.
  • Simulate webhooks locally with tools like ngrok or a staging endpoint.
  • Load-test with realistic rate limits to expose throttling issues.

14. Example full flow (summary)

  1. User composes message in app.
  2. App posts send request to your backend.
  3. Backend validates, stores message row (status = queued).
  4. Backend sends HTTP request to SMS provider; stores provider_message_id and sets status = sent (or failed).
  5. Provider delivers SMS; sends a DLR to your webhook or you poll status.
  6. Webhook updates local status to delivered/undelivered and logs provider metadata.
  7. Retry or manual intervention if undelivered.

15. Troubleshooting common issues

  • No DLRs: verify webhook URL, firewall, or provider configuration; check webhook logs.
  • Messages stuck in queued: inspect retry logic and provider error responses.
  • Incorrect encoding: ensure provider encoding flags or convert text to UCS-2 when needed.
  • High latency: check network, provider SLA, and throttling responses (429).

16. Libraries & tools

  • Indy (TIdHTTP) — widely available HTTP client.
  • REST.Client / REST.Response.Adapter — Delphi REST components for simpler implementations.
  • Third-party SMPP clients — search for actively maintained libraries.
  • ngrok/localtunnel — for webhook testing.

17. Sample checklist before going to production

  • Use account sandbox then production credentials.
  • Enforce TLS and secret management.
  • Configure webhook security (HMAC/IP whitelist).
  • Implement retries and rate limiting.
  • Log and monitor DLRs and failure rates.
  • Confirm opt-in/opt-out and legal compliance.

This guide gives you a practical path from initial setup through delivery reporting and production readiness. If you want, I can: provide a complete demo project for VCL or FireMonkey, create a webhook verification snippet with HMAC, or show an SMPP client integration example. Which would you like next?

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *