@curless/shopify-adapter 0.1.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/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # @curless/shopify-adapter
2
+
3
+ Use a **Shopify store as your agentbank catalog**, and record agent-settled sales
4
+ back as paid Shopify orders. Drop it into a merchant backend so an AI agent can buy
5
+ your Shopify products through any agentbank payment protocol — the money settles
6
+ through agentbank, and the sale shows up in your Shopify admin.
7
+
8
+ **Zero runtime dependencies** — it uses the platform `fetch` and the Shopify Admin
9
+ GraphQL API. Pairs with [`@curless/agentbank-merchant-sdk`](https://www.npmjs.com/package/@curless/agentbank-merchant-sdk),
10
+ which prices and opens the checkout.
11
+
12
+ ```bash
13
+ npm install @curless/shopify-adapter
14
+ ```
15
+
16
+ ## The two halves
17
+
18
+ ```ts
19
+ import {
20
+ shopifyConfigFromEnv,
21
+ fetchShopifyCatalog,
22
+ writebackShopifyOrder,
23
+ } from '@curless/shopify-adapter';
24
+
25
+ const shopify = shopifyConfigFromEnv(); // null unless SHOPIFY_* env is set
26
+ ```
27
+
28
+ **1. Catalog** — map the store's products into generic `CatalogItem`s you price from:
29
+
30
+ ```ts
31
+ const items = shopify ? await fetchShopifyCatalog(shopify) : [];
32
+ // items: { sku, name, priceMinor, currency, description, imageUrl?, variantId?, productType? }[]
33
+ // …price a merchant-quoted checkout from `items` with the merchant SDK…
34
+ ```
35
+
36
+ **2. Writeback** — after the agent pays through agentbank, record the paid order:
37
+
38
+ ```ts
39
+ await writebackShopifyOrder(shopify, {
40
+ variantId, // CatalogItem.variantId
41
+ quantity,
42
+ protocol: 'acp', // which agentbank protocol settled it
43
+ agentbankOrderId: orderId, // idempotency tag
44
+ unitPriceMinor: 268000, // the amount actually collected (see below)
45
+ currency: 'EUR',
46
+ });
47
+ ```
48
+
49
+ No money moves in Shopify — the order is marked **paid via an external transaction**
50
+ and tagged `agentbank` + `protocol:…` + the agentbank order id, so it's traceable and
51
+ never mistaken for a Shopify Payments sale.
52
+
53
+ ## Notes
54
+
55
+ - **Auth** is Shopify's 2026 **client-credentials** grant: a Dev Dashboard app no
56
+ longer shows a token in the UI, so you exchange `SHOPIFY_CLIENT_ID` +
57
+ `SHOPIFY_CLIENT_SECRET` for a short-lived Admin token programmatically. Set
58
+ `SHOPIFY_SHOP_DOMAIN` too. The app needs `read_products` (catalog) and
59
+ `write_orders` (writeback) — required scopes go in `shopify.app.toml`, then
60
+ `deploy` + **reinstall**. Secrets come from the environment; never hardcode them.
61
+ - **Amounts are currency-aware.** Prices convert to integer minor units using the
62
+ currency's own exponent — cents for USD/EUR, none for JPY, mills for KWD — so a
63
+ non-2-decimal store isn't silently mis-scaled.
64
+ - **Record the real amount.** Pass `unitPriceMinor` + `currency` to the writeback so
65
+ the Shopify order reflects what the agent actually paid. Omit them to fall back to
66
+ the variant's current price (which drifts if the price changed after payment).
67
+ - **Idempotency.** The writeback searches for an order already tagged with the
68
+ agentbank order id. Shopify's tag search **lags** creation by the search-index
69
+ refresh, so two writebacks fired within a second can both miss and duplicate — add
70
+ your own in-process guard on the order id if you fire them rapidly; the tag search
71
+ is the cross-restart backstop.
72
+ - **Pagination.** Products paginate to completion. A single product with more than
73
+ 250 variants has its tail dropped **with a warning** (not silently).
74
+
75
+ MIT
@@ -0,0 +1,24 @@
1
+ import type { CatalogItem, ShopifyConfig } from './types.js';
2
+ type AdminVariant = {
3
+ id: string;
4
+ sku: string | null;
5
+ price: string;
6
+ };
7
+ type AdminProduct = {
8
+ title: string;
9
+ description: string;
10
+ productType: string;
11
+ featuredImage: {
12
+ url: string;
13
+ } | null;
14
+ variants: {
15
+ pageInfo?: {
16
+ hasNextPage: boolean;
17
+ };
18
+ nodes: AdminVariant[];
19
+ };
20
+ };
21
+ export declare const productsToItems: (products: AdminProduct[], currencyCode: string, warn?: (msg: string) => void) => CatalogItem[];
22
+ export declare const fetchShopifyCatalog: (config: ShopifyConfig, fetchImpl?: typeof fetch, warn?: (msg: string) => void) => Promise<CatalogItem[]>;
23
+ export {};
24
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAI7D,KAAK,YAAY,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACtE,KAAK,YAAY,GAAG;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IAGpB,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,WAAW,EAAE,OAAO,CAAA;SAAE,CAAC;QAAC,KAAK,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;CAC1E,CAAC;AAUF,eAAO,MAAM,eAAe,GAC1B,UAAU,YAAY,EAAE,EACxB,cAAc,MAAM,EACpB,OAAM,CAAC,GAAG,EAAE,MAAM,KAAK,IAA4C,KAClE,WAAW,EA6Bb,CAAC;AAiCF,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,aAAa,EACrB,YAAW,OAAO,KAAa,EAC/B,OAAM,CAAC,GAAG,EAAE,MAAM,KAAK,IAA4C,KAClE,OAAO,CAAC,WAAW,EAAE,CAkBvB,CAAC"}
@@ -0,0 +1,77 @@
1
+ // Shopify store → CatalogItem[]. Reads the Admin GraphQL API (products), maps
2
+ // each variant to a generic CatalogItem in the store's own currency, and
3
+ // paginates products to completion. Injected fetch so it's testable offline.
4
+ import { exchangeToken, shopifyGraphql } from './client.js';
5
+ import { toMinor } from './money.js';
6
+ // Variants requested per product in one query. Shopify's max page size for a
7
+ // connection is 250; a product with more variants is surfaced with a warning
8
+ // (not silently truncated — see productsToItems).
9
+ const VARIANT_PAGE_SIZE = 250;
10
+ // Flatten products→variants into CatalogItems in the store's own currency.
11
+ // Carries whatever currency the store uses (a real merchant isn't necessarily
12
+ // EUR/USD). Only a missing SKU drops a variant.
13
+ export const productsToItems = (products, currencyCode, warn = (m) => console.warn(`[shopify] ${m}`)) => {
14
+ const items = [];
15
+ for (const p of products) {
16
+ // Loud, not silent: a >250-variant product had its tail dropped by the query.
17
+ if (p.variants.pageInfo?.hasNextPage) {
18
+ warn(`"${p.title}" has more than ${VARIANT_PAGE_SIZE} variants; only the first ${VARIANT_PAGE_SIZE} are mapped`);
19
+ }
20
+ for (const v of p.variants.nodes) {
21
+ if (!v.sku) {
22
+ warn(`skipping a "${p.title}" variant with no SKU`);
23
+ continue;
24
+ }
25
+ items.push({
26
+ sku: v.sku,
27
+ name: p.title,
28
+ priceMinor: toMinor(v.price, currencyCode),
29
+ currency: currencyCode,
30
+ description: p.description,
31
+ imageUrl: p.featuredImage?.url,
32
+ // gid://shopify/ProductVariant/123 → "123", the numeric id a cart
33
+ // permalink needs.
34
+ variantId: v.id.split('/').pop(),
35
+ productType: p.productType,
36
+ });
37
+ }
38
+ }
39
+ return items;
40
+ };
41
+ // The shop's own currency + one page of products. `productType` and per-variant
42
+ // `sku`/`price` are the mapped fields; `featuredImage` is the optional image.
43
+ const PRODUCTS_QUERY = `
44
+ query CatalogProducts($cursor: String) {
45
+ shop { currencyCode }
46
+ products(first: 100, after: $cursor) {
47
+ pageInfo { hasNextPage endCursor }
48
+ nodes {
49
+ title
50
+ description
51
+ productType
52
+ featuredImage { url }
53
+ variants(first: ${VARIANT_PAGE_SIZE}) {
54
+ pageInfo { hasNextPage }
55
+ nodes { id sku price }
56
+ }
57
+ }
58
+ }
59
+ }`;
60
+ // Fetch every product page from the Admin GraphQL API with a freshly-exchanged
61
+ // token, mapped to CatalogItems. Paginates to completion — a store with more
62
+ // than one page of products must not be silently truncated.
63
+ export const fetchShopifyCatalog = async (config, fetchImpl = fetch, warn = (m) => console.warn(`[shopify] ${m}`)) => {
64
+ const token = await exchangeToken(config, fetchImpl);
65
+ const products = [];
66
+ let currencyCode = 'USD';
67
+ let cursor = null;
68
+ do {
69
+ const page = await shopifyGraphql(config, token, PRODUCTS_QUERY, { cursor }, fetchImpl);
70
+ if (page.shop?.currencyCode)
71
+ currencyCode = page.shop.currencyCode;
72
+ products.push(...page.products.nodes);
73
+ cursor = page.products.pageInfo.hasNextPage ? page.products.pageInfo.endCursor : null;
74
+ } while (cursor);
75
+ return productsToItems(products, currencyCode, warn);
76
+ };
77
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { ShopifyConfig } from './types.js';
2
+ export declare const shopifyConfigFromEnv: (env?: Record<string, string | undefined>) => ShopifyConfig | null;
3
+ export declare const exchangeToken: (config: ShopifyConfig, fetchImpl?: typeof fetch) => Promise<string>;
4
+ export declare const shopifyGraphql: <T>(config: ShopifyConfig, token: string, query: string, variables: unknown, fetchImpl?: typeof fetch) => Promise<T>;
5
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD,eAAO,MAAM,oBAAoB,GAC/B,MAAK,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,KACpD,aAAa,GAAG,IAMlB,CAAC;AAKF,eAAO,MAAM,aAAa,GACxB,QAAQ,aAAa,EACrB,YAAW,OAAO,KAAa,KAC9B,OAAO,CAAC,MAAM,CAkBhB,CAAC;AASF,eAAO,MAAM,cAAc,GAAU,CAAC,EACpC,QAAQ,aAAa,EACrB,OAAO,MAAM,EACb,OAAO,MAAM,EACb,WAAW,OAAO,EAClB,YAAW,OAAO,KAAa,KAC9B,OAAO,CAAC,CAAC,CAgBX,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,57 @@
1
+ // Read Shopify config from the environment, or null when it isn't fully set — the
2
+ // normal case (no Shopify account, CI, a fresh clone). Null tells the caller to
3
+ // fall back rather than fail.
4
+ export const shopifyConfigFromEnv = (env = process.env) => {
5
+ const shopDomain = env.SHOPIFY_SHOP_DOMAIN;
6
+ const clientId = env.SHOPIFY_CLIENT_ID;
7
+ const clientSecret = env.SHOPIFY_CLIENT_SECRET;
8
+ if (!shopDomain || !clientId || !clientSecret)
9
+ return null;
10
+ return { shopDomain, clientId, clientSecret, apiVersion: env.SHOPIFY_API_VERSION ?? '2025-01' };
11
+ };
12
+ // Exchange client id + secret for a short-lived Admin API token
13
+ // (grant_type=client_credentials). Form-encoded, NOT JSON. Only works when the
14
+ // app and store are in the same Shopify org, which a self-installed dev app is.
15
+ export const exchangeToken = async (config, fetchImpl = fetch) => {
16
+ const res = await fetchImpl(`https://${config.shopDomain}/admin/oauth/access_token`, {
17
+ method: 'POST',
18
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
19
+ body: new URLSearchParams({
20
+ grant_type: 'client_credentials',
21
+ client_id: config.clientId,
22
+ client_secret: config.clientSecret,
23
+ }),
24
+ });
25
+ if (!res.ok) {
26
+ // Never echo the response body — it could reflect inputs. The status alone
27
+ // distinguishes bad credentials (401) from wrong shop (404) from an outage.
28
+ throw new Error(`Shopify token exchange failed: HTTP ${res.status}`);
29
+ }
30
+ const body = (await res.json());
31
+ if (!body.access_token)
32
+ throw new Error('Shopify token exchange returned no access_token');
33
+ return body.access_token;
34
+ };
35
+ const adminUrl = (config) => `https://${config.shopDomain}/admin/api/${config.apiVersion ?? '2025-01'}/graphql.json`;
36
+ // POST one GraphQL operation and return `data`, throwing on HTTP or GraphQL
37
+ // errors. Error messages carry the status or the `errors` array only — never the
38
+ // full response body (which would include `data`), keeping with token exchange's
39
+ // no-echo rule.
40
+ export const shopifyGraphql = async (config, token, query, variables, fetchImpl = fetch) => {
41
+ const res = await fetchImpl(adminUrl(config), {
42
+ method: 'POST',
43
+ headers: { 'content-type': 'application/json', 'X-Shopify-Access-Token': token },
44
+ body: JSON.stringify({ query, variables }),
45
+ });
46
+ if (!res.ok) {
47
+ // 403 here usually means the app is missing a required scope (read_products
48
+ // for the catalog, write_orders for writeback).
49
+ throw new Error(`Shopify Admin API: HTTP ${res.status}`);
50
+ }
51
+ const body = (await res.json());
52
+ if (body.errors || !body.data) {
53
+ throw new Error(`Shopify Admin API returned errors: ${JSON.stringify(body.errors)}`);
54
+ }
55
+ return body.data;
56
+ };
57
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { shopifyConfigFromEnv, exchangeToken, shopifyGraphql } from './client.js';
2
+ export { fetchShopifyCatalog, productsToItems } from './catalog.js';
3
+ export { writebackShopifyOrder } from './orders.js';
4
+ export { decimalsFor, toMinor, toMajorString } from './money.js';
5
+ export type { CatalogItem, ShopifyConfig, OrderWriteback, WritebackResult, } from './types.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACjE,YAAY,EACV,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ // @curless/shopify-adapter — use a Shopify store as an agentbank catalog source,
2
+ // and record agent-settled sales back as paid Shopify orders.
3
+ //
4
+ // import { shopifyConfigFromEnv, fetchShopifyCatalog, writebackShopifyOrder }
5
+ // from '@curless/shopify-adapter';
6
+ //
7
+ // const shopify = shopifyConfigFromEnv(); // null if not configured
8
+ // const items = shopify ? await fetchShopifyCatalog(shopify) : []; // CatalogItem[]
9
+ // // …price a checkout from `items` with @curless/agentbank-merchant-sdk…
10
+ // // …after the agent pays through agentbank:
11
+ // await writebackShopifyOrder(shopify, { variantId, quantity, protocol, agentbankOrderId,
12
+ // unitPriceMinor, currency });
13
+ export { shopifyConfigFromEnv, exchangeToken, shopifyGraphql } from './client.js';
14
+ export { fetchShopifyCatalog, productsToItems } from './catalog.js';
15
+ export { writebackShopifyOrder } from './orders.js';
16
+ export { decimalsFor, toMinor, toMajorString } from './money.js';
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,8DAA8D;AAC9D,EAAE;AACF,gFAAgF;AAChF,uCAAuC;AACvC,EAAE;AACF,iFAAiF;AACjF,uFAAuF;AACvF,4EAA4E;AAC5E,gDAAgD;AAChD,4FAA4F;AAC5F,wEAAwE;AACxE,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,7 @@
1
+ /** Minor-unit exponent for an ISO currency (default 2). */
2
+ export declare const decimalsFor: (currency: string) => number;
3
+ /** Decimal-string major amount ("2450.00") → integer minor units, for `currency`. */
4
+ export declare const toMinor: (amount: string, currency: string) => number;
5
+ /** Integer minor units → decimal-string major amount ("245000" → "2450.00"), for `currency`. */
6
+ export declare const toMajorString: (minor: number, currency: string) => string;
7
+ //# sourceMappingURL=money.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAmCA,2DAA2D;AAC3D,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,KAAG,MACG,CAAC;AAEnD,qFAAqF;AACrF,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,EAAE,UAAU,MAAM,KAAG,MAM1D,CAAC;AAEF,gGAAgG;AAChG,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,EAAE,UAAU,MAAM,KAAG,MACM,CAAC"}
package/dist/money.js ADDED
@@ -0,0 +1,46 @@
1
+ // Currency-aware minor-unit conversion. Shopify Admin prices are decimal strings
2
+ // in MAJOR units ("2450.00", or "2450" for a zero-decimal currency); the rest of
3
+ // the system is integer minor units. Different currencies have different minor-
4
+ // unit exponents — cents (2) for USD/EUR, none (0) for JPY/KRW, mills (3) for
5
+ // KWD/BHD — so hardcoding 2 dp silently mis-scales a JPY store by 100×. Convert
6
+ // via string math (never float) so 2450.00 lands on 245000, not 244999.99….
7
+ // Non-2-decimal currencies (ISO 4217 minor-unit exponents). Everything not
8
+ // listed is the common 2-decimal case.
9
+ const MINOR_UNIT_DECIMALS = {
10
+ BIF: 0,
11
+ CLP: 0,
12
+ DJF: 0,
13
+ GNF: 0,
14
+ ISK: 0,
15
+ JPY: 0,
16
+ KMF: 0,
17
+ KRW: 0,
18
+ PYG: 0,
19
+ RWF: 0,
20
+ UGX: 0,
21
+ VND: 0,
22
+ VUV: 0,
23
+ XAF: 0,
24
+ XOF: 0,
25
+ XPF: 0,
26
+ BHD: 3,
27
+ IQD: 3,
28
+ JOD: 3,
29
+ KWD: 3,
30
+ LYD: 3,
31
+ OMR: 3,
32
+ TND: 3,
33
+ };
34
+ /** Minor-unit exponent for an ISO currency (default 2). */
35
+ export const decimalsFor = (currency) => MINOR_UNIT_DECIMALS[currency.toUpperCase()] ?? 2;
36
+ /** Decimal-string major amount ("2450.00") → integer minor units, for `currency`. */
37
+ export const toMinor = (amount, currency) => {
38
+ const dp = decimalsFor(currency);
39
+ const [wholeRaw = '0', fracRaw = ''] = amount.trim().split('.');
40
+ const whole = wholeRaw === '' ? '0' : wholeRaw;
41
+ const frac = `${fracRaw}${'0'.repeat(dp)}`.slice(0, dp);
42
+ return Number(whole) * 10 ** dp + (dp > 0 ? Number(frac) : 0);
43
+ };
44
+ /** Integer minor units → decimal-string major amount ("245000" → "2450.00"), for `currency`. */
45
+ export const toMajorString = (minor, currency) => (minor / 10 ** decimalsFor(currency)).toFixed(decimalsFor(currency));
46
+ //# sourceMappingURL=money.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.js","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,iFAAiF;AACjF,gFAAgF;AAChF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAE5E,2EAA2E;AAC3E,uCAAuC;AACvC,MAAM,mBAAmB,GAA2B;IAClD,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;CACP,CAAC;AAEF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAAgB,EAAU,EAAE,CACtD,mBAAmB,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC;AAEnD,qFAAqF;AACrF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,QAAgB,EAAU,EAAE;IAClE,MAAM,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACjC,MAAM,CAAC,QAAQ,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC/C,MAAM,IAAI,GAAG,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChE,CAAC,CAAC;AAEF,gGAAgG;AAChG,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAa,EAAE,QAAgB,EAAU,EAAE,CACvE,CAAC,KAAK,GAAG,EAAE,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { OrderWriteback, ShopifyConfig, WritebackResult } from './types.js';
2
+ export declare const writebackShopifyOrder: (config: ShopifyConfig, input: OrderWriteback, fetchImpl?: typeof fetch) => Promise<WritebackResult>;
3
+ //# sourceMappingURL=orders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../src/orders.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAMjF,eAAO,MAAM,qBAAqB,GAChC,QAAQ,aAAa,EACrB,OAAO,cAAc,EACrB,YAAW,OAAO,KAAa,KAC9B,OAAO,CAAC,eAAe,CAoDzB,CAAC"}
package/dist/orders.js ADDED
@@ -0,0 +1,56 @@
1
+ // Order writeback: after a booking settles THROUGH AGENTBANK (an agent pays via a
2
+ // protocol), record it in Shopify as a paid order so the merchant sees the sale
3
+ // in their own admin. No money moves in Shopify here; the order is marked paid
4
+ // via an external transaction, tagged with the agentbank order id + protocol so
5
+ // it's traceable and not mistaken for a Shopify Payments sale.
6
+ //
7
+ // Needs the write_orders scope. Idempotency is a tag on the created order (the
8
+ // agentbank order id): a re-fire finds the existing one instead of duplicating.
9
+ // NOTE: Shopify's tag search lags order creation by the search-index refresh, so
10
+ // two writebacks fired within a second of each other can BOTH miss and create a
11
+ // duplicate. If you fire writebacks rapidly, add your own in-process guard on the
12
+ // agentbank order id ahead of this call; the tag search is the cross-restart
13
+ // backstop, not a race-proof lock.
14
+ import { exchangeToken, shopifyGraphql } from './client.js';
15
+ import { toMajorString } from './money.js';
16
+ const FIND_BY_TAG = 'query($q:String!){ orders(first:1, query:$q){ nodes{ id name } } }';
17
+ const ORDER_CREATE = 'mutation($o:OrderCreateOrderInput!){ orderCreate(order:$o){ order{ id name } userErrors{ field message } } }';
18
+ export const writebackShopifyOrder = async (config, input, fetchImpl = fetch) => {
19
+ const token = await exchangeToken(config, fetchImpl);
20
+ // Idempotency: find an existing order tagged with this agentbank order id.
21
+ if (input.agentbankOrderId) {
22
+ const found = await shopifyGraphql(config, token, FIND_BY_TAG, { q: `tag:${input.agentbankOrderId}` }, fetchImpl);
23
+ const existing = found.orders.nodes[0];
24
+ if (existing)
25
+ return { created: false, reason: `already written back (${existing.name})` };
26
+ }
27
+ const tags = ['agentbank', `protocol:${input.protocol}`];
28
+ if (input.agentbankOrderId)
29
+ tags.push(input.agentbankOrderId);
30
+ const note = `Paid via agentbank · protocol ${input.protocol}${input.agentbankOrderId ? ` · order ${input.agentbankOrderId}` : ''}`;
31
+ const lineItem = {
32
+ variantId: `gid://shopify/ProductVariant/${input.variantId}`,
33
+ quantity: input.quantity,
34
+ };
35
+ // Price the line explicitly with the amount actually collected, so the order
36
+ // records what was really paid rather than Shopify re-deriving it from the
37
+ // variant's current price. Omitting unitPriceMinor/currency keeps the variant
38
+ // price (Shopify's default).
39
+ if (input.unitPriceMinor != null && input.currency) {
40
+ lineItem.priceSet = {
41
+ shopMoney: {
42
+ amount: toMajorString(input.unitPriceMinor, input.currency),
43
+ currencyCode: input.currency,
44
+ },
45
+ };
46
+ }
47
+ const data = await shopifyGraphql(config, token, ORDER_CREATE, { o: { lineItems: [lineItem], financialStatus: 'PAID', note, tags } }, fetchImpl);
48
+ const errs = data.orderCreate?.userErrors ?? [];
49
+ if (errs.length)
50
+ return { created: false, reason: errs.map((e) => e.message).join('; ') };
51
+ const order = data.orderCreate?.order;
52
+ if (!order)
53
+ return { created: false, reason: 'orderCreate returned no order' };
54
+ return { created: true, name: order.name, id: order.id };
55
+ };
56
+ //# sourceMappingURL=orders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orders.js","sourceRoot":"","sources":["../src/orders.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ export type CatalogItem = {
2
+ /** The id the buyer agent + the gateway quote against — the Shopify variant SKU. */
3
+ sku: string;
4
+ name: string;
5
+ /** Exact amount charged, in the currency's minor units (cents for a 2-dp currency). */
6
+ priceMinor: number;
7
+ /** ISO 4217 code — the shop's own currency (every Admin price is in it). */
8
+ currency: string;
9
+ description: string;
10
+ /** Product image URL, present once Shopify has finished processing one (media ingestion is async). */
11
+ imageUrl?: string;
12
+ /** Shopify variant numeric id — used to build a `…/cart/{id}:{qty}` checkout permalink. */
13
+ variantId?: string;
14
+ /** Shopify `productType`, passed through so the consumer can map it to its own taxonomy. */
15
+ productType?: string;
16
+ };
17
+ export type ShopifyConfig = {
18
+ /** e.g. "your-store.myshopify.com" */
19
+ shopDomain: string;
20
+ /** Dev Dashboard app client id (API key). */
21
+ clientId: string;
22
+ /** Dev Dashboard app client secret. Secret — from env only, never hardcode. */
23
+ clientSecret: string;
24
+ /** Admin API version, e.g. "2025-01". Pinned so an API change can't silently shift behavior. */
25
+ apiVersion?: string;
26
+ };
27
+ export type OrderWriteback = {
28
+ /** Shopify variant numeric id (CatalogItem.variantId). */
29
+ variantId: string;
30
+ quantity: number;
31
+ /** The agentbank protocol that settled it (acp/ucp/x402/…) — recorded in the note + a tag. */
32
+ protocol: string;
33
+ /** The agentbank order / PaymentIntent id — used as an idempotency tag. */
34
+ agentbankOrderId?: string;
35
+ unitPriceMinor?: number;
36
+ currency?: string;
37
+ };
38
+ export type WritebackResult = {
39
+ created: true;
40
+ name: string;
41
+ id: string;
42
+ } | {
43
+ created: false;
44
+ reason: string;
45
+ };
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG;IACxB,oFAAoF;IACpF,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,sGAAsG;IACtG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAKF,MAAM,MAAM,cAAc,GAAG;IAC3B,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAO1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GACvB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // @curless/shopify-adapter — public types.
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,2CAA2C"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@curless/shopify-adapter",
3
+ "version": "0.1.0",
4
+ "description": "Use a Shopify store as an agentbank catalog source, and record agent-settled sales back as paid Shopify orders. Maps Shopify Admin products → a generic CatalogItem your merchant backend prices from, and writes an order back after an agent pays through agentbank. Zero runtime dependencies (uses the platform fetch).",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.10.1",
24
+ "typescript": "^5.6.3",
25
+ "vitest": "^3.2.6"
26
+ },
27
+ "engines": {
28
+ "node": ">=18"
29
+ },
30
+ "keywords": [
31
+ "agentbank",
32
+ "curless",
33
+ "shopify",
34
+ "adapter",
35
+ "catalog",
36
+ "agent-commerce",
37
+ "merchant"
38
+ ],
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://gitlab.com/robin-ruan/agentbank.git",
42
+ "directory": "packages/sdks/shopify-adapter"
43
+ },
44
+ "author": "robin",
45
+ "scripts": {
46
+ "build": "tsc",
47
+ "typecheck": "tsc --noEmit",
48
+ "test": "vitest run",
49
+ "clean": "rm -rf dist"
50
+ }
51
+ }