# Build a checkout extension

> Build a Shoplazza checkout extension with the shoplazza app command: initialize the project, create the extension, preview it on checkout, and publish it.

# Checkout Extension Development Guide

## Overview

Checkout Extension allows developers to inject custom content and business logic into the store checkout page without modifying theme code.

A Checkout Extension developed using the `shoplazza app` command supports the following two capabilities:

- **Insert custom content at specified extension points**: Render custom HTML at fixed positions on the checkout page — for example, displaying a prompt banner below contact information or adding explanatory text above the payment module.

- **Read checkout data and respond to changes**: Use the `CheckoutAPI` to obtain order, product, price, address, user, and other information, and listen for changes to execute custom logic.

### Development Methods Explained

- **Develop in an empty directory**: The generated extension can only be used by your own store and cannot be published for other merchants.

- **Develop in an existing app project**: Publish the extension as part of an app that other merchants can install and use. For a complete app development tutorial, see the [App Development Guide](/docs/app/getting-started/create-public-app/).

## What You'll Learn

- How to initialize and create a Checkout Extension using the CLI

- How to select an extension point and render custom content at a specific position on the checkout page

- How to use `extend` with an HTML template to create a minimal previewable extension

## Prerequisites

- [shoplazza-cli](https://www.npmjs.com/package/shoplazza-cli) installed and logged in to the store you develop on (`shoplazza app dev` pushes the extension there): `shoplazza auth login --store-domain <your-store>.myshoplaza.com --domain app`

- A Shoplazza developer account

- An existing shoplazza app project, or an empty directory for development

## Development Workflow

```mermaid
flowchart LR
    A[shoplazza app init --name: create app] --> B[shoplazza app extension create --type checkout: create extension]
    B --> C[Develop under src/]
    C --> D[shoplazza app dev: preview in Checkout Editor]
    D --> E{Distribute to other merchants?}
    E -- Not yet --> C
    E -- Yes --> F[Complete app development, then shoplazza app deploy]

```

### Step 1: Initialize

Run the following command in your app project root (or empty directory):

```bash
shoplazza app init --name "my-app"
```

This creates a new app named `my-app` and scaffolds its project as a subdirectory of the current directory.

:::tip Already have an app?
If you built your app following [Develop a standalone app](/docs/app/getting-started/create-public-app/develop-app), your project already has the CLI configuration — skip this step and continue from Step 2 in the project root.
:::

### Step 2: Create a Checkout Extension

Navigate to the generated app directory and run:

```bash
shoplazza app extension create --type checkout --name my-checkout
```

The extension is created in the `extensions/` directory. The directory structure looks like this:

```text
my-app/
└── extensions/
    └── my-checkout/
        ├── shoplazza.extension.toml   # Extension configuration file
        ├── extension.json             # Optional: hide native modules (deleteTarget)
        ├── package.json
        └── src/
            ├── index.js               # Extension entry point
            ├── index.html             # Main HTML template
            ├── style.html             # Styles
            ├── content.html           # Page content
            └── script.html            # Script logic
```

Develop your extension under the `src/` directory.

### Step 3: Start a Preview

After development, run the following command in the app directory:

```bash
shoplazza app dev
```

The extension will be automatically published to the bound store. You can then view the result in the store theme editor's "Checkout Editor."

`shoplazza app dev` pushes the extension once when it starts and does not watch for file changes — after each edit, stop it (Ctrl+C) and run it again, then refresh the Checkout Editor.

:::note
The extension shows up only if its app is installed on the store. If you haven't installed it yet, follow [Test app](/docs/app/getting-started/create-public-app/test-app) — if you haven't implemented the app itself, install via Scenario 1 there.
:::

## Extension Points Overview

Extension points determine the specific position on the checkout page where custom content is inserted. Specify the extension point in the `extensionPoint` field of `extend()`. Common extension points:

| Extension Point | Description |
|---|---|
| `Checkout::RenderBefore` | Before the page header |
| `Checkout::ContactInformation::RenderAfter` | After the contact information module |
| `Checkout::Summary::RenderBefore` | Before the order summary |
| `Checkout::SectionPayment::RenderBefore` | Before the payment module |

For a full list of extension points, see [Extension points](/docs/app/extensions/checkout/extension-points).

## A Minimal UI Example

Import `extend` from `shoplazza-extension-ui`, specify an extension point, and pass an HTML string to render content on the checkout page:

```javascript
import { extend } from 'shoplazza-extension-ui';

extend({
  extensionPoint: 'Checkout::RenderBefore',
  component: '<div style="padding:12px;background:#f5f5f5;">Hello, Shoplazza!</div>',
});
```

For more complex content, you can split it into `src/style.html` / `src/content.html` / `src/script.html` and assemble them in `src/index.html`. The `extend` function signature and the HTML template mechanism are in the [Checkout extension reference](/docs/app/extensions/checkout/extension-reference); the full API is in the [CheckoutAPI reference](/docs/app/extensions/checkout/checkout-api).

:::warning Don't forget semicolons
Each statement in JavaScript files must end with a semicolon. If a semicolon is missing, the `dev` runtime will throw an error and misleadingly indicate that `extend` must be written at the top of the file. This is caused by parsing failure and is not a problem with the position of `extend`.
:::

## Preview and Publish

Run the following command in the app directory:

```bash
shoplazza app dev
```

After the extension is automatically published to the bound store, open the store admin's "Checkout Editor" to preview the result.

If you want other merchants to be able to use the extension, you must first complete the [app development](/docs/app/getting-started/create-public-app/) and then publish it:

```bash
shoplazza app deploy
```

After other merchants install the app, they can preview the extension in their store theme editor's checkout page.

## Next Steps

- **Explore extension points**: See [Extension points](/docs/app/extensions/checkout/extension-points)

- **Explore the API**: See the [CheckoutAPI reference](/docs/app/extensions/checkout/checkout-api)

- **See real-world scenarios**: See the [Checkout Extension Recipes](/docs/app/extensions/checkout/extension-recipes)

- **Build a real feature**: See the [free-shipping progress bar recipe](/docs/app/extensions/checkout/extension-recipes#scenario-5-free-shipping-progress-bar-based-on-cart-amount)

- **Verify the app and extension end to end**: See [Test app](/docs/app/getting-started/create-public-app/test-app)

## Troubleshooting

### `extend` reports it must be at the top of the file

**Problem:** The `dev` runtime throws an error saying `extend` must be at the top of the file, even though it already is.

**Cause:** A missing semicolon elsewhere causes a parse failure that surfaces as a misleading `extend`-position error.

**Solution:** Make sure every statement ends with a semicolon (see the warning in the minimal example above).

### `window.CheckoutAPI` is `undefined`

**Problem:** Reading `window.CheckoutAPI` directly returns `undefined`.

**Cause:** `CheckoutAPI` is mounted only after the checkout page fully loads.

**Solution:** Wait for it to mount before calling — poll with `setTimeout`/`setInterval` (max retry ~5s). See the `mount` pattern in [Scenario 3](/docs/app/extensions/checkout/extension-recipes#scenario-3-api-calls-and-event-listening) of the recipes.
