> Beta:
> Post-purchase checkout extensions are in beta and can be used without restrictions in a [development store](/docs/api/development-stores). To use post-purchase extensions on a live store, you need to [request access](/docs/apps/build/checkout/product-offers/build-a-post-purchase-offer#step-5-request-access).
This guide shows you how to create an upsell offer on the post-purchase page in checkout. An upsell offer enables buyers to add products to their order after they've completed their payment.

## What you'll learn
In this tutorial, you’ll learn how to do the following tasks:
- Generate a post-purchase extension with Shopify CLI.
- Run the extension locally and test it on a development store.
- Build the extension UI with the component library.
- Authenticate requests from the extension to your app server.
- Deploy your extension code to Shopify.
- Create a version of your extension and publish it.
- Request access to run the post-purchase checkout extension on a live store.
## Requirements
- You're familiar with [post-purchase checkout extensions](/docs/apps/build/checkout/product-offers#post-purchase-product-offers).
- You've created a [Partner account](https://www.shopify.com/partners) and a [development store](/docs/api/development-stores#create-a-development-store-to-test-your-app).
- You can [place test orders](https://help.shopify.com/manual/checkout-settings/) on your development store.
- You've [created an app that uses the Remix template](/docs/apps/build/scaffold-app).
## Step 1: Create the extension
To create a post-purchase checkout extension, you can use Shopify CLI, which generates starter code for building your extension and automates common development tasks.
In your app directory, run the following command to create a new post-purchase checkout extension:
You should now have a new extension directory that includes the extension script at `src/index.{file-extension}`.
### Install dependencies
Run the following command to install the dependencies required for the app server inside the extension directory [that you created](#step-1-create-the-extension):
## Step 2: Test the extension
Post-purchase checkout extensions in production are hosted and served by Shopify. During development and testing, you need to use a Shopify browser extension to render your local extensions in checkout.
As you're testing, the browser extension automatically updates the checkout URLs.
1. Navigate to your app directory.
2. Either start or restart your server to build and preview your app:
When you run the `dev` command, Shopify CLI builds and serves your app. It also instructs you through multiple configuration steps. If you've already run `dev` for this app, then some of these steps are skipped.
To learn about the processes that are executed when you run `dev`, refer to the [Shopify CLI command reference](/docs/api/shopify-cli/app/app-dev).
3. Press `p` to open the developer console. In the developer console page, click **View checkout_post_purchase preview instructions** next to your post-purchase extension.

3. Install the **Shopify Post-purchase Developer Tools** for [Chrome](https://chrome.google.com/webstore/detail/shopify-post-purchase-dev/nenmcifhoegealiiblnpihbnjenleong) browser extension.
4. Copy the post-purchase extension URL from the developer console.
5. In the browser extension settings, enter the extension URL from the developer console:

6. Place a test order in your development store and go through the checkout steps. The post-purchase UI is shown after the payment step, but before the **Order status** page:

## Step 3: Build the extension UI
The [UI components](/docs/api/checkout-extensions/post-purchase/components) for post-purchase checkout extensions are managed by Shopify to ensure that the checkout is fast, optimized for mobile, and integrated with Shop Pay.
Post-purchase extensions have access to the [Checkout::PostPurchase::ShouldRender](/docs/api/checkout-extensions/post-purchase/api#extensionpoints) and [Checkout::PostPurchase::Render](docs/api/checkout-extensions/post-purchase/api#extensionpoints) extension points.
The [extension points API](/docs/api/checkout-extensions/post-purchase/api) provides helper functions to allow you to calculate potential order totals, update the order, modify metafields, and more.
Update the `extension/my-post-purchase-ui-extension/src/index` file with the following code. Replace the `APP_URL` constant, at the top of the file, with your local tunnel URL:
### How the extension code works
The following sections explain how different parts of the example extension code work.
#### Fetch product data in `Checkout::PostPurchase::ShouldRender`
The `Checkout::PostPurchase::ShouldRender` handler pre-fetches product data from the server that's running locally. You pass the [token](/docs/api/checkout-extensions/post-purchase/jwt-specification) in the `Authorization` header so that you can authenticate it with `authenticate.public` in the Remix app, and POST the reference ID to the server. By doing this, you can verify that the request came from Shopify, and that the request is signed with the same secret key as the app.
The data returned from the server is then stored in the extension's storage.
#### Compute shipping and taxes using `calculateChangeset`
Shipping costs and taxes are calculated based on the shipping address.
The order changes are passed to the `calculateChangeset` function provided by the API. The returned data is used to update the UI.
#### Update the order using `applyChangeset`
The `acceptOffer` handler posts a request to the server to sign the `changes` as a JWT token. The request includes `inputData.token`, which the server uses to verify Shopify's request. The following code calls `applyChangeset` with the JWT token returned from the server.
## Step 3: Update the app server
Your app requires two endpoints to support the extension. The first endpoint is used to fetch the product data for the upsell. The second endpoint is used to sign the changeset, so Shopify can verify the request to edit the order that came from your app.
### Define upsell product data
Create a new file called `app/offer.server.js`, which defines a function that's used to fetch the product data for the upsell. For simplicity in this tutorial, you'll hardcode the product data. The following logic can be replaced with an API call to Shopify or your own database:
You can use the following GraphQL query to fetch the first product in your Shopify store. Update the `getOffer` function with the data returned from the query, or the information from the product page in your Shopify admin.
> Note:
> Install the [Shopify GraphiQL app](/docs/api/usage/api-exploration/admin-graphiql-explorer) to make GraphQL queries to your store.
### Define the offer and sign changeset endpoints
In the `app/routes` folder, define two new routes. The first route is used to fetch the product data that you previously defined for the upsell. The second route is used to sign the changeset, so Shopify can verify that the request came from your app.
Both routes will verify that the request came from Shopify by checking that the JWT token is signed by the app's secret key using `authenticate.public`.
Add the following files to your project:
You are now ready to test your upsell functionality extension. Refer to [Step 2](#step-2-test-the-extension) for testing in these instructions.

## Step 4: Deploy the extension
When you're ready to release your changes to users, you can create and release an [app version](/docs/apps/launch/deployment/app-versions). An app version is a snapshot of your app configuration and all extensions.
1. Navigate to your app directory.
2. Run the following command.
Optionally, you can provide a name or message for the version using the `--version` and `--message` flags.
Releasing an app version replaces the current active version that's served to stores that have your app installed. It might take several minutes for app users to be upgraded to the new version.
> Tip:
> If you want to create a version, but avoid releasing it to users, then run the `deploy` command with a `--no-release` flag.
>
> You can release the unreleased app version using Shopify CLI's [`release`](/docs/api/shopify-cli/app/app-release) command, or through the Partner Dashboard.
## Step 5: Request access
If you want to run the post-purchase checkout extension on a live store, then you need to request access. Before you request access, you need an unpublished app in a [development store](/docs/api/development-stores#create-a-development-store-to-test-your-app).
After your access request has been approved, any apps that you distribute through the Shopify App Store also need to be [submitted for review](/docs/apps/launch/app-store-review/review-process) in the Partner Dashboard. The review process is slightly different for existing apps and new apps.
> Note:
> All merchants can install apps from the Shopify App Store that use post-purchase checkout extensions. Only Plus merchants can install custom apps which use post-purchase checkout extensions.
**For existing apps:**
* You need to test your app using an unpublished app in a development store.
* To add the post-purchase checkout extension to a published app, you must submit the unpublished testing app for review in the Partner Dashboard.
* If the new app meets the stated requirements, then it will be approved and the post-purchase extension can be added to your existing published app.
**For new apps:**
* You need to test your app using an unpublished app in a development store.
* To add the post-purchase checkout extension to a published app, you must submit the unpublished testing app for review in the Partner Dashboard.
* If the new app meets the stated requirements, then it will be approved and listed in the Shopify App Store.
### Request access from the Partner Dashboard
1. From your Partner Dashboard, click **Apps**.
2. Click the name of the app that you want to change.
3. Click **API access**.
4. On the **Access post-purchase extensions** card, click **Request access**.
5. In the **Post-purchase** page that opens, describe why you're applying for access.
6. You'll see a banner that your request is under review. The review process can take up to seven days.
After your request is approved, you're notified in the Partner Dashboard that your app has full access to the post-purchase extensions.
## Next steps
- Learn how to create a [post-purchase subscription upsell](/docs/apps/build/checkout/product-offers/create-a-post-purchase-subscription).
- Review the post-purchase targets [API reference](/docs/api/checkout-extensions/post-purchase/api).