@porulle/adapter-shopify 0.75.0 → 0.77.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/dist/webhooks.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Err, Ok } from "@porulle/core";
1
+ import { Err, Ok, toMinorUnits } from "@porulle/core";
2
2
  import { z } from "zod";
3
3
  import { shopifyGid, shopifyGraphql } from "./graphql.js";
4
4
  /**
@@ -14,6 +14,7 @@ const inventoryItemVariantSchema = z.object({
14
14
  const id = z.union([z.string(), z.number()]).transform(String);
15
15
  const withId = z.object({ id });
16
16
  const inventoryLevelPayload = z.object({ inventory_item_id: id });
17
+ const decimal = z.union([z.string(), z.number()]).transform(String);
17
18
  const refundPayload = z.object({
18
19
  id,
19
20
  order_id: id,
@@ -21,7 +22,28 @@ const refundPayload = z.object({
21
22
  quantity: z.number().int(),
22
23
  line_item: z.object({ variant_id: id.nullish(), product_id: id.nullish() }),
23
24
  })).default([]),
25
+ /** The money the refund moved. Absent from payloads that predate it; an empty list refunded nothing. */
26
+ transactions: z.array(z.object({ kind: z.string(), status: z.string().nullish(), amount: decimal, currency: z.string().nullish() })).optional(),
27
+ refund_shipping_lines: z.array(z.object({
28
+ subtotal_amount_set: z.object({ shop_money: z.object({ amount: decimal, currency_code: z.string() }) }),
29
+ })).default([]),
24
30
  });
31
+ /**
32
+ * What a Shopify refund paid back: its successful refund transactions (an order paid on the
33
+ * marketplace refunds through the `manual` gateway), and of that the delivery, from
34
+ * `refund_shipping_lines` (present on every payload of the pinned API version).
35
+ */
36
+ function refundMoney(refund) {
37
+ const currency = refund.transactions?.find((transaction) => transaction.currency)?.currency
38
+ ?? refund.refund_shipping_lines[0]?.subtotal_amount_set.shop_money.currency_code;
39
+ // Money with no currency cannot be read in minor units; counted as nothing, it can only under-ask.
40
+ const minor = (value) => (currency ? Math.abs(toMinorUnits(value, currency) ?? 0) : 0);
41
+ const amount = refund.transactions === undefined
42
+ ? undefined
43
+ : refund.transactions.filter((transaction) => transaction.kind.toLowerCase() === "refund" && (transaction.status ?? "success").toLowerCase() === "success").reduce((sum, transaction) => sum + minor(transaction.amount), 0);
44
+ const shippingAmount = refund.refund_shipping_lines.reduce((sum, line) => sum + minor(line.subtotal_amount_set.shop_money.amount), 0);
45
+ return { ...(amount === undefined ? {} : { amount }), ...(shippingAmount > 0 ? { shippingAmount } : {}) };
46
+ }
25
47
  const fulfilledPayload = z.object({
26
48
  id,
27
49
  fulfillments: z.array(z.object({
@@ -109,6 +131,7 @@ export async function decodeShopifyWebhook(shop, event) {
109
131
  const externalVariantId = entry.line_item.variant_id ?? entry.line_item.product_id;
110
132
  return externalVariantId == null ? [] : [{ externalVariantId, quantity: entry.quantity }];
111
133
  }),
134
+ ...refundMoney(parsed.data),
112
135
  }]);
113
136
  }
114
137
  const returnStatus = RETURN_STATUS[topic];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/adapter-shopify",
3
- "version": "0.75.0",
3
+ "version": "0.77.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "zod": "^4.1.11",
19
- "@porulle/core": "0.75.0"
19
+ "@porulle/core": "0.77.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^24.5.2",
@@ -26,8 +26,8 @@
26
26
  "miniflare": "4.20260722.1",
27
27
  "typescript": "5.9.2",
28
28
  "vitest": "^3.2.4",
29
- "@porulle/typescript-config": "0.1.0",
30
- "@porulle/eslint-config": "0.1.0"
29
+ "@porulle/eslint-config": "0.1.0",
30
+ "@porulle/typescript-config": "0.1.0"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"
package/src/webhooks.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Err, Ok } from "@porulle/core";
1
+ import { Err, Ok, toMinorUnits } from "@porulle/core";
2
2
  import type { ChannelConnectorError, ChannelEvent, ChannelShipment, ChannelWebhookEvent, Result } from "@porulle/core";
3
3
  import { z } from "zod";
4
4
  import { shopifyGid, shopifyGraphql } from "./graphql.js";
@@ -19,6 +19,7 @@ const inventoryItemVariantSchema = z.object({
19
19
  const id = z.union([z.string(), z.number()]).transform(String);
20
20
  const withId = z.object({ id });
21
21
  const inventoryLevelPayload = z.object({ inventory_item_id: id });
22
+ const decimal = z.union([z.string(), z.number()]).transform(String);
22
23
  const refundPayload = z.object({
23
24
  id,
24
25
  order_id: id,
@@ -26,7 +27,29 @@ const refundPayload = z.object({
26
27
  quantity: z.number().int(),
27
28
  line_item: z.object({ variant_id: id.nullish(), product_id: id.nullish() }),
28
29
  })).default([]),
30
+ /** The money the refund moved. Absent from payloads that predate it; an empty list refunded nothing. */
31
+ transactions: z.array(z.object({ kind: z.string(), status: z.string().nullish(), amount: decimal, currency: z.string().nullish() })).optional(),
32
+ refund_shipping_lines: z.array(z.object({
33
+ subtotal_amount_set: z.object({ shop_money: z.object({ amount: decimal, currency_code: z.string() }) }),
34
+ })).default([]),
29
35
  });
36
+
37
+ /**
38
+ * What a Shopify refund paid back: its successful refund transactions (an order paid on the
39
+ * marketplace refunds through the `manual` gateway), and of that the delivery, from
40
+ * `refund_shipping_lines` (present on every payload of the pinned API version).
41
+ */
42
+ function refundMoney(refund: z.infer<typeof refundPayload>): { amount?: number; shippingAmount?: number } {
43
+ const currency = refund.transactions?.find((transaction) => transaction.currency)?.currency
44
+ ?? refund.refund_shipping_lines[0]?.subtotal_amount_set.shop_money.currency_code;
45
+ // Money with no currency cannot be read in minor units; counted as nothing, it can only under-ask.
46
+ const minor = (value: string): number => (currency ? Math.abs(toMinorUnits(value, currency) ?? 0) : 0);
47
+ const amount = refund.transactions === undefined
48
+ ? undefined
49
+ : refund.transactions.filter((transaction) => transaction.kind.toLowerCase() === "refund" && (transaction.status ?? "success").toLowerCase() === "success").reduce((sum, transaction) => sum + minor(transaction.amount), 0);
50
+ const shippingAmount = refund.refund_shipping_lines.reduce((sum, line) => sum + minor(line.subtotal_amount_set.shop_money.amount), 0);
51
+ return { ...(amount === undefined ? {} : { amount }), ...(shippingAmount > 0 ? { shippingAmount } : {}) };
52
+ }
30
53
  const fulfilledPayload = z.object({
31
54
  id,
32
55
  fulfillments: z.array(z.object({
@@ -111,6 +134,7 @@ export async function decodeShopifyWebhook(shop: ShopifyGraphqlTarget | undefine
111
134
  const externalVariantId = entry.line_item.variant_id ?? entry.line_item.product_id;
112
135
  return externalVariantId == null ? [] : [{ externalVariantId, quantity: entry.quantity }];
113
136
  }),
137
+ ...refundMoney(parsed.data),
114
138
  }]);
115
139
  }
116
140
  const returnStatus = RETURN_STATUS[topic];