Steps to reproduce
- Add a manual payment method to your store
- Add checkout-ui extension with this target:
target = "purchase.checkout.payment-method-list.render-before"
or
target = "purchase.checkout.payment-method-list.render-after"
- Activate the app extension in your checkout (only availble in Shopify Plus)
- In your code add a callback to log the value of the selected payment method and available payment methods .e.g
export default extension("purchase.checkout.payment-method-list.render-before", (root, api) => {
const { availablePaymentOptions, selectedPaymentOptions, } = api;
console.log({ availablePaymentOptions, selectedPaymentOptions, })
availablePaymentOptions.subscribe((available)=>console.log({available}))
selectedPaymentOptions.subscribe((selected)=>console.log({selected}));
})
- Go to checkout and choose your manual payment method (pay attention to the logged values)
- Reload the page
After you reload the page the manual-payment method should be automatically selected again, but this time will have a different generic, value .e.g manual-payment-money order instead of manual-payment-123
Expected behaviour
The returned payment method ids should be the same no matter the situation.
Additional info:
Tested on safari,firefox and brave, dependencies:
"@shopify/cli": "3.92.1", "@shopify/ui-extensions": "2026.04.1",
Also tested on this:
"@shopify/cli": "3.92.1", "@shopify/ui-extensions": "2025.7.1",
Hi @David_Arbias
Apologies for the late response on this - in case this is still an issue for you, it looks like the changing IDs may in fact be expected behaviour.
The SelectedPaymentOption.handle is not a globally unique identifier (see docs here). It may be an identifier specific to the given checkout session or the current shop.
I’m not sure if this is expected behavior, if you can’t identify payment methods what would be the use of Payments API?
Hi @David_Arbias
The SelectedPaymentOption.handle (and AvailablePaymentOption.handle) is:
- Unique within a given checkout session
- Opaque and not guaranteed to be stable across sessions or reloads
So the API is designed for use cases like:
- “In this checkout, which option did the buyer select?”
- “Within this render of checkout, track or react to the currently selected payment option”
…but not for:
- “Persist this handle in my own system and expect it to represent the same method forever across all future checkouts”
That’s why the docs describe it as “the unique handle for the payment option” but explicitly note it’s not a globally unique identifier, and may be scoped to the checkout session or shop.
This example highlights exactly what I’m talking about, there’s no way to know which option he selected if the handle is random.
The type field on PaymentOption is the only stable, cross-session identifier in the API. For a manual payment method, that’s 'manualPayment'. But here’s the limitation you’ve identified: if a store has multiple manual payment methods (e.g. “Money Order” and “Bank Transfer”), they both resolve to type === 'manualPayment' and there’s currently no field in the API that exposes the payment method’s name or a stable unique ID to distinguish them across sessions.
So within a single checkout session, handle works fine as it’s stable from the moment the buyer lands on checkout through to order submission, so you can reliably answer “which option did they select right now?” Cross-session (including reloads), you lose that precision for methods that share a type.
This is a genuine gap in the current API design. The interface intentionally exposes only type and handle so no names, labels, or provider-specific identifiers are surfaced. I’ve logged this as a feature request - the specific ask would be a stable, human-readable identifier (name/slug) on PaymentOption that persists across sessions for the same payment method.