@conseqa/connector-shopify 0.0.0-reserved.0 → 0.1.8

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.
@@ -0,0 +1,35 @@
1
+ /** Time-ordered, 29 characters: short enough for every provider's reference field. */
2
+ export declare function createShopifyCorrelationKey(nowMs?: number, random?: Uint8Array): string;
3
+ /** The marker written into a refund's note, which Shopify stores and returns on every read. */
4
+ export declare function correlationToken(providerCorrelationKey: string): string;
5
+ /** A refund note that carries the key after whatever the agent wrote for people to read. */
6
+ export declare function correlationNote(providerCorrelationKey: string, note?: string | null): string;
7
+ /** Every Conseqa key found in a note. More than one means the note cannot identify one call. */
8
+ export declare function correlationKeysIn(note: string | null | undefined): string[];
9
+ /**
10
+ * The refundCreate mutation an application sends. The same key goes into the
11
+ * refund's note, where a read-only reader can find it, and into Shopify's
12
+ * @idempotent directive, which stops a retried call from refunding twice.
13
+ * Shopify does not store the idempotency key where it can be read, which is why
14
+ * the note carries it too.
15
+ */
16
+ export declare const SHOPIFY_REFUND_CREATE_MUTATION = "mutation ConseqaRefundCreate($input: RefundInput!, $idempotencyKey: String!) {\n refundCreate(input: $input) @idempotent(key: $idempotencyKey) {\n refund {\n id\n }\n userErrors {\n field\n message\n }\n }\n}";
17
+ export interface ShopifyRefundInput {
18
+ readonly orderId: string;
19
+ readonly note?: string | null;
20
+ readonly [field: string]: unknown;
21
+ }
22
+ export interface ShopifyRefundRequestDecoration {
23
+ readonly providerCorrelationKey: string;
24
+ readonly query: string;
25
+ readonly variables: {
26
+ readonly input: ShopifyRefundInput & {
27
+ readonly orderId: string;
28
+ readonly note: string;
29
+ };
30
+ readonly idempotencyKey: string;
31
+ };
32
+ }
33
+ /** Builds the request for an application's existing refund: its input, plus the key. */
34
+ export declare function decorateShopifyRefundRequest(input: ShopifyRefundInput, providerCorrelationKey: string): ShopifyRefundRequestDecoration;
35
+ //# sourceMappingURL=correlation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation.d.ts","sourceRoot":"","sources":["../src/correlation.ts"],"names":[],"mappings":"AAoCA,sFAAsF;AACtF,wBAAgB,2BAA2B,CACzC,KAAK,SAAa,EAClB,MAAM,GAAE,UAA4B,GACnC,MAAM,CAER;AAID,+FAA+F;AAC/F,wBAAgB,gBAAgB,CAAC,sBAAsB,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,sBAAsB,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAI5F;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAE3E;AAED;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,kPAUzC,CAAC;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE;QAClB,QAAQ,CAAC,KAAK,EAAE,kBAAkB,GAAG;YAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QACzF,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;KACjC,CAAC;CACH;AAED,wFAAwF;AACxF,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,kBAAkB,EACzB,sBAAsB,EAAE,MAAM,GAC7B,8BAA8B,CAahC"}
@@ -0,0 +1,87 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { toShopifyOrderGid } from "./types.js";
3
+ const alphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
4
+ function uuidV7Bytes(nowMs, random) {
5
+ if (random.length < 10)
6
+ throw new Error("UUIDv7 requires ten random bytes");
7
+ const bytes = new Uint8Array(16);
8
+ let timestamp = BigInt(Math.trunc(nowMs));
9
+ for (let index = 5; index >= 0; index -= 1) {
10
+ bytes[index] = Number(timestamp & 0xffn);
11
+ timestamp >>= 8n;
12
+ }
13
+ bytes.set(random.slice(0, 10), 6);
14
+ bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x70;
15
+ bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;
16
+ return bytes;
17
+ }
18
+ function base32(bytes) {
19
+ let accumulator = 0;
20
+ let bits = 0;
21
+ let output = "";
22
+ for (const byte of bytes) {
23
+ accumulator = (accumulator << 8) | byte;
24
+ bits += 8;
25
+ while (bits >= 5) {
26
+ output += alphabet[(accumulator >>> (bits - 5)) & 31];
27
+ bits -= 5;
28
+ }
29
+ accumulator &= (1 << bits) - 1;
30
+ }
31
+ if (bits > 0)
32
+ output += alphabet[(accumulator << (5 - bits)) & 31];
33
+ return output;
34
+ }
35
+ /** Time-ordered, 29 characters: short enough for every provider's reference field. */
36
+ export function createShopifyCorrelationKey(nowMs = Date.now(), random = randomBytes(10)) {
37
+ return `cq_${base32(uuidV7Bytes(nowMs, random)).toLowerCase()}`;
38
+ }
39
+ const TOKEN = /\[conseqa:(cq_[0-9a-z]{26})\]/gu;
40
+ /** The marker written into a refund's note, which Shopify stores and returns on every read. */
41
+ export function correlationToken(providerCorrelationKey) {
42
+ return `[conseqa:${providerCorrelationKey}]`;
43
+ }
44
+ /** A refund note that carries the key after whatever the agent wrote for people to read. */
45
+ export function correlationNote(providerCorrelationKey, note) {
46
+ const human = note?.trim();
47
+ const token = correlationToken(providerCorrelationKey);
48
+ return human ? `${human} ${token}` : token;
49
+ }
50
+ /** Every Conseqa key found in a note. More than one means the note cannot identify one call. */
51
+ export function correlationKeysIn(note) {
52
+ return [...(note ?? "").matchAll(TOKEN)].map((match) => match[1]);
53
+ }
54
+ /**
55
+ * The refundCreate mutation an application sends. The same key goes into the
56
+ * refund's note, where a read-only reader can find it, and into Shopify's
57
+ * @idempotent directive, which stops a retried call from refunding twice.
58
+ * Shopify does not store the idempotency key where it can be read, which is why
59
+ * the note carries it too.
60
+ */
61
+ export const SHOPIFY_REFUND_CREATE_MUTATION = `mutation ConseqaRefundCreate($input: RefundInput!, $idempotencyKey: String!) {
62
+ refundCreate(input: $input) @idempotent(key: $idempotencyKey) {
63
+ refund {
64
+ id
65
+ }
66
+ userErrors {
67
+ field
68
+ message
69
+ }
70
+ }
71
+ }`;
72
+ /** Builds the request for an application's existing refund: its input, plus the key. */
73
+ export function decorateShopifyRefundRequest(input, providerCorrelationKey) {
74
+ return {
75
+ providerCorrelationKey,
76
+ query: SHOPIFY_REFUND_CREATE_MUTATION,
77
+ variables: {
78
+ input: {
79
+ ...input,
80
+ orderId: toShopifyOrderGid(input.orderId),
81
+ note: correlationNote(providerCorrelationKey, input.note),
82
+ },
83
+ idempotencyKey: providerCorrelationKey,
84
+ },
85
+ };
86
+ }
87
+ //# sourceMappingURL=correlation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation.js","sourceRoot":"","sources":["../src/correlation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from "./client.js";
2
+ export * from "./contract.js";
3
+ export * from "./correlation.js";
4
+ export * from "./money.js";
5
+ export * from "./refund.js";
6
+ export * from "./types.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./client.js";
2
+ export * from "./contract.js";
3
+ export * from "./correlation.js";
4
+ export * from "./money.js";
5
+ export * from "./refund.js";
6
+ export * from "./types.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ /** How many minor units a currency has: 2 for INR and USD, 0 for JPY, 3 for KWD. */
2
+ export declare function currencyExponent(currency: string): number;
3
+ /**
4
+ * Shopify reports money as decimal strings ("25.00"). This converts one exactly
5
+ * to minor units, and returns null rather than rounding when it cannot: a
6
+ * verifier that rounds can call a wrong amount right.
7
+ */
8
+ export declare function toMinorUnits(amount: string, currency: string): number | null;
9
+ //# sourceMappingURL=money.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASzD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS5E"}
package/dist/money.js ADDED
@@ -0,0 +1,29 @@
1
+ /** How many minor units a currency has: 2 for INR and USD, 0 for JPY, 3 for KWD. */
2
+ export function currencyExponent(currency) {
3
+ try {
4
+ return (new Intl.NumberFormat("en", { style: "currency", currency }).resolvedOptions()
5
+ .maximumFractionDigits ?? 2);
6
+ }
7
+ catch {
8
+ return 2;
9
+ }
10
+ }
11
+ /**
12
+ * Shopify reports money as decimal strings ("25.00"). This converts one exactly
13
+ * to minor units, and returns null rather than rounding when it cannot: a
14
+ * verifier that rounds can call a wrong amount right.
15
+ */
16
+ export function toMinorUnits(amount, currency) {
17
+ const match = /^(-)?(\d+)(?:\.(\d+))?$/u.exec(amount.trim());
18
+ if (!match)
19
+ return null;
20
+ const exponent = currencyExponent(currency);
21
+ const fraction = match[3] ?? "";
22
+ if (fraction.length > exponent && /[^0]/u.test(fraction.slice(exponent)))
23
+ return null;
24
+ const value = Number(`${match[2]}${fraction.slice(0, exponent).padEnd(exponent, "0")}`);
25
+ if (!Number.isSafeInteger(value))
26
+ return null;
27
+ return match[1] ? -value : value;
28
+ }
29
+ //# sourceMappingURL=money.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.js","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,OAAO,CACL,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC,eAAe,EAAE;aAC3E,qBAAqB,IAAI,CAAC,CAC9B,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,QAAgB;IAC3D,MAAM,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACtF,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACxF,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;AACnC,CAAC"}
@@ -0,0 +1,48 @@
1
+ import type { VerifierResult } from "@conseqa/verifiers";
2
+ import type { ShopifyReadApi, ShopifyRefundBinding, ShopifyRefundDiscoveryResult, ShopifyRefundIntent, ShopifyRefundObservation, ShopifyRefundResource } from "./types.js";
3
+ export interface ShopifyRefundDiscoveryContext {
4
+ readonly intent: ShopifyRefundIntent;
5
+ readonly providerCorrelationKey: string;
6
+ readonly dispatchStartedAt: string;
7
+ readonly discoveryDeadline: string;
8
+ readonly signal?: AbortSignal;
9
+ }
10
+ export declare function isExactRefundCandidate(refund: ShopifyRefundResource, context: ShopifyRefundDiscoveryContext): boolean;
11
+ /**
12
+ * Finds the refund an agent's call made when its reply was lost. The order is
13
+ * named by the intent; the refund is the one whose note carries this call's
14
+ * key. Nothing is ever sent that could refund again.
15
+ */
16
+ export declare function discoverShopifyRefund(api: ShopifyReadApi, context: ShopifyRefundDiscoveryContext): Promise<ShopifyRefundDiscoveryResult>;
17
+ /**
18
+ * Reads the refund and decides. A refund record alone does not prove money
19
+ * moved: Shopify records the refund and then its transactions, whose status is
20
+ * what settles it. Verified needs every refund transaction to have succeeded
21
+ * and their sum to equal the intended amount.
22
+ */
23
+ export declare function verifyShopifyRefund(api: ShopifyReadApi, intent: ShopifyRefundIntent, binding: ShopifyRefundBinding, signal?: AbortSignal, expectedCorrelationKey?: string): Promise<VerifierResult<ShopifyRefundObservation>>;
24
+ /** The refund's ID from what the application's call returned, or null when it did not say. */
25
+ export declare function bindShopifyRefundResult(result: unknown): ShopifyRefundBinding | null;
26
+ /**
27
+ * Use this only when application code can prove the request was rejected
28
+ * locally before any network dispatch. A generic TypeError or AbortError is not
29
+ * proof, because either can also occur after the request reached Shopify.
30
+ */
31
+ export declare class ShopifyRefundNotDispatchedError extends Error {
32
+ readonly code = "shopify_refund_not_dispatched";
33
+ constructor(message: string, options?: ErrorOptions);
34
+ }
35
+ export interface ShopifyRefundExceptionClassification {
36
+ readonly certainty: "rejected" | "ambiguous" | "not_executed";
37
+ readonly ambiguous: boolean;
38
+ readonly retryable: boolean;
39
+ readonly reason: string;
40
+ }
41
+ /**
42
+ * What an exception from the application's refundCreate call proves. Shopify
43
+ * answers a refusal it validated with userErrors and creates nothing, which is
44
+ * a definite rejection. Everything else may have happened, so it is ambiguous
45
+ * and the runner looks for the refund.
46
+ */
47
+ export declare function classifyShopifyRefundException(error: unknown): ShopifyRefundExceptionClassification;
48
+ //# sourceMappingURL=refund.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refund.d.ts","sourceRoot":"","sources":["../src/refund.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIzD,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EACpB,4BAA4B,EAC5B,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EAEtB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAQD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAUT;AAaD;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,4BAA4B,CAAC,CA0CvC;AAeD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,oBAAoB,EAC7B,MAAM,CAAC,EAAE,WAAW,EACpB,sBAAsB,CAAC,EAAE,MAAM,GAC9B,OAAO,CAAC,cAAc,CAAC,wBAAwB,CAAC,CAAC,CAuJnD;AAgBD,8FAA8F;AAC9F,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,oBAAoB,GAAG,IAAI,CAIpF;AAED;;;;GAIG;AACH,qBAAa,+BAAgC,SAAQ,KAAK;IACxD,QAAQ,CAAC,IAAI,mCAAmC;gBAEpC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAED,MAAM,WAAW,oCAAoC;IACnD,QAAQ,CAAC,SAAS,EAAE,UAAU,GAAG,WAAW,GAAG,cAAc,CAAC;IAC9D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,OAAO,GACb,oCAAoC,CAkDtC"}
package/dist/refund.js ADDED
@@ -0,0 +1,323 @@
1
+ import { ShopifyReadError } from "./client.js";
2
+ import { correlationKeysIn } from "./correlation.js";
3
+ import { toMinorUnits } from "./money.js";
4
+ /** The refund's note names this call, and only this call. */
5
+ function carriesKey(refund, key) {
6
+ const keys = correlationKeysIn(refund.note);
7
+ return keys.length === 1 && keys[0] === key;
8
+ }
9
+ export function isExactRefundCandidate(refund, context) {
10
+ if (!carriesKey(refund, context.providerCorrelationKey))
11
+ return false;
12
+ if (refund.createdAt) {
13
+ const createdAtMs = new Date(refund.createdAt).getTime();
14
+ const lowerBound = new Date(context.dispatchStartedAt).getTime() - 60_000;
15
+ const upperBound = new Date(context.discoveryDeadline).getTime();
16
+ if (!Number.isFinite(createdAtMs) || createdAtMs < lowerBound || createdAtMs > upperBound)
17
+ return false;
18
+ }
19
+ return true;
20
+ }
21
+ function failureKind(error) {
22
+ const shopifyError = error instanceof ShopifyReadError ? error : undefined;
23
+ if (shopifyError?.kind === "schema")
24
+ return { reason: "schema", retryable: false };
25
+ if (shopifyError?.kind === "authentication" || shopifyError?.kind === "authorization")
26
+ return { reason: "authentication", retryable: false };
27
+ return { reason: "provider", retryable: shopifyError ? shopifyError.kind === "retryable" : true };
28
+ }
29
+ /**
30
+ * Finds the refund an agent's call made when its reply was lost. The order is
31
+ * named by the intent; the refund is the one whose note carries this call's
32
+ * key. Nothing is ever sent that could refund again.
33
+ */
34
+ export async function discoverShopifyRefund(api, context) {
35
+ try {
36
+ const order = await api.getOrderRefunds(context.intent.orderId, context.signal);
37
+ if (!order) {
38
+ return {
39
+ kind: "inconclusive",
40
+ reason: "order_unreadable",
41
+ message: "The order cannot be read with this token. It needs read_orders, and read_all_orders for orders older than 60 days.",
42
+ retryable: false,
43
+ };
44
+ }
45
+ const candidates = order.refunds.filter((refund) => isExactRefundCandidate(refund, context));
46
+ if (candidates.length > 1) {
47
+ return {
48
+ kind: "inconclusive",
49
+ reason: "multiple_candidates",
50
+ message: `${candidates.length} refunds carry this call's key`,
51
+ candidateCount: candidates.length,
52
+ };
53
+ }
54
+ if (candidates.length === 1)
55
+ return { kind: "found", binding: { refundId: candidates[0].id, recovered: true } };
56
+ // Absence is only proof when every refund on the order was read.
57
+ if (!order.refundsComplete) {
58
+ return {
59
+ kind: "inconclusive",
60
+ reason: "too_many_refunds",
61
+ message: "The order has more refunds than one read returns, so absence cannot be proven",
62
+ retryable: false,
63
+ };
64
+ }
65
+ return { kind: "not_found" };
66
+ }
67
+ catch (error) {
68
+ const { reason, retryable } = failureKind(error);
69
+ return {
70
+ kind: "inconclusive",
71
+ reason,
72
+ message: error instanceof Error ? error.message : String(error),
73
+ retryable,
74
+ };
75
+ }
76
+ }
77
+ /** A transaction's amount in the intended currency, from whichever of its two amounts uses it. */
78
+ function amountIn(transaction, currency) {
79
+ for (const money of [transaction.amountSet.shopMoney, transaction.amountSet.presentmentMoney]) {
80
+ if (money && money.currencyCode.toUpperCase() === currency)
81
+ return toMinorUnits(money.amount, currency);
82
+ }
83
+ return null;
84
+ }
85
+ const SETTLED_FAILED = new Set(["FAILURE", "ERROR"]);
86
+ const UNSETTLED = new Set(["PENDING", "AWAITING_RESPONSE", "UNKNOWN"]);
87
+ const KNOWN = new Set(["SUCCESS", ...SETTLED_FAILED, ...UNSETTLED]);
88
+ /**
89
+ * Reads the refund and decides. A refund record alone does not prove money
90
+ * moved: Shopify records the refund and then its transactions, whose status is
91
+ * what settles it. Verified needs every refund transaction to have succeeded
92
+ * and their sum to equal the intended amount.
93
+ */
94
+ export async function verifyShopifyRefund(api, intent, binding, signal, expectedCorrelationKey) {
95
+ try {
96
+ const refund = await api.getRefund(binding.refundId, signal);
97
+ if (!refund) {
98
+ return {
99
+ verdict: "inconclusive",
100
+ errorKind: "provider",
101
+ message: "Shopify has no readable refund with this ID",
102
+ retryable: true,
103
+ };
104
+ }
105
+ const refundTransactions = refund.transactions.filter((transaction) => transaction.kind === "REFUND");
106
+ const amounts = refundTransactions.map((transaction) => amountIn(transaction, intent.currency));
107
+ const statuses = refundTransactions.map((transaction) => transaction.status);
108
+ const amountMinor = amounts.reduce((sum, amount) => sum + (amount ?? 0), 0);
109
+ const status = refundTransactions.length === 0
110
+ ? "no_transactions"
111
+ : statuses.some((value) => SETTLED_FAILED.has(value))
112
+ ? "failed"
113
+ : statuses.every((value) => value === "SUCCESS")
114
+ ? "succeeded"
115
+ : "pending";
116
+ const keys = correlationKeysIn(refund.note);
117
+ const observation = {
118
+ refundId: refund.id,
119
+ orderId: refund.orderId ?? null,
120
+ correlationKey: keys.length === 1 ? keys[0] : null,
121
+ amountMinor,
122
+ currency: amounts.length > 0 && amounts.every((amount) => amount !== null) ? intent.currency : null,
123
+ transactionStatuses: statuses,
124
+ status,
125
+ settled: status === "succeeded" || status === "failed",
126
+ createdAt: refund.createdAt ?? null,
127
+ };
128
+ const exactIdentity = refund.id === binding.refundId &&
129
+ (refund.orderId === undefined || refund.orderId === intent.orderId) &&
130
+ (expectedCorrelationKey === undefined || carriesKey(refund, expectedCorrelationKey));
131
+ if (!exactIdentity) {
132
+ return {
133
+ verdict: "inconclusive",
134
+ observation,
135
+ errorKind: "schema",
136
+ message: "Shopify returned a refund that does not belong to this call",
137
+ retryable: false,
138
+ };
139
+ }
140
+ if (!refund.transactionsComplete) {
141
+ return {
142
+ verdict: "inconclusive",
143
+ observation,
144
+ errorKind: "provider",
145
+ message: "The refund has more transactions than one read returns",
146
+ retryable: false,
147
+ };
148
+ }
149
+ const unknownStatus = statuses.find((value) => !KNOWN.has(value));
150
+ if (unknownStatus !== undefined) {
151
+ return {
152
+ verdict: "inconclusive",
153
+ observation,
154
+ errorKind: "schema",
155
+ message: `Unknown Shopify transaction status: ${unknownStatus}`,
156
+ retryable: false,
157
+ };
158
+ }
159
+ if (amounts.some((amount) => amount === null)) {
160
+ return {
161
+ verdict: "inconclusive",
162
+ observation,
163
+ errorKind: "schema",
164
+ message: `Shopify reports the refund in a currency other than ${intent.currency}, or an amount that is not exact`,
165
+ retryable: false,
166
+ };
167
+ }
168
+ const evidence = (statusPassed, amountPassed) => ({
169
+ source: "shopify.refund.node",
170
+ observedAt: new Date().toISOString(),
171
+ fields: observation,
172
+ checks: [
173
+ {
174
+ path: "/status",
175
+ operator: "equals",
176
+ expected: "succeeded",
177
+ observed: status,
178
+ passed: statusPassed,
179
+ },
180
+ {
181
+ path: "/amountMinor",
182
+ operator: "equals",
183
+ expected: intent.amountMinor,
184
+ observed: amountMinor,
185
+ passed: amountPassed,
186
+ },
187
+ ],
188
+ });
189
+ if (status === "failed")
190
+ return {
191
+ verdict: "violated",
192
+ observation,
193
+ evidence: evidence(false, amountMinor === intent.amountMinor),
194
+ retryable: false,
195
+ };
196
+ if (status === "succeeded") {
197
+ const amountPassed = amountMinor === intent.amountMinor;
198
+ return {
199
+ verdict: amountPassed ? "satisfied" : "violated",
200
+ observation,
201
+ evidence: evidence(true, amountPassed),
202
+ retryable: false,
203
+ };
204
+ }
205
+ // No transaction yet, or one Shopify has not settled: look again until the deadline.
206
+ return {
207
+ verdict: "pending",
208
+ observation,
209
+ evidence: evidence(false, amountMinor === intent.amountMinor),
210
+ message: status === "no_transactions"
211
+ ? "Shopify recorded the refund but no refund transaction yet"
212
+ : `Shopify still reports ${statuses.join(", ")}`,
213
+ retryable: true,
214
+ };
215
+ }
216
+ catch (error) {
217
+ const shopifyError = error instanceof ShopifyReadError ? error : undefined;
218
+ return {
219
+ verdict: "inconclusive",
220
+ errorKind: shopifyError?.kind === "authentication"
221
+ ? "authentication"
222
+ : shopifyError?.kind === "authorization"
223
+ ? "authorization"
224
+ : shopifyError?.kind === "schema"
225
+ ? "schema"
226
+ : shopifyError?.kind === "retryable"
227
+ ? "rate_limited"
228
+ : "provider",
229
+ message: error instanceof Error ? error.message : String(error),
230
+ retryable: shopifyError ? shopifyError.kind === "retryable" : true,
231
+ ...(shopifyError?.retryAfterMs === undefined
232
+ ? {}
233
+ : { retryAfterMs: shopifyError.retryAfterMs }),
234
+ };
235
+ }
236
+ }
237
+ const REFUND_GID = /^gid:\/\/shopify\/Refund\/\d{1,20}$/u;
238
+ function record(value) {
239
+ return value !== null && typeof value === "object"
240
+ ? value
241
+ : undefined;
242
+ }
243
+ /** The refundCreate payload, from the shapes an application commonly returns. */
244
+ function refundCreatePayload(result) {
245
+ const top = record(result);
246
+ return record(record(top?.data)?.refundCreate) ?? record(top?.refundCreate) ?? top;
247
+ }
248
+ /** The refund's ID from what the application's call returned, or null when it did not say. */
249
+ export function bindShopifyRefundResult(result) {
250
+ const payload = refundCreatePayload(result);
251
+ const id = record(payload?.refund)?.id ?? payload?.id;
252
+ return typeof id === "string" && REFUND_GID.test(id) ? { refundId: id, recovered: false } : null;
253
+ }
254
+ /**
255
+ * Use this only when application code can prove the request was rejected
256
+ * locally before any network dispatch. A generic TypeError or AbortError is not
257
+ * proof, because either can also occur after the request reached Shopify.
258
+ */
259
+ export class ShopifyRefundNotDispatchedError extends Error {
260
+ code = "shopify_refund_not_dispatched";
261
+ constructor(message, options) {
262
+ super(message, options);
263
+ this.name = "ShopifyRefundNotDispatchedError";
264
+ }
265
+ }
266
+ /**
267
+ * What an exception from the application's refundCreate call proves. Shopify
268
+ * answers a refusal it validated with userErrors and creates nothing, which is
269
+ * a definite rejection. Everything else may have happened, so it is ambiguous
270
+ * and the runner looks for the refund.
271
+ */
272
+ export function classifyShopifyRefundException(error) {
273
+ if (error instanceof ShopifyRefundNotDispatchedError) {
274
+ return { certainty: "not_executed", ambiguous: false, retryable: false, reason: error.code };
275
+ }
276
+ const top = record(error);
277
+ if (top) {
278
+ const payload = refundCreatePayload(top.response ?? top.body ?? top);
279
+ const userErrors = payload?.userErrors ?? top.userErrors;
280
+ const refundId = record(payload?.refund)?.id;
281
+ if (Array.isArray(userErrors) && userErrors.length > 0 && refundId === undefined) {
282
+ return {
283
+ certainty: "rejected",
284
+ ambiguous: false,
285
+ retryable: false,
286
+ reason: "shopify_user_errors",
287
+ };
288
+ }
289
+ const status = Number(top.statusCode ?? top.status ?? record(top.response)?.status);
290
+ if (status === 401 || status === 403) {
291
+ return {
292
+ certainty: "rejected",
293
+ ambiguous: false,
294
+ retryable: false,
295
+ reason: `definite_auth_http_${status}`,
296
+ };
297
+ }
298
+ const graphqlErrors = record(top.body)?.errors ?? top.errors;
299
+ const codes = Array.isArray(graphqlErrors)
300
+ ? graphqlErrors.map((entry) => String(record(record(entry)?.extensions)?.code ?? ""))
301
+ : [];
302
+ const throttled = codes.includes("THROTTLED");
303
+ return {
304
+ certainty: "ambiguous",
305
+ ambiguous: true,
306
+ retryable: throttled || status === 429 || status >= 500,
307
+ reason: throttled
308
+ ? "ambiguous_throttled"
309
+ : codes.some((code) => code.startsWith("IDEMPOTENCY"))
310
+ ? "ambiguous_idempotency"
311
+ : Number.isFinite(status)
312
+ ? `ambiguous_http_${status}`
313
+ : "unknown_provider_error",
314
+ };
315
+ }
316
+ return {
317
+ certainty: "ambiguous",
318
+ ambiguous: true,
319
+ retryable: true,
320
+ reason: error instanceof Error ? error.name : "unknown_provider_error",
321
+ };
322
+ }
323
+ //# sourceMappingURL=refund.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refund.js","sourceRoot":"","sources":["../src/refund.ts"],"names":[],"mappings":"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"}