shopify-starter-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/skills/shopify-apps/SKILL.md +47 -0
- package/.agent/skills/shopify-automation/SKILL.md +172 -0
- package/.agent/skills/shopify-development/README.md +60 -0
- package/.agent/skills/shopify-development/SKILL.md +368 -0
- package/.agent/skills/shopify-development/references/app-development.md +578 -0
- package/.agent/skills/shopify-development/references/extensions.md +555 -0
- package/.agent/skills/shopify-development/references/themes.md +498 -0
- package/.agent/skills/shopify-development/scripts/requirements.txt +19 -0
- package/.agent/skills/shopify-development/scripts/shopify_graphql.py +428 -0
- package/.agent/skills/shopify-development/scripts/shopify_init.py +441 -0
- package/.agent/skills/shopify-development/scripts/tests/test_shopify_init.py +379 -0
- package/bin/cli.js +3 -0
- package/package.json +32 -0
- package/src/index.js +116 -0
- package/templates/.agent/skills/shopify-apps/SKILL.md +47 -0
- package/templates/.agent/skills/shopify-automation/SKILL.md +172 -0
- package/templates/.agent/skills/shopify-development/README.md +60 -0
- package/templates/.agent/skills/shopify-development/SKILL.md +368 -0
- package/templates/.agent/skills/shopify-development/references/app-development.md +578 -0
- package/templates/.agent/skills/shopify-development/references/extensions.md +555 -0
- package/templates/.agent/skills/shopify-development/references/themes.md +498 -0
- package/templates/.agent/skills/shopify-development/scripts/requirements.txt +19 -0
- package/templates/.agent/skills/shopify-development/scripts/shopify_graphql.py +428 -0
- package/templates/.agent/skills/shopify-development/scripts/shopify_init.py +441 -0
- package/templates/.agent/skills/shopify-development/scripts/tests/test_shopify_init.py +379 -0
- package/templates/.devcontainer/devcontainer.json +27 -0
- package/templates/tests/playwright.config.ts +26 -0
- package/templates/tests/vitest.config.ts +9 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
# Extensions Reference
|
|
2
|
+
|
|
3
|
+
Guide for building UI extensions and Shopify Functions.
|
|
4
|
+
|
|
5
|
+
## Checkout UI Extensions
|
|
6
|
+
|
|
7
|
+
Customize checkout and thank-you pages with native-rendered components.
|
|
8
|
+
|
|
9
|
+
### Extension Points
|
|
10
|
+
|
|
11
|
+
**Block Targets (Merchant-Configurable):**
|
|
12
|
+
|
|
13
|
+
- `purchase.checkout.block.render` - Main checkout
|
|
14
|
+
- `purchase.thank-you.block.render` - Thank you page
|
|
15
|
+
|
|
16
|
+
**Static Targets (Fixed Position):**
|
|
17
|
+
|
|
18
|
+
- `purchase.checkout.header.render-after`
|
|
19
|
+
- `purchase.checkout.contact.render-before`
|
|
20
|
+
- `purchase.checkout.shipping-option-list.render-after`
|
|
21
|
+
- `purchase.checkout.payment-method-list.render-after`
|
|
22
|
+
- `purchase.checkout.footer.render-before`
|
|
23
|
+
|
|
24
|
+
### Setup
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
shopify app generate extension --type checkout_ui_extension
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Configuration (`shopify.extension.toml`):
|
|
31
|
+
|
|
32
|
+
```toml
|
|
33
|
+
api_version = "2026-01"
|
|
34
|
+
name = "gift-message"
|
|
35
|
+
type = "ui_extension"
|
|
36
|
+
|
|
37
|
+
[[extensions.targeting]]
|
|
38
|
+
target = "purchase.checkout.block.render"
|
|
39
|
+
|
|
40
|
+
[capabilities]
|
|
41
|
+
network_access = true
|
|
42
|
+
api_access = true
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Basic Example
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
import {
|
|
49
|
+
reactExtension,
|
|
50
|
+
BlockStack,
|
|
51
|
+
TextField,
|
|
52
|
+
Checkbox,
|
|
53
|
+
useApi,
|
|
54
|
+
} from "@shopify/ui-extensions-react/checkout";
|
|
55
|
+
|
|
56
|
+
export default reactExtension("purchase.checkout.block.render", () => (
|
|
57
|
+
<Extension />
|
|
58
|
+
));
|
|
59
|
+
|
|
60
|
+
function Extension() {
|
|
61
|
+
const [message, setMessage] = useState("");
|
|
62
|
+
const [isGift, setIsGift] = useState(false);
|
|
63
|
+
const { applyAttributeChange } = useApi();
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (isGift) {
|
|
67
|
+
applyAttributeChange({
|
|
68
|
+
type: "updateAttribute",
|
|
69
|
+
key: "gift_message",
|
|
70
|
+
value: message,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}, [message, isGift]);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<BlockStack spacing="loose">
|
|
77
|
+
<Checkbox checked={isGift} onChange={setIsGift}>
|
|
78
|
+
This is a gift
|
|
79
|
+
</Checkbox>
|
|
80
|
+
{isGift && (
|
|
81
|
+
<TextField
|
|
82
|
+
label="Gift Message"
|
|
83
|
+
value={message}
|
|
84
|
+
onChange={setMessage}
|
|
85
|
+
multiline={3}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
</BlockStack>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Common Hooks
|
|
94
|
+
|
|
95
|
+
**useApi:**
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
const { extensionPoint, shop, storefront, i18n, sessionToken } = useApi();
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**useCartLines:**
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
const lines = useCartLines();
|
|
105
|
+
lines.forEach((line) => {
|
|
106
|
+
console.log(line.merchandise.product.title, line.quantity);
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**useShippingAddress:**
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
const address = useShippingAddress();
|
|
114
|
+
console.log(address.city, address.countryCode);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**useApplyCartLinesChange:**
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const applyChange = useApplyCartLinesChange();
|
|
121
|
+
|
|
122
|
+
async function addItem() {
|
|
123
|
+
await applyChange({
|
|
124
|
+
type: "addCartLine",
|
|
125
|
+
merchandiseId: "gid://shopify/ProductVariant/123",
|
|
126
|
+
quantity: 1,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Core Components
|
|
132
|
+
|
|
133
|
+
**Layout:**
|
|
134
|
+
|
|
135
|
+
- `BlockStack` - Vertical stacking
|
|
136
|
+
- `InlineStack` - Horizontal layout
|
|
137
|
+
- `Grid`, `GridItem` - Grid layout
|
|
138
|
+
- `View` - Container
|
|
139
|
+
- `Divider` - Separator
|
|
140
|
+
|
|
141
|
+
**Input:**
|
|
142
|
+
|
|
143
|
+
- `TextField` - Text input
|
|
144
|
+
- `Checkbox` - Boolean
|
|
145
|
+
- `Select` - Dropdown
|
|
146
|
+
- `DatePicker` - Date selection
|
|
147
|
+
- `Form` - Form wrapper
|
|
148
|
+
|
|
149
|
+
**Display:**
|
|
150
|
+
|
|
151
|
+
- `Text`, `Heading` - Typography
|
|
152
|
+
- `Banner` - Messages
|
|
153
|
+
- `Badge` - Status
|
|
154
|
+
- `Image` - Images
|
|
155
|
+
- `Link` - Hyperlinks
|
|
156
|
+
- `List`, `ListItem` - Lists
|
|
157
|
+
|
|
158
|
+
**Interactive:**
|
|
159
|
+
|
|
160
|
+
- `Button` - Actions
|
|
161
|
+
- `Modal` - Overlays
|
|
162
|
+
- `Pressable` - Click areas
|
|
163
|
+
|
|
164
|
+
## Admin UI Extensions
|
|
165
|
+
|
|
166
|
+
Extend Shopify admin interface.
|
|
167
|
+
|
|
168
|
+
### Admin Action
|
|
169
|
+
|
|
170
|
+
Custom actions on resource pages.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
shopify app generate extension --type admin_action
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
import {
|
|
178
|
+
reactExtension,
|
|
179
|
+
AdminAction,
|
|
180
|
+
Button,
|
|
181
|
+
} from "@shopify/ui-extensions-react/admin";
|
|
182
|
+
|
|
183
|
+
export default reactExtension("admin.product-details.action.render", () => (
|
|
184
|
+
<Extension />
|
|
185
|
+
));
|
|
186
|
+
|
|
187
|
+
function Extension() {
|
|
188
|
+
const { data } = useData();
|
|
189
|
+
|
|
190
|
+
async function handleExport() {
|
|
191
|
+
const response = await fetch("/api/export", {
|
|
192
|
+
method: "POST",
|
|
193
|
+
body: JSON.stringify({ productId: data.product.id }),
|
|
194
|
+
});
|
|
195
|
+
console.log("Exported:", await response.json());
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<AdminAction
|
|
200
|
+
title="Export Product"
|
|
201
|
+
primaryAction={<Button onPress={handleExport}>Export</Button>}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Targets:**
|
|
208
|
+
|
|
209
|
+
- `admin.product-details.action.render`
|
|
210
|
+
- `admin.order-details.action.render`
|
|
211
|
+
- `admin.customer-details.action.render`
|
|
212
|
+
|
|
213
|
+
### Admin Block
|
|
214
|
+
|
|
215
|
+
Embedded content in admin pages.
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
import {
|
|
219
|
+
reactExtension,
|
|
220
|
+
BlockStack,
|
|
221
|
+
Text,
|
|
222
|
+
Badge,
|
|
223
|
+
} from "@shopify/ui-extensions-react/admin";
|
|
224
|
+
|
|
225
|
+
export default reactExtension("admin.product-details.block.render", () => (
|
|
226
|
+
<Extension />
|
|
227
|
+
));
|
|
228
|
+
|
|
229
|
+
function Extension() {
|
|
230
|
+
const { data } = useData();
|
|
231
|
+
const [analytics, setAnalytics] = useState(null);
|
|
232
|
+
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
fetchAnalytics(data.product.id).then(setAnalytics);
|
|
235
|
+
}, []);
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<BlockStack>
|
|
239
|
+
<Text variant="headingMd">Product Analytics</Text>
|
|
240
|
+
<Text>Views: {analytics?.views || 0}</Text>
|
|
241
|
+
<Text>Conversions: {analytics?.conversions || 0}</Text>
|
|
242
|
+
<Badge tone={analytics?.trending ? "success" : "info"}>
|
|
243
|
+
{analytics?.trending ? "Trending" : "Normal"}
|
|
244
|
+
</Badge>
|
|
245
|
+
</BlockStack>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Targets:**
|
|
251
|
+
|
|
252
|
+
- `admin.product-details.block.render`
|
|
253
|
+
- `admin.order-details.block.render`
|
|
254
|
+
- `admin.customer-details.block.render`
|
|
255
|
+
|
|
256
|
+
## POS UI Extensions
|
|
257
|
+
|
|
258
|
+
Customize Point of Sale experience.
|
|
259
|
+
|
|
260
|
+
### Smart Grid Tile
|
|
261
|
+
|
|
262
|
+
Quick access action on POS home screen.
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
import {
|
|
266
|
+
reactExtension,
|
|
267
|
+
SmartGridTile,
|
|
268
|
+
} from "@shopify/ui-extensions-react/pos";
|
|
269
|
+
|
|
270
|
+
export default reactExtension("pos.home.tile.render", () => <Extension />);
|
|
271
|
+
|
|
272
|
+
function Extension() {
|
|
273
|
+
function handlePress() {
|
|
274
|
+
// Navigate to custom workflow
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<SmartGridTile
|
|
279
|
+
title="Gift Cards"
|
|
280
|
+
subtitle="Manage gift cards"
|
|
281
|
+
onPress={handlePress}
|
|
282
|
+
/>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### POS Modal
|
|
288
|
+
|
|
289
|
+
Full-screen workflow.
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
import {
|
|
293
|
+
reactExtension,
|
|
294
|
+
Screen,
|
|
295
|
+
BlockStack,
|
|
296
|
+
Button,
|
|
297
|
+
TextField,
|
|
298
|
+
} from "@shopify/ui-extensions-react/pos";
|
|
299
|
+
|
|
300
|
+
export default reactExtension("pos.home.modal.render", () => <Extension />);
|
|
301
|
+
|
|
302
|
+
function Extension() {
|
|
303
|
+
const { navigation } = useApi();
|
|
304
|
+
const [amount, setAmount] = useState("");
|
|
305
|
+
|
|
306
|
+
function handleIssue() {
|
|
307
|
+
// Issue gift card
|
|
308
|
+
navigation.pop();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<Screen name="Gift Card" title="Issue Gift Card">
|
|
313
|
+
<BlockStack>
|
|
314
|
+
<TextField label="Amount" value={amount} onChange={setAmount} />
|
|
315
|
+
<TextField label="Recipient Email" />
|
|
316
|
+
<Button onPress={handleIssue}>Issue</Button>
|
|
317
|
+
</BlockStack>
|
|
318
|
+
</Screen>
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Customer Account Extensions
|
|
324
|
+
|
|
325
|
+
Customize customer account pages.
|
|
326
|
+
|
|
327
|
+
### Order Status Extension
|
|
328
|
+
|
|
329
|
+
```javascript
|
|
330
|
+
import {
|
|
331
|
+
reactExtension,
|
|
332
|
+
BlockStack,
|
|
333
|
+
Text,
|
|
334
|
+
Button,
|
|
335
|
+
} from "@shopify/ui-extensions-react/customer-account";
|
|
336
|
+
|
|
337
|
+
export default reactExtension(
|
|
338
|
+
"customer-account.order-status.block.render",
|
|
339
|
+
() => <Extension />,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
function Extension() {
|
|
343
|
+
const { order } = useApi();
|
|
344
|
+
|
|
345
|
+
function handleReturn() {
|
|
346
|
+
// Initiate return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<BlockStack>
|
|
351
|
+
<Text variant="headingMd">Need to return?</Text>
|
|
352
|
+
<Text>Start return for order {order.name}</Text>
|
|
353
|
+
<Button onPress={handleReturn}>Start Return</Button>
|
|
354
|
+
</BlockStack>
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Targets:**
|
|
360
|
+
|
|
361
|
+
- `customer-account.order-status.block.render`
|
|
362
|
+
- `customer-account.order-index.block.render`
|
|
363
|
+
- `customer-account.profile.block.render`
|
|
364
|
+
|
|
365
|
+
## Shopify Functions
|
|
366
|
+
|
|
367
|
+
Serverless backend customization.
|
|
368
|
+
|
|
369
|
+
### Function Types
|
|
370
|
+
|
|
371
|
+
**Discounts:**
|
|
372
|
+
|
|
373
|
+
- `order_discount` - Order-level discounts
|
|
374
|
+
- `product_discount` - Product-specific discounts
|
|
375
|
+
- `shipping_discount` - Shipping discounts
|
|
376
|
+
|
|
377
|
+
**Payment Customization:**
|
|
378
|
+
|
|
379
|
+
- Hide/rename/reorder payment methods
|
|
380
|
+
|
|
381
|
+
**Delivery Customization:**
|
|
382
|
+
|
|
383
|
+
- Custom shipping options
|
|
384
|
+
- Delivery rules
|
|
385
|
+
|
|
386
|
+
**Validation:**
|
|
387
|
+
|
|
388
|
+
- Cart validation rules
|
|
389
|
+
- Checkout validation
|
|
390
|
+
|
|
391
|
+
### Create Function
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
shopify app generate extension --type function
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Order Discount Function
|
|
398
|
+
|
|
399
|
+
```javascript
|
|
400
|
+
// input.graphql
|
|
401
|
+
query Input {
|
|
402
|
+
cart {
|
|
403
|
+
lines {
|
|
404
|
+
quantity
|
|
405
|
+
merchandise {
|
|
406
|
+
... on ProductVariant {
|
|
407
|
+
product {
|
|
408
|
+
hasTag(tag: "bulk-discount")
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// function.js
|
|
417
|
+
export default function orderDiscount(input) {
|
|
418
|
+
const targets = input.cart.lines
|
|
419
|
+
.filter(line => line.merchandise.product.hasTag)
|
|
420
|
+
.map(line => ({
|
|
421
|
+
productVariant: { id: line.merchandise.id }
|
|
422
|
+
}));
|
|
423
|
+
|
|
424
|
+
if (targets.length === 0) {
|
|
425
|
+
return { discounts: [] };
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
discounts: [{
|
|
430
|
+
targets,
|
|
431
|
+
value: {
|
|
432
|
+
percentage: {
|
|
433
|
+
value: 10 // 10% discount
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}]
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Payment Customization Function
|
|
442
|
+
|
|
443
|
+
```javascript
|
|
444
|
+
export default function paymentCustomization(input) {
|
|
445
|
+
const hidePaymentMethods = input.cart.lines.some(
|
|
446
|
+
(line) => line.merchandise.product.hasTag,
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
if (!hidePaymentMethods) {
|
|
450
|
+
return { operations: [] };
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return {
|
|
454
|
+
operations: [
|
|
455
|
+
{
|
|
456
|
+
hide: {
|
|
457
|
+
paymentMethodId: "gid://shopify/PaymentMethod/123",
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Validation Function
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
export default function cartValidation(input) {
|
|
469
|
+
const errors = [];
|
|
470
|
+
|
|
471
|
+
// Max 5 items per cart
|
|
472
|
+
if (input.cart.lines.length > 5) {
|
|
473
|
+
errors.push({
|
|
474
|
+
localizedMessage: "Maximum 5 items allowed per order",
|
|
475
|
+
target: "cart",
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Min $50 for wholesale
|
|
480
|
+
const isWholesale = input.cart.lines.some(
|
|
481
|
+
(line) => line.merchandise.product.hasTag,
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
if (isWholesale && input.cart.cost.totalAmount.amount < 50) {
|
|
485
|
+
errors.push({
|
|
486
|
+
localizedMessage: "Wholesale orders require $50 minimum",
|
|
487
|
+
target: "cart",
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return { errors };
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
## Network Requests
|
|
496
|
+
|
|
497
|
+
Extensions can call external APIs.
|
|
498
|
+
|
|
499
|
+
```javascript
|
|
500
|
+
import { useApi } from "@shopify/ui-extensions-react/checkout";
|
|
501
|
+
|
|
502
|
+
function Extension() {
|
|
503
|
+
const { sessionToken } = useApi();
|
|
504
|
+
|
|
505
|
+
async function fetchData() {
|
|
506
|
+
const token = await sessionToken.get();
|
|
507
|
+
|
|
508
|
+
const response = await fetch("https://your-app.com/api/data", {
|
|
509
|
+
headers: {
|
|
510
|
+
Authorization: `Bearer ${token}`,
|
|
511
|
+
"Content-Type": "application/json",
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
return await response.json();
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## Best Practices
|
|
521
|
+
|
|
522
|
+
**Performance:**
|
|
523
|
+
|
|
524
|
+
- Lazy load data
|
|
525
|
+
- Memoize expensive computations
|
|
526
|
+
- Use loading states
|
|
527
|
+
- Minimize re-renders
|
|
528
|
+
|
|
529
|
+
**UX:**
|
|
530
|
+
|
|
531
|
+
- Provide clear error messages
|
|
532
|
+
- Show loading indicators
|
|
533
|
+
- Validate inputs
|
|
534
|
+
- Support keyboard navigation
|
|
535
|
+
|
|
536
|
+
**Security:**
|
|
537
|
+
|
|
538
|
+
- Verify session tokens on backend
|
|
539
|
+
- Sanitize user input
|
|
540
|
+
- Use HTTPS for all requests
|
|
541
|
+
- Don't expose sensitive data
|
|
542
|
+
|
|
543
|
+
**Testing:**
|
|
544
|
+
|
|
545
|
+
- Test on development stores
|
|
546
|
+
- Verify mobile/desktop
|
|
547
|
+
- Check accessibility
|
|
548
|
+
- Test edge cases
|
|
549
|
+
|
|
550
|
+
## Resources
|
|
551
|
+
|
|
552
|
+
- Checkout Extensions: https://shopify.dev/docs/api/checkout-extensions
|
|
553
|
+
- Admin Extensions: https://shopify.dev/docs/apps/admin/extensions
|
|
554
|
+
- Functions: https://shopify.dev/docs/apps/functions
|
|
555
|
+
- Components: https://shopify.dev/docs/api/checkout-ui-extensions/components
|