@progus/connector 0.6.5 → 0.6.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.
@@ -44,6 +44,9 @@ type AssignPartnerIdInput = {
44
44
  shopDomain: string;
45
45
  partnerId: string;
46
46
  };
47
+ type AssignPartnerIdWithSubscriptionInput = AssignPartnerIdInput & {
48
+ admin: ShopifyAdminGraphQLClient;
49
+ };
47
50
  type SubscriptionEventData = {
48
51
  subscriptionStatus?: string;
49
52
  subscriptionName?: string;
@@ -82,4 +85,4 @@ declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntr
82
85
  declare function fetchAppsCatalog(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
83
86
  declare function getCrossSellOffersFromApi(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
84
87
 
85
- export { type AssignPartnerIdInput as A, type ConnectorConfig as C, type Logger as L, type SubscriptionEventData as S, type TrackEventParams as T, type TrackResult as a, type CheckPartnerIdResult as b, type ShopifyAdminGraphQLClient as c, getCrossSellOffersFromApi as d, type AppsCatalogEntry as e, fetchAppsCatalog as f, getCrossSellOffers as g, type CrossSellFetchOptions as h, type CrossSellOptions as i, type TrackEventName as j };
88
+ export { type AssignPartnerIdInput as A, type ConnectorConfig as C, type Logger as L, type SubscriptionEventData as S, type TrackEventParams as T, type TrackResult as a, type AssignPartnerIdWithSubscriptionInput as b, type CheckPartnerIdResult as c, type ShopifyAdminGraphQLClient as d, getCrossSellOffersFromApi as e, fetchAppsCatalog as f, getCrossSellOffers as g, type AppsCatalogEntry as h, type CrossSellFetchOptions as i, type CrossSellOptions as j, type TrackEventName as k };
@@ -44,6 +44,9 @@ type AssignPartnerIdInput = {
44
44
  shopDomain: string;
45
45
  partnerId: string;
46
46
  };
47
+ type AssignPartnerIdWithSubscriptionInput = AssignPartnerIdInput & {
48
+ admin: ShopifyAdminGraphQLClient;
49
+ };
47
50
  type SubscriptionEventData = {
48
51
  subscriptionStatus?: string;
49
52
  subscriptionName?: string;
@@ -82,4 +85,4 @@ declare function getCrossSellOffers(options?: CrossSellOptions): AppsCatalogEntr
82
85
  declare function fetchAppsCatalog(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
83
86
  declare function getCrossSellOffersFromApi(options?: CrossSellFetchOptions): Promise<AppsCatalogEntry[]>;
84
87
 
85
- export { type AssignPartnerIdInput as A, type ConnectorConfig as C, type Logger as L, type SubscriptionEventData as S, type TrackEventParams as T, type TrackResult as a, type CheckPartnerIdResult as b, type ShopifyAdminGraphQLClient as c, getCrossSellOffersFromApi as d, type AppsCatalogEntry as e, fetchAppsCatalog as f, getCrossSellOffers as g, type CrossSellFetchOptions as h, type CrossSellOptions as i, type TrackEventName as j };
88
+ export { type AssignPartnerIdInput as A, type ConnectorConfig as C, type Logger as L, type SubscriptionEventData as S, type TrackEventParams as T, type TrackResult as a, type AssignPartnerIdWithSubscriptionInput as b, type CheckPartnerIdResult as c, type ShopifyAdminGraphQLClient as d, getCrossSellOffersFromApi as e, fetchAppsCatalog as f, getCrossSellOffers as g, type AppsCatalogEntry as h, type CrossSellFetchOptions as i, type CrossSellOptions as j, type TrackEventName as k };
@@ -1 +1 @@
1
- export { e as AppsCatalogEntry, h as CrossSellFetchOptions, i as CrossSellOptions, f as fetchAppsCatalog, g as getCrossSellOffers, d as getCrossSellOffersFromApi } from './crossSell-Bs_6xA6f.cjs';
1
+ export { h as AppsCatalogEntry, i as CrossSellFetchOptions, j as CrossSellOptions, f as fetchAppsCatalog, g as getCrossSellOffers, e as getCrossSellOffersFromApi } from './crossSell-BZZ9FX7S.cjs';
@@ -1 +1 @@
1
- export { e as AppsCatalogEntry, h as CrossSellFetchOptions, i as CrossSellOptions, f as fetchAppsCatalog, g as getCrossSellOffers, d as getCrossSellOffersFromApi } from './crossSell-Bs_6xA6f.js';
1
+ export { h as AppsCatalogEntry, i as CrossSellFetchOptions, j as CrossSellOptions, f as fetchAppsCatalog, g as getCrossSellOffers, e as getCrossSellOffersFromApi } from './crossSell-BZZ9FX7S.js';
package/dist/index.cjs CHANGED
@@ -30,6 +30,142 @@ __export(index_exports, {
30
30
  });
31
31
  module.exports = __toCommonJS(index_exports);
32
32
 
33
+ // src/subscription.ts
34
+ var ACTIVE_SUBSCRIPTIONS_QUERY = `
35
+ query {
36
+ currentAppInstallation {
37
+ activeSubscriptions {
38
+ id
39
+ name
40
+ status
41
+ lineItems {
42
+ plan {
43
+ pricingDetails {
44
+ __typename
45
+ ... on AppRecurringPricing {
46
+ interval
47
+ price {
48
+ amount
49
+ }
50
+ }
51
+ ... on AppUsagePricing {
52
+ cappedAmount {
53
+ amount
54
+ }
55
+ terms
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ `;
64
+ var SUBSCRIPTION_DETAILS_QUERY = `
65
+ query AppSubscriptionDetails($id: ID!) {
66
+ node(id: $id) {
67
+ ... on AppSubscription {
68
+ id
69
+ name
70
+ status
71
+ lineItems {
72
+ plan {
73
+ pricingDetails {
74
+ __typename
75
+ ... on AppRecurringPricing {
76
+ interval
77
+ price {
78
+ amount
79
+ }
80
+ }
81
+ ... on AppUsagePricing {
82
+ cappedAmount {
83
+ amount
84
+ }
85
+ terms
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
93
+ `;
94
+ function normalizeInterval(intervalRaw, nameHint) {
95
+ const interval = intervalRaw ? String(intervalRaw).toUpperCase() : "";
96
+ if (interval === "ANNUAL" || interval === "EVERY_30_DAYS") return interval;
97
+ if (interval === "MONTHLY" || interval === "EVERY_30DAYS") return "EVERY_30_DAYS";
98
+ const name = (nameHint ?? "").toLowerCase();
99
+ if (name.includes("annual") || name.includes("year")) return "ANNUAL";
100
+ if (name) return "EVERY_30_DAYS";
101
+ return "";
102
+ }
103
+ function parseSubscriptionId(rawId) {
104
+ if (!rawId) return null;
105
+ return rawId.match(/\d+$/)?.[0] ?? rawId.split("/").pop() ?? rawId;
106
+ }
107
+ function parsePrice(rawPrice) {
108
+ if (typeof rawPrice === "number") return rawPrice;
109
+ if (typeof rawPrice === "string") {
110
+ const parsed = Number(rawPrice);
111
+ return Number.isFinite(parsed) ? parsed : null;
112
+ }
113
+ return null;
114
+ }
115
+ function extractPricing(subscription) {
116
+ const pricingDetails = subscription?.lineItems?.[0]?.plan?.pricingDetails;
117
+ const rawPrice = pricingDetails?.price?.amount ?? pricingDetails?.cappedAmount?.amount;
118
+ const subscriptionPrice = parsePrice(rawPrice);
119
+ const subscriptionPeriod = normalizeInterval(pricingDetails?.interval, subscription?.name);
120
+ return { subscriptionPrice, subscriptionPeriod };
121
+ }
122
+ async function fetchActiveSubscriptionEventData(admin, logger = console) {
123
+ const response = await admin.graphql(ACTIVE_SUBSCRIPTIONS_QUERY);
124
+ const result = await response.json();
125
+ if (result.errors?.length) {
126
+ logger.error?.("Partner subscription query errors", { errors: result.errors });
127
+ }
128
+ const subscriptions = result.data?.currentAppInstallation?.activeSubscriptions ?? [];
129
+ const active = subscriptions.find((sub) => String(sub.status ?? "").toUpperCase() === "ACTIVE") ?? subscriptions[0];
130
+ const subscriptionId = parseSubscriptionId(active?.id);
131
+ if (!subscriptionId) return null;
132
+ let { subscriptionPrice, subscriptionPeriod } = extractPricing(active);
133
+ if (!subscriptionPeriod || subscriptionPrice === null) {
134
+ try {
135
+ const detailsResponse = await admin.graphql(SUBSCRIPTION_DETAILS_QUERY, {
136
+ variables: { id: active?.id }
137
+ });
138
+ const detailsJson = await detailsResponse.json();
139
+ if (detailsJson.errors?.length) {
140
+ logger.error?.("Partner subscription details query errors", { errors: detailsJson.errors });
141
+ }
142
+ const details = detailsJson.data?.node;
143
+ ({ subscriptionPrice, subscriptionPeriod } = extractPricing(details));
144
+ if (!subscriptionPeriod || subscriptionPrice === null) return null;
145
+ return {
146
+ subscriptionStatus: details?.status ?? active?.status ?? "ACTIVE",
147
+ subscriptionName: details?.name ?? active?.name,
148
+ subscriptionId,
149
+ subscriptionPrice,
150
+ subscriptionPeriod
151
+ };
152
+ } catch (error) {
153
+ logger.error?.("Partner subscription details query failed", {
154
+ error: error instanceof Error ? error.message : String(error)
155
+ });
156
+ return null;
157
+ }
158
+ }
159
+ if (!subscriptionPeriod || subscriptionPrice === null) return null;
160
+ return {
161
+ subscriptionStatus: active?.status ?? "ACTIVE",
162
+ subscriptionName: active?.name,
163
+ subscriptionId,
164
+ subscriptionPrice,
165
+ subscriptionPeriod
166
+ };
167
+ }
168
+
33
169
  // src/signing.ts
34
170
  var import_crypto = require("crypto");
35
171
  function signPayload(body, secret) {
@@ -76,6 +212,7 @@ function createProgusConnector(config) {
76
212
  trackSubscriptionUpdated: fail,
77
213
  trackSubscriptionCancelled: fail,
78
214
  assignPartnerId: fail,
215
+ assignPartnerIdWithSubscription: fail,
79
216
  checkPartnerId: async () => ({ success: false, message, status: 500 })
80
217
  };
81
218
  }
@@ -177,6 +314,25 @@ function createProgusConnector(config) {
177
314
  const result = await trackInstall({ shopDomain: payload.shopDomain, partnerId: normalized });
178
315
  return { ...result, partnerId: normalized };
179
316
  }
317
+ async function assignPartnerIdWithSubscription(payload) {
318
+ const { admin, ...rest } = payload;
319
+ const result = await assignPartnerId(rest);
320
+ if (!result.success) return result;
321
+ try {
322
+ const subscriptionPayload = await fetchActiveSubscriptionEventData(admin, logger);
323
+ if (subscriptionPayload) {
324
+ await trackSubscription({
325
+ shopDomain: rest.shopDomain,
326
+ data: subscriptionPayload
327
+ });
328
+ }
329
+ } catch (error) {
330
+ logger?.error?.("Partner subscription sync failed", {
331
+ error: error instanceof Error ? error.message : String(error)
332
+ });
333
+ }
334
+ return result;
335
+ }
180
336
  async function checkPartnerId(payload) {
181
337
  if (!payload.shopDomain) {
182
338
  return { success: false, message: "Missing shop domain" };
@@ -247,6 +403,7 @@ function createProgusConnector(config) {
247
403
  trackSubscriptionUpdated: trackSubscription,
248
404
  trackSubscriptionCancelled: trackSubscription,
249
405
  assignPartnerId,
406
+ assignPartnerIdWithSubscription,
250
407
  checkPartnerId
251
408
  };
252
409
  }
@@ -349,142 +506,6 @@ async function getCrossSellOffersFromApi(options = {}) {
349
506
  const appsCatalog = options.appsCatalog ?? await fetchAppsCatalog(options);
350
507
  return getCrossSellOffers({ ...options, appsCatalog });
351
508
  }
352
-
353
- // src/subscription.ts
354
- var ACTIVE_SUBSCRIPTIONS_QUERY = `
355
- query {
356
- currentAppInstallation {
357
- activeSubscriptions {
358
- id
359
- name
360
- status
361
- lineItems {
362
- plan {
363
- pricingDetails {
364
- __typename
365
- ... on AppRecurringPricing {
366
- interval
367
- price {
368
- amount
369
- }
370
- }
371
- ... on AppUsagePricing {
372
- cappedAmount {
373
- amount
374
- }
375
- terms
376
- }
377
- }
378
- }
379
- }
380
- }
381
- }
382
- }
383
- `;
384
- var SUBSCRIPTION_DETAILS_QUERY = `
385
- query AppSubscriptionDetails($id: ID!) {
386
- node(id: $id) {
387
- ... on AppSubscription {
388
- id
389
- name
390
- status
391
- lineItems {
392
- plan {
393
- pricingDetails {
394
- __typename
395
- ... on AppRecurringPricing {
396
- interval
397
- price {
398
- amount
399
- }
400
- }
401
- ... on AppUsagePricing {
402
- cappedAmount {
403
- amount
404
- }
405
- terms
406
- }
407
- }
408
- }
409
- }
410
- }
411
- }
412
- }
413
- `;
414
- function normalizeInterval(intervalRaw, nameHint) {
415
- const interval = intervalRaw ? String(intervalRaw).toUpperCase() : "";
416
- if (interval === "ANNUAL" || interval === "EVERY_30_DAYS") return interval;
417
- if (interval === "MONTHLY" || interval === "EVERY_30DAYS") return "EVERY_30_DAYS";
418
- const name = (nameHint ?? "").toLowerCase();
419
- if (name.includes("annual") || name.includes("year")) return "ANNUAL";
420
- if (name) return "EVERY_30_DAYS";
421
- return "";
422
- }
423
- function parseSubscriptionId(rawId) {
424
- if (!rawId) return null;
425
- return rawId.match(/\d+$/)?.[0] ?? rawId.split("/").pop() ?? rawId;
426
- }
427
- function parsePrice(rawPrice) {
428
- if (typeof rawPrice === "number") return rawPrice;
429
- if (typeof rawPrice === "string") {
430
- const parsed = Number(rawPrice);
431
- return Number.isFinite(parsed) ? parsed : null;
432
- }
433
- return null;
434
- }
435
- function extractPricing(subscription) {
436
- const pricingDetails = subscription?.lineItems?.[0]?.plan?.pricingDetails;
437
- const rawPrice = pricingDetails?.price?.amount ?? pricingDetails?.cappedAmount?.amount;
438
- const subscriptionPrice = parsePrice(rawPrice);
439
- const subscriptionPeriod = normalizeInterval(pricingDetails?.interval, subscription?.name);
440
- return { subscriptionPrice, subscriptionPeriod };
441
- }
442
- async function fetchActiveSubscriptionEventData(admin, logger = console) {
443
- const response = await admin.graphql(ACTIVE_SUBSCRIPTIONS_QUERY);
444
- const result = await response.json();
445
- if (result.errors?.length) {
446
- logger.error?.("Partner subscription query errors", { errors: result.errors });
447
- }
448
- const subscriptions = result.data?.currentAppInstallation?.activeSubscriptions ?? [];
449
- const active = subscriptions.find((sub) => String(sub.status ?? "").toUpperCase() === "ACTIVE") ?? subscriptions[0];
450
- const subscriptionId = parseSubscriptionId(active?.id);
451
- if (!subscriptionId) return null;
452
- let { subscriptionPrice, subscriptionPeriod } = extractPricing(active);
453
- if (!subscriptionPeriod || subscriptionPrice === null) {
454
- try {
455
- const detailsResponse = await admin.graphql(SUBSCRIPTION_DETAILS_QUERY, {
456
- variables: { id: active?.id }
457
- });
458
- const detailsJson = await detailsResponse.json();
459
- if (detailsJson.errors?.length) {
460
- logger.error?.("Partner subscription details query errors", { errors: detailsJson.errors });
461
- }
462
- const details = detailsJson.data?.node;
463
- ({ subscriptionPrice, subscriptionPeriod } = extractPricing(details));
464
- if (!subscriptionPeriod || subscriptionPrice === null) return null;
465
- return {
466
- subscriptionStatus: details?.status ?? active?.status ?? "ACTIVE",
467
- subscriptionName: details?.name ?? active?.name,
468
- subscriptionId,
469
- subscriptionPrice,
470
- subscriptionPeriod
471
- };
472
- } catch (error) {
473
- logger.error?.("Partner subscription details query failed", {
474
- error: error instanceof Error ? error.message : String(error)
475
- });
476
- return null;
477
- }
478
- }
479
- if (!subscriptionPeriod || subscriptionPrice === null) return null;
480
- return {
481
- subscriptionStatus: active?.status ?? "ACTIVE",
482
- subscriptionName: active?.name,
483
- subscriptionId,
484
- subscriptionPrice,
485
- subscriptionPeriod
486
- };
487
- }
488
509
  // Annotate the CommonJS export names for ESM import in node:
489
510
  0 && (module.exports = {
490
511
  createProgusConnector,
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as ConnectorConfig, T as TrackEventParams, a as TrackResult, S as SubscriptionEventData, A as AssignPartnerIdInput, b as CheckPartnerIdResult, c as ShopifyAdminGraphQLClient } from './crossSell-Bs_6xA6f.cjs';
2
- export { e as AppsCatalogEntry, h as CrossSellFetchOptions, i as CrossSellOptions, L as Logger, j as TrackEventName, f as fetchAppsCatalog, g as getCrossSellOffers, d as getCrossSellOffersFromApi } from './crossSell-Bs_6xA6f.cjs';
1
+ import { C as ConnectorConfig, T as TrackEventParams, a as TrackResult, S as SubscriptionEventData, A as AssignPartnerIdInput, b as AssignPartnerIdWithSubscriptionInput, c as CheckPartnerIdResult, d as ShopifyAdminGraphQLClient } from './crossSell-BZZ9FX7S.cjs';
2
+ export { h as AppsCatalogEntry, i as CrossSellFetchOptions, j as CrossSellOptions, L as Logger, k as TrackEventName, f as fetchAppsCatalog, g as getCrossSellOffers, e as getCrossSellOffersFromApi } from './crossSell-BZZ9FX7S.cjs';
3
3
 
4
4
  type Connector = {
5
5
  track: (eventName: TrackEventParams["eventName"], payload: Omit<TrackEventParams, "eventName">) => Promise<TrackResult>;
@@ -25,6 +25,9 @@ type Connector = {
25
25
  assignPartnerId: (payload: AssignPartnerIdInput) => Promise<TrackResult & {
26
26
  partnerId?: string;
27
27
  }>;
28
+ assignPartnerIdWithSubscription: (payload: AssignPartnerIdWithSubscriptionInput) => Promise<TrackResult & {
29
+ partnerId?: string;
30
+ }>;
28
31
  checkPartnerId: (payload: {
29
32
  shopDomain: string;
30
33
  }) => Promise<CheckPartnerIdResult>;
@@ -39,4 +42,4 @@ declare function signPayload(body: string, secret: string): string;
39
42
 
40
43
  declare function normalizePartnerId(value?: string | null): string | null;
41
44
 
42
- export { AssignPartnerIdInput, CheckPartnerIdResult, ConnectorConfig, ShopifyAdminGraphQLClient, SubscriptionEventData, TrackEventParams, TrackResult, createProgusConnector, fetchActiveSubscriptionEventData, normalizePartnerId, signPayload };
45
+ export { AssignPartnerIdInput, AssignPartnerIdWithSubscriptionInput, CheckPartnerIdResult, ConnectorConfig, ShopifyAdminGraphQLClient, SubscriptionEventData, TrackEventParams, TrackResult, createProgusConnector, fetchActiveSubscriptionEventData, normalizePartnerId, signPayload };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as ConnectorConfig, T as TrackEventParams, a as TrackResult, S as SubscriptionEventData, A as AssignPartnerIdInput, b as CheckPartnerIdResult, c as ShopifyAdminGraphQLClient } from './crossSell-Bs_6xA6f.js';
2
- export { e as AppsCatalogEntry, h as CrossSellFetchOptions, i as CrossSellOptions, L as Logger, j as TrackEventName, f as fetchAppsCatalog, g as getCrossSellOffers, d as getCrossSellOffersFromApi } from './crossSell-Bs_6xA6f.js';
1
+ import { C as ConnectorConfig, T as TrackEventParams, a as TrackResult, S as SubscriptionEventData, A as AssignPartnerIdInput, b as AssignPartnerIdWithSubscriptionInput, c as CheckPartnerIdResult, d as ShopifyAdminGraphQLClient } from './crossSell-BZZ9FX7S.js';
2
+ export { h as AppsCatalogEntry, i as CrossSellFetchOptions, j as CrossSellOptions, L as Logger, k as TrackEventName, f as fetchAppsCatalog, g as getCrossSellOffers, e as getCrossSellOffersFromApi } from './crossSell-BZZ9FX7S.js';
3
3
 
4
4
  type Connector = {
5
5
  track: (eventName: TrackEventParams["eventName"], payload: Omit<TrackEventParams, "eventName">) => Promise<TrackResult>;
@@ -25,6 +25,9 @@ type Connector = {
25
25
  assignPartnerId: (payload: AssignPartnerIdInput) => Promise<TrackResult & {
26
26
  partnerId?: string;
27
27
  }>;
28
+ assignPartnerIdWithSubscription: (payload: AssignPartnerIdWithSubscriptionInput) => Promise<TrackResult & {
29
+ partnerId?: string;
30
+ }>;
28
31
  checkPartnerId: (payload: {
29
32
  shopDomain: string;
30
33
  }) => Promise<CheckPartnerIdResult>;
@@ -39,4 +42,4 @@ declare function signPayload(body: string, secret: string): string;
39
42
 
40
43
  declare function normalizePartnerId(value?: string | null): string | null;
41
44
 
42
- export { AssignPartnerIdInput, CheckPartnerIdResult, ConnectorConfig, ShopifyAdminGraphQLClient, SubscriptionEventData, TrackEventParams, TrackResult, createProgusConnector, fetchActiveSubscriptionEventData, normalizePartnerId, signPayload };
45
+ export { AssignPartnerIdInput, AssignPartnerIdWithSubscriptionInput, CheckPartnerIdResult, ConnectorConfig, ShopifyAdminGraphQLClient, SubscriptionEventData, TrackEventParams, TrackResult, createProgusConnector, fetchActiveSubscriptionEventData, normalizePartnerId, signPayload };
package/dist/index.js CHANGED
@@ -8,6 +8,142 @@ import {
8
8
  stripTrailingSlash
9
9
  } from "./chunk-WZ5FAA44.js";
10
10
 
11
+ // src/subscription.ts
12
+ var ACTIVE_SUBSCRIPTIONS_QUERY = `
13
+ query {
14
+ currentAppInstallation {
15
+ activeSubscriptions {
16
+ id
17
+ name
18
+ status
19
+ lineItems {
20
+ plan {
21
+ pricingDetails {
22
+ __typename
23
+ ... on AppRecurringPricing {
24
+ interval
25
+ price {
26
+ amount
27
+ }
28
+ }
29
+ ... on AppUsagePricing {
30
+ cappedAmount {
31
+ amount
32
+ }
33
+ terms
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ `;
42
+ var SUBSCRIPTION_DETAILS_QUERY = `
43
+ query AppSubscriptionDetails($id: ID!) {
44
+ node(id: $id) {
45
+ ... on AppSubscription {
46
+ id
47
+ name
48
+ status
49
+ lineItems {
50
+ plan {
51
+ pricingDetails {
52
+ __typename
53
+ ... on AppRecurringPricing {
54
+ interval
55
+ price {
56
+ amount
57
+ }
58
+ }
59
+ ... on AppUsagePricing {
60
+ cappedAmount {
61
+ amount
62
+ }
63
+ terms
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ `;
72
+ function normalizeInterval(intervalRaw, nameHint) {
73
+ const interval = intervalRaw ? String(intervalRaw).toUpperCase() : "";
74
+ if (interval === "ANNUAL" || interval === "EVERY_30_DAYS") return interval;
75
+ if (interval === "MONTHLY" || interval === "EVERY_30DAYS") return "EVERY_30_DAYS";
76
+ const name = (nameHint ?? "").toLowerCase();
77
+ if (name.includes("annual") || name.includes("year")) return "ANNUAL";
78
+ if (name) return "EVERY_30_DAYS";
79
+ return "";
80
+ }
81
+ function parseSubscriptionId(rawId) {
82
+ if (!rawId) return null;
83
+ return rawId.match(/\d+$/)?.[0] ?? rawId.split("/").pop() ?? rawId;
84
+ }
85
+ function parsePrice(rawPrice) {
86
+ if (typeof rawPrice === "number") return rawPrice;
87
+ if (typeof rawPrice === "string") {
88
+ const parsed = Number(rawPrice);
89
+ return Number.isFinite(parsed) ? parsed : null;
90
+ }
91
+ return null;
92
+ }
93
+ function extractPricing(subscription) {
94
+ const pricingDetails = subscription?.lineItems?.[0]?.plan?.pricingDetails;
95
+ const rawPrice = pricingDetails?.price?.amount ?? pricingDetails?.cappedAmount?.amount;
96
+ const subscriptionPrice = parsePrice(rawPrice);
97
+ const subscriptionPeriod = normalizeInterval(pricingDetails?.interval, subscription?.name);
98
+ return { subscriptionPrice, subscriptionPeriod };
99
+ }
100
+ async function fetchActiveSubscriptionEventData(admin, logger = console) {
101
+ const response = await admin.graphql(ACTIVE_SUBSCRIPTIONS_QUERY);
102
+ const result = await response.json();
103
+ if (result.errors?.length) {
104
+ logger.error?.("Partner subscription query errors", { errors: result.errors });
105
+ }
106
+ const subscriptions = result.data?.currentAppInstallation?.activeSubscriptions ?? [];
107
+ const active = subscriptions.find((sub) => String(sub.status ?? "").toUpperCase() === "ACTIVE") ?? subscriptions[0];
108
+ const subscriptionId = parseSubscriptionId(active?.id);
109
+ if (!subscriptionId) return null;
110
+ let { subscriptionPrice, subscriptionPeriod } = extractPricing(active);
111
+ if (!subscriptionPeriod || subscriptionPrice === null) {
112
+ try {
113
+ const detailsResponse = await admin.graphql(SUBSCRIPTION_DETAILS_QUERY, {
114
+ variables: { id: active?.id }
115
+ });
116
+ const detailsJson = await detailsResponse.json();
117
+ if (detailsJson.errors?.length) {
118
+ logger.error?.("Partner subscription details query errors", { errors: detailsJson.errors });
119
+ }
120
+ const details = detailsJson.data?.node;
121
+ ({ subscriptionPrice, subscriptionPeriod } = extractPricing(details));
122
+ if (!subscriptionPeriod || subscriptionPrice === null) return null;
123
+ return {
124
+ subscriptionStatus: details?.status ?? active?.status ?? "ACTIVE",
125
+ subscriptionName: details?.name ?? active?.name,
126
+ subscriptionId,
127
+ subscriptionPrice,
128
+ subscriptionPeriod
129
+ };
130
+ } catch (error) {
131
+ logger.error?.("Partner subscription details query failed", {
132
+ error: error instanceof Error ? error.message : String(error)
133
+ });
134
+ return null;
135
+ }
136
+ }
137
+ if (!subscriptionPeriod || subscriptionPrice === null) return null;
138
+ return {
139
+ subscriptionStatus: active?.status ?? "ACTIVE",
140
+ subscriptionName: active?.name,
141
+ subscriptionId,
142
+ subscriptionPrice,
143
+ subscriptionPeriod
144
+ };
145
+ }
146
+
11
147
  // src/signing.ts
12
148
  import { createHmac } from "crypto";
13
149
  function signPayload(body, secret) {
@@ -33,6 +169,7 @@ function createProgusConnector(config) {
33
169
  trackSubscriptionUpdated: fail,
34
170
  trackSubscriptionCancelled: fail,
35
171
  assignPartnerId: fail,
172
+ assignPartnerIdWithSubscription: fail,
36
173
  checkPartnerId: async () => ({ success: false, message, status: 500 })
37
174
  };
38
175
  }
@@ -134,6 +271,25 @@ function createProgusConnector(config) {
134
271
  const result = await trackInstall({ shopDomain: payload.shopDomain, partnerId: normalized });
135
272
  return { ...result, partnerId: normalized };
136
273
  }
274
+ async function assignPartnerIdWithSubscription(payload) {
275
+ const { admin, ...rest } = payload;
276
+ const result = await assignPartnerId(rest);
277
+ if (!result.success) return result;
278
+ try {
279
+ const subscriptionPayload = await fetchActiveSubscriptionEventData(admin, logger);
280
+ if (subscriptionPayload) {
281
+ await trackSubscription({
282
+ shopDomain: rest.shopDomain,
283
+ data: subscriptionPayload
284
+ });
285
+ }
286
+ } catch (error) {
287
+ logger?.error?.("Partner subscription sync failed", {
288
+ error: error instanceof Error ? error.message : String(error)
289
+ });
290
+ }
291
+ return result;
292
+ }
137
293
  async function checkPartnerId(payload) {
138
294
  if (!payload.shopDomain) {
139
295
  return { success: false, message: "Missing shop domain" };
@@ -204,145 +360,10 @@ function createProgusConnector(config) {
204
360
  trackSubscriptionUpdated: trackSubscription,
205
361
  trackSubscriptionCancelled: trackSubscription,
206
362
  assignPartnerId,
363
+ assignPartnerIdWithSubscription,
207
364
  checkPartnerId
208
365
  };
209
366
  }
210
-
211
- // src/subscription.ts
212
- var ACTIVE_SUBSCRIPTIONS_QUERY = `
213
- query {
214
- currentAppInstallation {
215
- activeSubscriptions {
216
- id
217
- name
218
- status
219
- lineItems {
220
- plan {
221
- pricingDetails {
222
- __typename
223
- ... on AppRecurringPricing {
224
- interval
225
- price {
226
- amount
227
- }
228
- }
229
- ... on AppUsagePricing {
230
- cappedAmount {
231
- amount
232
- }
233
- terms
234
- }
235
- }
236
- }
237
- }
238
- }
239
- }
240
- }
241
- `;
242
- var SUBSCRIPTION_DETAILS_QUERY = `
243
- query AppSubscriptionDetails($id: ID!) {
244
- node(id: $id) {
245
- ... on AppSubscription {
246
- id
247
- name
248
- status
249
- lineItems {
250
- plan {
251
- pricingDetails {
252
- __typename
253
- ... on AppRecurringPricing {
254
- interval
255
- price {
256
- amount
257
- }
258
- }
259
- ... on AppUsagePricing {
260
- cappedAmount {
261
- amount
262
- }
263
- terms
264
- }
265
- }
266
- }
267
- }
268
- }
269
- }
270
- }
271
- `;
272
- function normalizeInterval(intervalRaw, nameHint) {
273
- const interval = intervalRaw ? String(intervalRaw).toUpperCase() : "";
274
- if (interval === "ANNUAL" || interval === "EVERY_30_DAYS") return interval;
275
- if (interval === "MONTHLY" || interval === "EVERY_30DAYS") return "EVERY_30_DAYS";
276
- const name = (nameHint ?? "").toLowerCase();
277
- if (name.includes("annual") || name.includes("year")) return "ANNUAL";
278
- if (name) return "EVERY_30_DAYS";
279
- return "";
280
- }
281
- function parseSubscriptionId(rawId) {
282
- if (!rawId) return null;
283
- return rawId.match(/\d+$/)?.[0] ?? rawId.split("/").pop() ?? rawId;
284
- }
285
- function parsePrice(rawPrice) {
286
- if (typeof rawPrice === "number") return rawPrice;
287
- if (typeof rawPrice === "string") {
288
- const parsed = Number(rawPrice);
289
- return Number.isFinite(parsed) ? parsed : null;
290
- }
291
- return null;
292
- }
293
- function extractPricing(subscription) {
294
- const pricingDetails = subscription?.lineItems?.[0]?.plan?.pricingDetails;
295
- const rawPrice = pricingDetails?.price?.amount ?? pricingDetails?.cappedAmount?.amount;
296
- const subscriptionPrice = parsePrice(rawPrice);
297
- const subscriptionPeriod = normalizeInterval(pricingDetails?.interval, subscription?.name);
298
- return { subscriptionPrice, subscriptionPeriod };
299
- }
300
- async function fetchActiveSubscriptionEventData(admin, logger = console) {
301
- const response = await admin.graphql(ACTIVE_SUBSCRIPTIONS_QUERY);
302
- const result = await response.json();
303
- if (result.errors?.length) {
304
- logger.error?.("Partner subscription query errors", { errors: result.errors });
305
- }
306
- const subscriptions = result.data?.currentAppInstallation?.activeSubscriptions ?? [];
307
- const active = subscriptions.find((sub) => String(sub.status ?? "").toUpperCase() === "ACTIVE") ?? subscriptions[0];
308
- const subscriptionId = parseSubscriptionId(active?.id);
309
- if (!subscriptionId) return null;
310
- let { subscriptionPrice, subscriptionPeriod } = extractPricing(active);
311
- if (!subscriptionPeriod || subscriptionPrice === null) {
312
- try {
313
- const detailsResponse = await admin.graphql(SUBSCRIPTION_DETAILS_QUERY, {
314
- variables: { id: active?.id }
315
- });
316
- const detailsJson = await detailsResponse.json();
317
- if (detailsJson.errors?.length) {
318
- logger.error?.("Partner subscription details query errors", { errors: detailsJson.errors });
319
- }
320
- const details = detailsJson.data?.node;
321
- ({ subscriptionPrice, subscriptionPeriod } = extractPricing(details));
322
- if (!subscriptionPeriod || subscriptionPrice === null) return null;
323
- return {
324
- subscriptionStatus: details?.status ?? active?.status ?? "ACTIVE",
325
- subscriptionName: details?.name ?? active?.name,
326
- subscriptionId,
327
- subscriptionPrice,
328
- subscriptionPeriod
329
- };
330
- } catch (error) {
331
- logger.error?.("Partner subscription details query failed", {
332
- error: error instanceof Error ? error.message : String(error)
333
- });
334
- return null;
335
- }
336
- }
337
- if (!subscriptionPeriod || subscriptionPrice === null) return null;
338
- return {
339
- subscriptionStatus: active?.status ?? "ACTIVE",
340
- subscriptionName: active?.name,
341
- subscriptionId,
342
- subscriptionPrice,
343
- subscriptionPeriod
344
- };
345
- }
346
367
  export {
347
368
  createProgusConnector,
348
369
  fetchActiveSubscriptionEventData,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@progus/connector",
3
- "version": "0.6.5",
3
+ "version": "0.6.8",
4
4
  "description": "Progus partner/affiliate connector helpers",
5
5
  "license": "MIT",
6
6
  "type": "module",