Skip to main content

Create a customer-facing purchase option portal

This tutorial describes how to create an automated, secure purchase option portal where customers can manage their purchase option information without interacting with merchants. Customers can use an email link or button to access the portal.


In this tutorial, you'll learn how to do the following tasks:

  • Expose a link or button to send an email
  • Authenticate and access the customer-facing portal
  • Make requests to the GraphQL Admin API to implement relevant views and forms on your app
  • Update a customer's payment method

Note
  • Most subscriptions, pre-order and try before you buy apps need to request API access through the Partner Dashboard. We give API access to apps that are designed according to our [principles for subscriptions, pre-order and TBYB apps] (/docs/apps/selling-strategies/purchase-options#shopifys-principles).
  • Public apps that use subscriptions, pre-order or TBYB need to meet specific requirements to be published on the Shopify App Store.
  • Custom apps created in the Shopify admin can't use subscriptions, pre-order or TBYB because these apps can't use extensions or request access to protected scopes. If you're building a solution for a single store, then build your custom app in the Partner Dashboard.

Your app requires the following access scopes:

  • write_products
  • read_customer_payment_methods
  • read_own_subscription_contracts
  • write_own_subscription_contracts

Anchor to Deferred purchase optionsDeferred purchase options

  • write_products
  • read_all_orders
  • read_customer_payment_methods
  • read_purchase_options
  • write_purchase_options
  • read_payment_mandate
  • write_payment_mandate

Provide a link or button that triggers an email to be sent to a customer. You should place a link or button in an area that's accessible to the customer.

You can provide a link or button in one of the following areas:

  • On the customer account page on the storefront

  • On the Order status page, by customizing the Order status page to expose a link or button

  • In an email, sent directly to the customer after the app receives an order creation webhook or a subscription contract creation webhook. The email should contain a secure login link for the customer. For example:

    https://example.com/apps/my-purchase-option-app/customer_area?customer_id={customer_id}&shop_id={shop_id}&authentication_token={authentication_token}
Caution

You shouldn't render the login link on the shop's storefront because untrusted JavaScript in themes and apps can read and steal information.


Anchor to Step 2: Authenticate and access the customer-facing portalStep 2: Authenticate and access the customer-facing portal

After you've exposed an action to send an email, the customer can use an email link to access the secure purchase option portal. Use an authentication scheme of your choice.

Note
  • The login link should be revocable. A fixed URL isn't safe to use because emails can be forwarded.
  • Just verifying the authenticity of the URL isn't a secure method. Ideally, a tuple that includes the shop, customer, and token should be stored in the database.
  • The token should be deleted whenever convenient. For example, you can expire the token after X minutes or when the customer signs off. In the case of a sign off, the customer can start the process again with another email that contains a new login URL. Learn about considerations for customer-facing portals.

Anchor to Step 3: Make requests to the GraphQL Admin APIStep 3: Make requests to the GraphQL Admin API

After clicking the link, the customer is redirected to an endpoint predetermined by the URL.

For example, if the following URL is provided:

https://example.com/apps/my-purchase-option-app/customer_area?customer_id={customer_id}&shop_id={shop_id}&authentication_token={authentication_token}

Then Shopify makes a request to the following endpoint:

https://my-purchase-option-app.com/app_proxy/customer_area?customer_id={customer_id}&shop_id={shop_id}&authentication_token={authentication_token}

The app can then implement the relevant views and forms. For example, the app might do the following implementation tasks:

  • Updating billing and shipping addresses
  • Pausing, unpausing, skipping, and canceling subscriptions
  • Canceling and modifying pre-orders

All endpoints are forwarded from the app proxy into your app, as long as the links and form actions start with https://example.com/apps/my-purchase-option-app/.

When your app receives the request, you can make further requests to the GraphQL Admin API. For example, you can use the sellingPlanCreate mutation and the subscriptionContractCreate mutation.


Anchor to Step 4: Update a customer's payment methodStep 4: Update a customer's payment method

For security reasons, customer payment methods, such as credit cards, can't be updated using the API. Customers need to authenticate their payment method where applicable or request a second email that redirects them to a secure flow where their payment method can be safely updated.

After a customer has updated their payment method, the information is available to the app using the same customer payment method ID.

The following request returns a secure URL to send to the customer to update their payment method:

Request: POST /admin/api/2025-07.json

mutation {
customerPaymentMethodGetUpdateUrl(customerPaymentMethodId: "b0761d88b49eba7568f3bcf795402080") {
updatePaymentMethodUrl
userErrors {
code
field
message
}
}
}

JSON response:

{
"data": {
"customerPaymentMethodGetUpdateUrl": {
"updatePaymentMethodUrl": {
"https:\\/\\/shop1.myshopify.io\\/payment_methods\\/confirm?key=eyJfcmFpbHMiOnsibWVzc2FnZSI6IklqZzRZalpqTVRNNU1UVTJZbVV6WVdRMlpHVXpaRFEwWm1ZM016UTBZV0kwSWc9PSIsImV4cCI6IjIwMjEtMDktMTVUMjE6NTE6NTUuNTIwWiIsInB1ciI6bnVsbH19--07cc836a6ce419a16d0e7ddfc514f586e48e8eae"
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 10,
"actualQueryCost": 10
}
}
}

If the response is an error message or if the customer needs to update their payment method at a later date, then you can use the customerPaymentMethodSendUpdateEmail mutation to send an email to the email address associated with the specified ID.

Request: POST /admin/api/2025-07.json

mutation {
customerPaymentMethodSendUpdateEmail(customerPaymentMethodId: "b0761d88b49eba7568f3bcf795402080") {
customer {
id
}
userErrors {
field
message
}
}
}

JSON response:

{
"data": {
"customerPaymentMethodSendUpdateEmail": {
"customer": {
"id": "gid://shopify/Customer/2"
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 10,
"actualQueryCost": 10
}
}
}

Was this page helpful?