Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions example-pos-ui-extensions--discounts--preact/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Environment Configuration
.env
.env.*

# Dependency directory
node_modules

# Test coverage directory
coverage

# Ignore Apple macOS Desktop Services Store
.DS_Store

# Logs
logs
*.log

# extensions build output
extensions/*/build
extensions/*/dist

# lock files




# Ignore shopify files created during app dev
.shopify/*
.shopify.lock
30 changes: 30 additions & 0 deletions example-pos-ui-extensions--discounts--preact/.graphqlrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const fs = require("node:fs");

function getConfig() {
const config = {
projects: {},
};

let extensions = [];
try {
extensions = fs.readdirSync("./extensions");
} catch {
// ignore if no extensions
}

for (const entry of extensions) {
const extensionPath = `./extensions/${entry}`;
const schema = `${extensionPath}/schema.graphql`;
if (!fs.existsSync(schema)) {
continue;
}
config.projects[entry] = {
schema,
documents: [`${extensionPath}/**/*.graphql`],
};
}

return config;
}

module.exports = getConfig();
4 changes: 4 additions & 0 deletions example-pos-ui-extensions--discounts--preact/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
engine-strict=true
auto-install-peers=true
shamefully-hoist=true
@shopify:registry=https://registry.npmjs.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"graphql.vscode-graphql"
]
}
8 changes: 8 additions & 0 deletions example-pos-ui-extensions--discounts--preact/.vscode/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"servers": {
"shopify-dev-mcp": {
"command": "npx",
"args": ["-y", "@shopify/dev-mcp@latest"]
}
}
}
78 changes: 78 additions & 0 deletions example-pos-ui-extensions--discounts--preact/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Shopify App Template - Extension only

This is a template for building an [extension-only Shopify app](https://shopify.dev/docs/apps/build/app-extensions/build-extension-only-app). It contains the basics for building a Shopify app that uses only app extensions.

This template doesn't include a server or the ability to embed a page in the Shopify Admin. If you want either of these capabilities, choose the [Remix app template](https://github.com/Shopify/shopify-app-template-remix) instead.

Whether you choose to use this template or another one, you can use your preferred package manager and the Shopify CLI with [these steps](#installing-the-template).

## Benefits

Shopify apps are built on a variety of Shopify tools to create a great merchant experience. The [create an app](https://shopify.dev/docs/apps/getting-started/create) tutorial in our developer documentation will guide you through creating a Shopify app.

This app template does little more than install the CLI and scaffold a repository.

## Getting started

### Requirements

1. You must [download and install Node.js](https://nodejs.org/en/download/) if you don't already have it.
1. You must [create a Shopify partner account](https://partners.shopify.com/signup) if you don’t have one.
1. You must create a store for testing if you don't have one, either a [development store](https://help.shopify.com/en/partners/dashboard/development-stores#create-a-development-store) or a [Shopify Plus sandbox store](https://help.shopify.com/en/partners/dashboard/managing-stores/plus-sandbox-store).

### Installing the template

This template can be installed using your preferred package manager:

Using yarn:

```shell
yarn create @shopify/app
```

Using npm:

```shell
npm init @shopify/app@latest
```

Using pnpm:

```shell
pnpm create @shopify/app@latest
```

This will clone the template and install the required dependencies.

#### Local Development

[The Shopify CLI](https://shopify.dev/docs/apps/tools/cli) connects to an app in your Partners dashboard. It provides environment variables and runs commands in parallel.

You can develop locally using your preferred package manager. Run one of the following commands from the root of your app.

Using yarn:

```shell
yarn dev
```

Using npm:

```shell
npm run dev
```

Using pnpm:

```shell
pnpm run dev
```

Open the URL generated in your console. Once you grant permission to the app, you can start development (such as generating extensions).

## Developer resources

- [Introduction to Shopify apps](https://shopify.dev/docs/apps/getting-started)
- [App extensions](https://shopify.dev/docs/apps/build/app-extensions)
- [Extension only apps](https://shopify.dev/docs/apps/build/app-extensions/build-extension-only-app)
- [Shopify CLI](https://shopify.dev/docs/apps/tools/cli)
59 changes: 59 additions & 0 deletions example-pos-ui-extensions--discounts--preact/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Security Policy

## Supported versions

### New features

New features will only be added to the master branch and will not be made available in point releases.

### Bug fixes

Only the latest release series will receive bug fixes. When enough bugs are fixed and its deemed worthy to release a new gem, this is the branch it happens from.

### Security issues

Only the latest release series will receive patches and new versions in case of a security issue.

### Severe security issues

For severe security issues we will provide new versions as above, and also the last major release series will receive patches and new versions. The classification of the security issue is judged by the core team.

### Unsupported Release Series

When a release series is no longer supported, it's your own responsibility to deal with bugs and security issues. If you are not comfortable maintaining your own versions, you should upgrade to a supported version.

## Reporting a bug

All security bugs in shopify repositories should be reported to [our hackerone program](https://hackerone.com/shopify)
Shopify's whitehat program is our way to reward security researchers for finding serious security vulnerabilities in the In Scope properties listed at the bottom of this page, including our core application (all functionality associated with a Shopify store, particularly your-store.myshopify.com/admin) and certain ancillary applications.

## Disclosure Policy

We look forward to working with all security researchers and strive to be respectful, always assume the best and treat others as peers. We expect the same in return from all participants. To achieve this, our team strives to:

- Reply to all reports within one business day and triage within two business days (if applicable)
- Be as transparent as possible, answering all inquires about our report decisions and adding hackers to duplicate HackerOne reports
- Award bounties within a week of resolution (excluding extenuating circumstances)
- Only close reports as N/A when the issue reported is included in Known Issues, Ineligible Vulnerabilities Types or lacks evidence of a vulnerability

**The following rules must be followed in order for any rewards to be paid:**

- You may only test against shops you have created which include your HackerOne YOURHANDLE @ wearehackerone.com registered email address.
- You must not attempt to gain access to, or interact with, any shops other than those created by you.
- The use of commercial scanners is prohibited (e.g., Nessus).
- Rules for reporting must be followed.
- Do not disclose any issues publicly before they have been resolved.
- Shopify reserves the right to modify the rules for this program or deem any submissions invalid at any time. Shopify may cancel the whitehat program without notice at any time.
- Contacting Shopify Support over chat, email or phone about your HackerOne report is not allowed. We may disqualify you from receiving a reward, or from participating in the program altogether.
- You are not an employee of Shopify; employees should report bugs to the internal bug bounty program.
- You hereby represent, warrant and covenant that any content you submit to Shopify is an original work of authorship and that you are legally entitled to grant the rights and privileges conveyed by these terms. You further represent, warrant and covenant that the consent of no other person or entity is or will be necessary for Shopify to use the submitted content.
- By submitting content to Shopify, you irrevocably waive all moral rights which you may have in the content.
- All content submitted by you to Shopify under this program is licensed under the MIT License.
- You must report any discovered vulnerability to Shopify as soon as you have validated the vulnerability.
- Failure to follow any of the foregoing rules will disqualify you from participating in this program.

\*\* Please see our [Hackerone Profile](https://hackerone.com/shopify) for full details

## Receiving Security Updates

To receive all general updates to vulnerabilities, please subscribe to our hackerone [Hacktivity](https://hackerone.com/shopify/hacktivity)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "discount",
"private": true,
"version": "1.0.0",
"license": "UNLICENSED",
"dependencies": {
"@shopify/ui-extensions": "2025.10.x",
"preact": "^10.10.x"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import '@shopify/ui-extensions';

//@ts-ignore
declare module './src/Tile.jsx' {
const shopify: import('@shopify/ui-extensions/pos.home.tile.render').Api;
const globalThis: { shopify: typeof shopify };
}

//@ts-ignore
declare module './src/Modal.jsx' {
const shopify: import('@shopify/ui-extensions/pos.home.modal.render').Api;
const globalThis: { shopify: typeof shopify };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
api_version = "2025-10"

[[extensions]]
type = "ui_extension"
name = "Discount"
uid = "bdff5acd-d192-ddcd-05cf-ad6d759e978ac7c62980"
handle = "discount"
description = "Add discount"

# module: file that contains your extension’s source code
# target: location where your extension appears in POS
[[extensions.targeting]]
module = "./src/Tile.jsx"
target = "pos.home.tile.render"

[[extensions.targeting]]
module = "./src/Modal.jsx"
target = "pos.home.modal.render"
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { render } from "preact";

export default async () => {
render(<Extension />, document.body);
};

function Extension() {
// [START modal.define-onpress]
const onButtonClick = (type, title, amount) => {
shopify.cart.applyCartDiscount(type, title, amount);
shopify.toast.show("Discount applied");
};
// [END modal.define-onpress]
return (
<s-page heading="Available Discounts">
<s-scroll-box padding="base">
<s-button onClick={() => onButtonClick("Percentage", "25% off", "25")}>
25%
</s-button>
<s-button onClick={() => onButtonClick("FixedAmount", "$10 off", "10")}>
$10
</s-button>
</s-scroll-box>
</s-page>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {render} from 'preact';
import {useState, useEffect} from 'preact/hooks';

export default async () => {
render(<Extension />, document.body);
}

function Extension() {
const shouldDisable = (subtotal) => {
return Number(subtotal) <= 100;
};

// [START tile.enable]
const [disabled, setDisabled] = useState(
shouldDisable(shopify.cart.current.value.subtotal),
);
// [END tile.enable]

// [START tile.subscribe]
useEffect(() => {
const unsubscribe = shopify.cart.current.subscribe((cart) => {
setDisabled(shouldDisable(cart.subtotal));
});
return unsubscribe;
}, []);
// [END tile.subscribe]

return (
<s-tile
heading="Discount Example App"
subheading="Preact"
onClick={() => shopify.action.presentModal()}
disabled={disabled}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact",
"target": "ES2020",
"checkJs": true,
"allowJs": true,
"moduleResolution": "node",
"esModuleInterop": true,
"noEmit": true
}
}
Loading