OctoWiki
Vendor AdapterseSIM Vendors

DT One eSIM

eSIM vendor adapter — DT One DVS service_id 13, async PIN purchase, activation_code/ICCID delivery, URL-path-token webhook auth, install/activation polling.

Quick facts

Code DTONE_ESIM (service_id=13) · Line eSIM · Auth HTTP Basic · Model async PIN purchase (create → poll) · Webhook URL path-token auth (no signature) · Delivery activation_code (LPA) + ICCID + QR · Adapter services/external_vendors/esim/dtone/

The eSIM sibling of DT One (top-up) — a separate adapter/package/DB row, distinguished on the wire only by service_id=13. Registered esim/factory.go:150. Default timeout 90s (so a caller deadline binds). See Order Lifecycle.

Auth

HTTP Basic (username/password, host). Unlike top-up, the eSIM seeder does seed a callback_url.../webhooks/esims/DTONE_ESIM/{webhook_path_token} — and a webhook_path_token (32-hex, 128-bit) which is the entire inbound-webhook auth (see below).

Catalog (plans)

  • GetOperatorsGET /v1/operators?service_id=13&per_page=100 → one operator per country/region.
  • GetPlans(operatorID)GET /v1/products?operator_id=...; mapPlan extracts DataAmountGB (first DATA/GB benefit) and ValidityDays (DAY unit), Price=Wholesale, RetailPrice=Retail. Plans have no min/max (fixed PIN products). ShouldSyncPlans()=true — eSIM plans ARE synced on the catalog cron (opposite of top-up).

Order create — async PIN purchase

PurchaseEsimPOST /v1/async/transactions {ExternalID, ProductID, AutoConfirm:true, CreditPartyIdentifier:{mobile_number: dummyNumber "+15417543010"}} — a PIN purchase targets no real phone, hence the dummy number. Same error tree as top-up (1007001→duplicate, transient→PENDING, parseable→FAILED). On success, if txn.Pin != nil it sets ActivationCode = Pin.Code (LPA string) and ICCID = Pin.Serial — though in production the PIN typically arrives later via GET/callback, not on create.

Order status / poll + install poll

  • GetOrderStatusGET /v1/transactions/?external_id={merchantOrderID} (1008004/empty → ErrOrderNotFound); extracts PIN + CompletedAt when present.
  • LookupCreditPartyStatus(iccid)POST /v1/lookup/credit-party-status {service_id:13, credit_party_identifier:{account_number: iccid}}installation_date + activation_date. Driven by jobs/esim_installation_poll_job.goservices/esim_order.go RefreshEsimInstallationStatus (only for DELIVERED orders with an ICCID), which persists InstalledAt/ActivatedAt and fires esim.installed/esim.activated lifecycle notifications.

Webhook — URL path-token auth (headline)

DT One can't send signature headers, so the inbound webhook is authenticated by an unguessable URL path token. Route POST /webhooks/esims/:vendor_code/:token; the handler loads vendor_attributes.webhook_path_token and does a constant-time compare (crypto/subtle) — 401 on blank/mismatch. VerifyWebhookSignature returns true (no signature). Note: rotating the token only affects newly-created transactions; in-flight ones keep validating against the old value.

ParseWebhook maps status + extracts PIN/CompletedAt. Persisted to vendor_webhook_logs (resource_type esim_order); unlike the top-up handler it does not pre-check idempotency before logging (relies on the terminal-state row lock in ProcessEsimWebhook).

Delivery

Artifacts are the activation_code (LPA, e.g. LPA:1$smdp.io$ABC123) and ICCID on TransactionPin{Code, Serial}. services/esim_order.go encrypts them before persistence and decrypts for QR rendering. Lifecycle: DELIVERED → installed → activated.

Balance / quirks

GetBalance = GET /v1/balances; Ping = GET /v1/services/13. Much logic (mapStatus, transient checks, sentinels) is duplicated from the top-up adapter (mapped to EsimStatus*); the eSIM HTTP error table is a smaller subset. The eSIM package imports the top-up types package for the ErrDuplicateMerchantRef/ErrOrderNotFound sentinels.

Tests, docs

Unit tests dtone_esim_vendor_test.go + error_messages_test.go (pure httptest). Mocky route shared (frontend/mocky-balboa/src/routes/dtone.ts). Docs docs/dtone/ (incl. esim_webhook.json).

On this page