New feature. Express Checkout shipping is available to all merchants. No allowlisting or account setup is required.What it covers today: Apple Pay and Google Pay, on the Web SDK (Primer Checkout). iOS and Android are coming.Because it is new, test your integration end to end in the sandbox before going live in production.
When to use it
Use it when shipping cost or availability depends on the destination and cannot be known when the client session is created. For example:- Physical goods priced by weight, zone, or carrier rate.
- Free-shipping thresholds that vary by region.
- Shipping choices that depend on state held in the browser, such as a selected store or a delivery window.
How it works
- The shopper enters an address. The checkout emits
primer:shipping-address-change. The sheet stays in a loading state. - Your page returns options by calling
setShippingOptions(). This is display only. Primer does not store the list and does not price anything from it. - An option is selected. The checkout emits
primer:shipping-option-changewith that option. This fires when the shopper picks one, and also for the option the sheet pre-selects, so expect it more than once per sheet session. - Your backend confirms it by PATCHing
order.shippingonto the client session, then your page callscontinue(). - Primer re-reads the session, checks that the confirmation matches the selection, recomputes
order.amount, and refreshes the sheet total. - The shopper authorizes. Authorization waits for the confirmation, so a payment can never be created against a shipping option your backend has not priced.
The two events are a pair.
primer:shipping-address-change is display only and never changes the amount. primer:shipping-option-change is the one that moves money, and it goes through your backend.Prerequisites
- Primer Checkout already integrated. See First payment.
@primer-io/primer-js1.9.2 or later. Earlier versions do not dispatch these events at all.- Apple Pay and/or Google Pay configured in the Dashboard.
- An API key with
client_tokens:writefor the client session PATCH.
Step 1. Enable shipping in the payment sheet
Shipping selection is off by default. Turn it on for each wallet in the SDK options.Step 2. Supply options for the address
Listen forprimer:shipping-address-change and call setShippingOptions() with the options you want the sheet to show.
Event payload
primer:shipping-address-change is a CustomEvent. Its data is on event.detail.
The wallet gives you a partial address on purpose. If you price below postal code granularity, you cannot do it at this stage.
The ShippingOption shape
Step 3. Confirm the selection from your backend
When an option is selected, the checkout emitsprimer:shipping-option-change. Send the selection to your backend, which PATCHes it onto the client session, then call continue().
Event payload
The PATCH
Your backend PATCHesorder.shipping onto the client session:
Tax
Primer recomputes the order total as:taxAmount on each line item and re-send the full lineItems array in the same PATCH.
Two things to watch:
taxAmountis added once per line. It is not multiplied byquantity.- A top-level
order.taxAmountis not included in the recalculated total. Use per line item tax, or a fee.
Step 4. Primer verifies and refreshes
Aftercontinue(), the SDK re-reads the client session and checks that order.shipping.methodId and order.shipping.amount match the option that was selected. If they match, the sheet total updates and the shopper can authorize. If they do not, the attempt fails with an integration error.
Lifecycle and edge cases
Timeouts
Each callback has 20 seconds to respond. Apple and Google also apply their own limit inside the sheet, which we do not control, so treat 20 seconds as a ceiling rather than a budget. Keep rate calls and confirmations fast.When something goes wrong
Failures in this flow surface onprimer:payment-failure, like any other payment failure:
error.originisintegrationwhen the fix is on your side, andprimerwhen it is on ours.error.codeisREQUEST_TIMEOUTwhen a callback was not answered in time, andREQUEST_ERRORwhen the confirmation did not match the selection.error.suggestioncarries a short, actionable hint. Read it first.
No listener responded
If nothing responds, either because no listener is registered or because an async listener did not callpreventDefault(), the SDK logs a console warning and carries on by itself. No options are shown, no shipping is confirmed, and the shopper is charged the total without shipping.
If the amounts look wrong, check the browser console for a warning about primer:shipping-address-change or primer:shipping-option-change having no listener.
Retries
If a payment fails and the shopper reopens the wallet sheet, the shipping flow starts again from the address change. Options supplied in a previous attempt are not reused.Changing the address twice
If the shopper edits the address again before the previous confirmation has completed, that earlier confirmation is dropped and the flow restarts from the new address. Your backend can therefore receive a PATCH for an address the shopper has already moved on from. Always price from the address in the request you are handling, never from stored state.See also
Events reference
Full reference for the shipping events and every other checkout event
Apple Pay
Configure Apple Pay, including merchant identity and domain verification
Google Pay
Configure Google Pay
Update client session
API reference for the PATCH used to commit shipping