OctoWiki
Vendor AdapterseSIM Vendors

Octopus (OCTO_ESIM) — federated eSIM

eSIM vendor adapter consuming another Octopus's /api/v1/esim — login-token auth, the airtight delivered=PROCESSING webhook rule, two-factor webhook security.

Quick facts

Code OCTO_ESIM · Line eSIM · Auth login-token (octopuscommon.TokenManager) · Model async — webhook is only a "poll now" nudge · Webhook HMAC + URL path token · Delivery activation_code + ICCID (from GET only) · Adapter services/external_vendors/esim/octopus/

The eSIM member of the OCTO federation family — consumes another Octopus's /api/v1/esim. Registered esim/factory.go:152. Default timeout 90s (matches DT One eSIM). Uses shared octopuscommon TokenManager + SelfLoopGuard. See the self-vendor memory.

Auth

Shared octopuscommon.NewTokenManager + bearerAuth. Credentials: host (→ APIBaseURL, falls back to Credentials["host"]), username, password, webhook_secret, self_base_url.

Catalog

GetOperators = GET /api/v1/esim/products; GetPlans = GET /api/v1/esim/products/{id}/variants (bare array or {"data":[...]}). Composite Plan.ID = "productID:variantID". ShouldSyncPlans()=false (provisioned directly).

Order create

PurchaseEsimPOST /api/v1/esim/orders {product_id, amount, client_reference} (composite PlanID → upstreamProductID() prefix; MerchantOrderIDclient_reference). 2xx → parse; unparseable success → leave status empty (PENDING); 400+duplicate → ErrDuplicateMerchantRef; transient (408/429/5xx) → PENDING; other 4xx → terminal FAILED.

Order status — the authoritative completion path

GetOrderStatus — by id GET /api/v1/esim/orders/{id}, or recovery by ?client_reference=. This GET returns activation_code; the webhook never does.

Webhook — the eSIM airtight rule (headline)

Status is derived from the event TYPE, not data.status:

  • esim.delivered → PROCESSING — a non-terminal "poll now" nudge. The webhook omits activation_code, so it cannot complete the order; ProcessEsimWebhook reduces PROCESSING to PENDING (a terminal short-circuit guards against regression), and the cron poll's GetOrderStatus fetches the code and completes it.
  • esim.failed → FAILED (terminal, triggers auto-refund); esim.cancelled → CANCELLED.
  • esim.installed/activated/depleted/unknown → rejected (ok=false) so lifecycle events can't regress an order.

This omission is intentional and mirrored by the upstream TriggerEsimEvent, which deliberately leaves activation_code out of the webhook data.

Two-factor webhook security

VerifyWebhookSignature (HMAC; blank ⇒ true) plus an unguessable URL path token — the handler reads webhook_path_token and constant-time compares, 401 on blank/mismatch. The signature is defence-in-depth.

Delivery

ActivationCode + ICCID are carried back — but only populated on a delivered GET, never from the webhook. LookupCreditPartyStatus is a graceful no-op (Success=false, nil err) so the lifecycle poller finds nothing and moves on.

Quirks

Composite PlanID; bare-array lists; write-only client_reference; delivered=PROCESSING nudge → cron poll completes; lifecycle events rejected; ShouldSyncPlans=false; two-factor webhook.

Tests

esim/octopus/octopus_esim_vendor_test.go, octopus_esim_fuzz_test.go, octopus_esim_soak_test.go (goleak + -race), octopus_esim_sandbox_test.go. E2E: test/clientapi/octopus_esim_orchestration_test.go (build tag orchestration, incl. bad path-token). Uses test/octopusfake/ — its eSIM engine renders activation_code/iccid only on a delivered GET, mirroring "webhook never carries the code." Webhook-body gotcha: use testhelpers.WithRawBody.

On this page