The customer paid. A QR code arrived. The phone still had no data.
That is not one bug. It is several machines that all returned success for their hop, and a radio that never attached.
This is the public sequence. GSMA Remote SIM Provisioning, consumer flavour. Not a walkthrough of any platform I have worked on, and not a substitute for the spec.
The diagram people draw
pay → QR → internetThat diagram is how a retail page is sold. It is not how the system works.
A plastic SIM was a chip you were handed, already written. An eSIM purchase is a commercial entitlement that might become a profile on a particular eUICC, which might then be enabled, which might then authenticate with a network. Each “might” is a different process, often a different vendor, and a different idea of “done”.
The sequence that actually has to finish
order / payment
|
v
SM-DP+ prepares a profile
|
v
activation code (QR, string, or operator app)
|
v
device LPA ──HTTPS──> SM-DP+
| (EID, matching ID)
v
profile installed on the eUICC
|
v
enable (installed is not enabled)
|
v
network attach (IMSI / AKA, then a session)If you only remember one sentence: HTTP 200 on any of those boxes is not connectivity.
What you actually bought
You bought a plan: coverage, validity, a data allowance, maybe a phone number. That lives in a billing system.
You did not buy a file that is the SIM. The credentials that used to live on plastic - IMSI, keys, operator files - are built later as an eSIM profile and bound to a specific eUICC.
Until that profile exists at an SM-DP+ (Subscription Manager Data Preparation), there is nothing for the phone to download. Charging the card before the profile is ready is a product decision. Treating the charge as proof the radio should work is a category error.
The QR is an address, not the profile
Consumer devices usually start from an activation code. The QR is just a way to type it. The usual shape:
LPA:1$<SM-DP+ host>$<MatchingId>Sometimes a confirmation code is a fourth field. Sometimes there is no QR at all: the operator already has the device EID (the eUICC’s identifier) and the download is triggered by an app, or discovered via SM-DS.
What that string does not contain: the IMSI, the Ki, the profile. It tells the phone’s LPA (Local Profile Assistant) which SM-DP+ to call and which pending profile to ask for.
If you print the QR before the SM-DP+ has the profile, the scan fails in a way that looks like “the QR is broken”. The QR is fine. The upstream work is not finished.
Download, install, enable
The LPA talks to the SM-DP+ over HTTPS. They authenticate. If the matching ID (and optional confirmation code) line up, the profile is downloaded and installed onto the eUICC.
Installed is a state. Enabled is another one.
A consumer eUICC can hold more than one installed profile. Typically only one is enabled. Enabling is the switch that tells the modem which credentials to use. Users tap that switch, or an app does, or it happens as part of first install. If they do not, you have a successful download and a phone that is still on the old line - or on nothing.
Then the modem still has to attach: authenticate with the network, get a session, hit an APN that exists. Coverage, roaming agreements, device compatibility, and a plan that is actually active can all fail here. None of that is visible as a Laravel validation error.
Where backends get this wrong
I will not name vendors. The failure modes are public because they are structural.
Idempotency. A retry after payment must not mint a second profile for the same order. Message brokers redeliver. Users double-tap. The SM-DP+ will happily prepare two profiles if you ask twice. Related: what idempotent means.
Ready vs paid. The order can be paid while the profile is not_prepared. Showing the QR on the success page because the charge succeeded is how you generate support tickets that look like “the QR doesn’t work”.
Expiry. Unused profiles at an SM-DP+ do not live forever. A code emailed on Monday and scanned on Friday can be dead for a reason that is not the phone.
The wrong hop in the logs. “The API succeeded” is not a diagnosis. Which API? The commerce one, the RSP one, the download, or nothing because the user never enabled? Silent success on the wrong layer is the same class of bug as a 421 Laravel never emitted.
Attach is not your JSON. A profile can be enabled and the radio can still fail to camp. That is a network / device / plan problem wearing an app-support costume.
What I want this page to be for
A map. So that “buy an eSIM” stops meaning one function call.
The hard part is not drawing the QR. The hard part is the same hard part as the rest of production: a side effect that must not happen twice, a status that must mean one thing, and a customer who is looking at a phone, not at your dashboard.