@hartl-services/medusa-affiliate 0.3.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/.medusa/server/src/admin/index.js +3739 -0
- package/.medusa/server/src/admin/index.mjs +3738 -0
- package/.medusa/server/src/api/admin/affiliate-applications/[id]/approve/route.js +28 -0
- package/.medusa/server/src/api/admin/affiliate-applications/[id]/reject/route.js +19 -0
- package/.medusa/server/src/api/admin/affiliate-applications/route.js +23 -0
- package/.medusa/server/src/api/admin/affiliate-distribution-reports/query.js +171 -0
- package/.medusa/server/src/api/admin/affiliate-distribution-reports/route.js +13 -0
- package/.medusa/server/src/api/admin/affiliate-program-settings/route.js +27 -0
- package/.medusa/server/src/api/admin/affiliate-reports/[id]/route.js +45 -0
- package/.medusa/server/src/api/admin/affiliate-reports/route.js +27 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/consignment-reminders/route.js +31 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/consignment-transfers/route.js +41 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/[coupon_id]/reconcile/route.js +34 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/[coupon_id]/revoke/route.js +36 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/[coupon_id]/rotate/route.js +40 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/route.js +56 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/distribution/route.js +45 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/inactive/route.js +20 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/route.js +50 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/slugs/[slug_id]/route.js +19 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/slugs/route.js +26 -0
- package/.medusa/server/src/api/admin/affiliates/route.js +43 -0
- package/.medusa/server/src/api/admin/consignment-report-response.js +36 -0
- package/.medusa/server/src/api/admin/consignment-reports/[id]/approve/route.js +25 -0
- package/.medusa/server/src/api/admin/consignment-reports/[id]/correct/route.js +18 -0
- package/.medusa/server/src/api/admin/consignment-reports/[id]/reject/route.js +15 -0
- package/.medusa/server/src/api/admin/consignment-reports/[id]/route.js +11 -0
- package/.medusa/server/src/api/admin/consignment-reports/route.js +27 -0
- package/.medusa/server/src/api/admin/customers/[id]/affiliate/route.js +50 -0
- package/.medusa/server/src/api/admin/orders/[id]/affiliate-attribution/route.js +30 -0
- package/.medusa/server/src/api/affiliate-coupon-http.js +152 -0
- package/.medusa/server/src/api/affiliate-referral-cookie.js +34 -0
- package/.medusa/server/src/api/affiliate-self-service-http.js +37 -0
- package/.medusa/server/src/api/middlewares.js +786 -0
- package/.medusa/server/src/api/store/affiliate-referrals/apply-coupon/route.js +47 -0
- package/.medusa/server/src/api/store/affiliate-referrals/capture/route.js +76 -0
- package/.medusa/server/src/api/store/affiliate-referrals/carts/[cart_id]/route.js +20 -0
- package/.medusa/server/src/api/store/affiliates/consignment-inventory/route.js +53 -0
- package/.medusa/server/src/api/store/affiliates/consignment-report-response.js +31 -0
- package/.medusa/server/src/api/store/affiliates/consignment-reports/[id]/submit/route.js +40 -0
- package/.medusa/server/src/api/store/affiliates/consignment-reports/route.js +43 -0
- package/.medusa/server/src/api/store/affiliates/coupon-labels/route.js +14 -0
- package/.medusa/server/src/api/store/affiliates/me/commissions/route.js +35 -0
- package/.medusa/server/src/api/store/affiliates/me/coupon-codes/[coupon_id]/revoke/route.js +36 -0
- package/.medusa/server/src/api/store/affiliates/me/coupon-codes/[coupon_id]/rotate/route.js +40 -0
- package/.medusa/server/src/api/store/affiliates/me/coupon-codes/route.js +55 -0
- package/.medusa/server/src/api/store/affiliates/me/dashboard/route.js +15 -0
- package/.medusa/server/src/api/store/affiliates/me/route.js +85 -0
- package/.medusa/server/src/api/store/affiliates/me/slugs/[slug_id]/route.js +29 -0
- package/.medusa/server/src/api/store/affiliates/me/slugs/route.js +34 -0
- package/.medusa/server/src/api/store/affiliates/own-order-discount/route.js +22 -0
- package/.medusa/server/src/api/store/affiliates/own-order-promotion/route.js +30 -0
- package/.medusa/server/src/api/store/affiliates/register/route.js +27 -0
- package/.medusa/server/src/api/store/affiliates/resolve-referral/route.js +34 -0
- package/.medusa/server/src/api/validators.js +52 -0
- package/.medusa/server/src/api/wire-response.js +10 -0
- package/.medusa/server/src/integration/email-events.js +51 -0
- package/.medusa/server/src/integrations/inventory-transfer/batch-management-transfer-adapter.js +36 -0
- package/.medusa/server/src/integrations/inventory-transfer/native-inventory-transfer-adapter.js +30 -0
- package/.medusa/server/src/integrations/inventory-transfer/resolve-inventory-transfer-adapter.js +46 -0
- package/.medusa/server/src/integrations/inventory-transfer/types.js +3 -0
- package/.medusa/server/src/jobs/affiliate-attribution-expiry.js +20 -0
- package/.medusa/server/src/jobs/affiliate-consignment-report-reminders.js +15 -0
- package/.medusa/server/src/jobs/affiliate-promotion-freshness.js +24 -0
- package/.medusa/server/src/lib/query-string.js +15 -0
- package/.medusa/server/src/modules/affiliate/domain/affiliate-attribution-policy.js +81 -0
- package/.medusa/server/src/modules/affiliate/domain/affiliate-coupon-policy.js +61 -0
- package/.medusa/server/src/modules/affiliate/domain/commission-policy.js +33 -0
- package/.medusa/server/src/modules/affiliate/domain/consignment-reminder-rules.js +74 -0
- package/.medusa/server/src/modules/affiliate/domain/email-normalization.js +20 -0
- package/.medusa/server/src/modules/affiliate/domain/own-order-coupon-code.js +37 -0
- package/.medusa/server/src/modules/affiliate/domain/plugin-options.js +40 -0
- package/.medusa/server/src/modules/affiliate/index.js +13 -0
- package/.medusa/server/src/modules/affiliate/migrations/Migration20260827085154.js +423 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-application.js +38 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-cart-referral.js +65 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-code-redemption.js +41 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-coupon.js +55 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-distribution-settings.js +22 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-order-attribution.js +49 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-program-settings.js +28 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-referral-session.js +34 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-slug.js +27 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate.js +49 -0
- package/.medusa/server/src/modules/affiliate/models/commission-entry.js +62 -0
- package/.medusa/server/src/modules/affiliate/models/consignment-report-line.js +21 -0
- package/.medusa/server/src/modules/affiliate/models/consignment-report.js +31 -0
- package/.medusa/server/src/modules/affiliate/models/consignment-transfer-line.js +16 -0
- package/.medusa/server/src/modules/affiliate/models/consignment-transfer.js +20 -0
- package/.medusa/server/src/modules/affiliate/models/customer-affiliate-assignment.js +58 -0
- package/.medusa/server/src/modules/affiliate/service.js +2321 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-application-manager.js +171 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-coupon-manager.js +441 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-discount.service.js +55 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-manager.js +242 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-partner-portal.service.js +138 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-report.service.js +436 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-slug-manager.js +163 -0
- package/.medusa/server/src/modules/affiliate/services/commission-ledger.service.js +729 -0
- package/.medusa/server/src/modules/affiliate/services/customer-affiliate-assignment-manager.js +350 -0
- package/.medusa/server/src/modules/affiliate/services/order-attribution-manager.js +1051 -0
- package/.medusa/server/src/modules/affiliate/services/referral-lifecycle-manager.js +1420 -0
- package/.medusa/server/src/modules/affiliate/services/utils.js +49 -0
- package/.medusa/server/src/modules/affiliate/types/internal.js +3 -0
- package/.medusa/server/src/paths/affiliate.js +7 -0
- package/.medusa/server/src/paths/index.js +18 -0
- package/.medusa/server/src/schemas/index.js +18 -0
- package/.medusa/server/src/schemas/store.js +24 -0
- package/.medusa/server/src/subscribers/affiliate-cart-updated.js +92 -0
- package/.medusa/server/src/subscribers/affiliate-order-canceled.js +70 -0
- package/.medusa/server/src/subscribers/affiliate-order-placed.js +63 -0
- package/.medusa/server/src/subscribers/affiliate-order-return-received.js +67 -0
- package/.medusa/server/src/subscribers/affiliate-product-eligibility-updated.js +87 -0
- package/.medusa/server/src/types/admin-api.js +3 -0
- package/.medusa/server/src/types/enums.js +53 -0
- package/.medusa/server/src/types/index.js +32 -0
- package/.medusa/server/src/types/public.js +3 -0
- package/.medusa/server/src/types/store-api.js +3 -0
- package/.medusa/server/src/utils/affiliate-cart-operation-lock.js +316 -0
- package/.medusa/server/src/utils/affiliate-promotion-mutation-authorization.js +49 -0
- package/.medusa/server/src/utils/cart-promotions.js +9 -0
- package/.medusa/server/src/utils/money.js +79 -0
- package/.medusa/server/src/utils/promotion-code-availability.js +34 -0
- package/.medusa/server/src/utils/registered-email-checkout.js +101 -0
- package/.medusa/server/src/utils/resolve-services.js +7 -0
- package/.medusa/server/src/utils/settle-cart-promotions.js +44 -0
- package/.medusa/server/src/workflows/affiliate/affiliate-coupon-cart.js +215 -0
- package/.medusa/server/src/workflows/affiliate/affiliate-coupon-command-authorization.js +20 -0
- package/.medusa/server/src/workflows/affiliate/apply-affiliate-coupon.js +174 -0
- package/.medusa/server/src/workflows/affiliate/apply-own-order-promotion.js +89 -0
- package/.medusa/server/src/workflows/affiliate/approve-consignment-report.js +186 -0
- package/.medusa/server/src/workflows/affiliate/correct-approved-consignment-report.js +263 -0
- package/.medusa/server/src/workflows/affiliate/create-affiliate-coupon.js +35 -0
- package/.medusa/server/src/workflows/affiliate/create-commission-correction-for-cancellation.js +21 -0
- package/.medusa/server/src/workflows/affiliate/create-commission-correction-for-refund.js +24 -0
- package/.medusa/server/src/workflows/affiliate/create-commission-for-order.js +17 -0
- package/.medusa/server/src/workflows/affiliate/deactivate-affiliate.js +89 -0
- package/.medusa/server/src/workflows/affiliate/enable-distribution.js +120 -0
- package/.medusa/server/src/workflows/affiliate/enrich-affiliate-report.js +75 -0
- package/.medusa/server/src/workflows/affiliate/ensure-affiliate-coupon-promotion.js +410 -0
- package/.medusa/server/src/workflows/affiliate/ensure-affiliate-own-order-promotion.js +240 -0
- package/.medusa/server/src/workflows/affiliate/ensure-default-affiliate-coupon.js +32 -0
- package/.medusa/server/src/workflows/affiliate/expire-affiliate-attribution-state.js +131 -0
- package/.medusa/server/src/workflows/affiliate/finalize-affiliate-order-attribution.js +82 -0
- package/.medusa/server/src/workflows/affiliate/payment-authorization.js +33 -0
- package/.medusa/server/src/workflows/affiliate/promotion-eligibility.js +179 -0
- package/.medusa/server/src/workflows/affiliate/reconcile-affiliate-cart-referral.js +219 -0
- package/.medusa/server/src/workflows/affiliate/reconcile-affiliate-coupon.js +388 -0
- package/.medusa/server/src/workflows/affiliate/refresh-affiliate-promotions.js +114 -0
- package/.medusa/server/src/workflows/affiliate/resolve-coupon-labels.js +40 -0
- package/.medusa/server/src/workflows/affiliate/revoke-affiliate-coupon.js +29 -0
- package/.medusa/server/src/workflows/affiliate/rotate-affiliate-coupon.js +36 -0
- package/.medusa/server/src/workflows/affiliate/run-consignment-report-reminders.js +58 -0
- package/.medusa/server/src/workflows/affiliate/submit-consignment-report.js +279 -0
- package/.medusa/server/src/workflows/affiliate/transfer-batch-aware-inventory.js +172 -0
- package/.medusa/server/src/workflows/affiliate/transfer-consignment-stock.js +106 -0
- package/.medusa/server/src/workflows/affiliate/update-affiliate-coupon-discounts.js +129 -0
- package/.medusa/server/src/workflows/affiliate/utils.js +118 -0
- package/.medusa/server/src/workflows/hooks/complete-cart-affiliate-attribution.js +57 -0
- package/.medusa/server/src/workflows/hooks/complete-cart-order-created-composition.js +111 -0
- package/.medusa/server/src/workflows/hooks/complete-cart-own-order-discount.js +75 -0
- package/.medusa/server/src/workflows/hooks/enforce-unique-promotion-codes.js +33 -0
- package/.medusa/server/src/workflows/hooks/validate-affiliate-customer-code.js +239 -0
- package/.medusa/server/src/workflows/hooks/validate-complete-cart-affiliate-referral.js +137 -0
- package/LICENSE +21 -0
- package/README.md +799 -0
- package/dist/integration/email-events.d.ts +20 -0
- package/dist/integration/email-events.js +50 -0
- package/package.json +84 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withAffiliateCartOperationLock = exports.withAffiliateOperationLocks = exports.acquireAffiliateOperationLocks = exports.affiliateProgramSagaLockKey = exports.affiliateSagaLockKey = exports.runAsIndependentAffiliateOperation = exports.isAffiliateCartOperationLockKeyHeld = exports.affiliateCartLockKey = exports.AffiliateOperationLockTimeoutError = void 0;
|
|
4
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
5
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
6
|
+
class KeyedConnectionPermits {
|
|
7
|
+
constructor(capacity) {
|
|
8
|
+
this.capacity = capacity;
|
|
9
|
+
this.active = 0;
|
|
10
|
+
this.activeKeys = new Set();
|
|
11
|
+
this.waiters = [];
|
|
12
|
+
}
|
|
13
|
+
async acquire(keys) {
|
|
14
|
+
return await new Promise((resolve) => {
|
|
15
|
+
this.waiters.push({ keys, resolve });
|
|
16
|
+
this.dispatch();
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
release(keys) {
|
|
20
|
+
this.active -= 1;
|
|
21
|
+
for (const key of keys) {
|
|
22
|
+
this.activeKeys.delete(key);
|
|
23
|
+
}
|
|
24
|
+
this.dispatch();
|
|
25
|
+
}
|
|
26
|
+
dispatch() {
|
|
27
|
+
for (let index = 0; index < this.waiters.length;) {
|
|
28
|
+
if (this.active >= this.capacity) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const waiter = this.waiters[index];
|
|
32
|
+
if (waiter.keys.some((key) => this.activeKeys.has(key))) {
|
|
33
|
+
index += 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
this.waiters.splice(index, 1);
|
|
37
|
+
this.active += 1;
|
|
38
|
+
for (const key of waiter.keys) {
|
|
39
|
+
this.activeKeys.add(key);
|
|
40
|
+
}
|
|
41
|
+
let released = false;
|
|
42
|
+
waiter.resolve(() => {
|
|
43
|
+
if (released)
|
|
44
|
+
return;
|
|
45
|
+
released = true;
|
|
46
|
+
this.release(waiter.keys);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
class AffiliateOperationLockTimeoutError extends Error {
|
|
52
|
+
constructor(lockKey) {
|
|
53
|
+
super(`Timed out acquiring Affiliate operation lock "${lockKey}".`);
|
|
54
|
+
this.name = "AffiliateOperationLockTimeoutError";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.AffiliateOperationLockTimeoutError = AffiliateOperationLockTimeoutError;
|
|
58
|
+
const heldOperationLocks = new node_async_hooks_1.AsyncLocalStorage();
|
|
59
|
+
const schedulerByClient = new WeakMap();
|
|
60
|
+
const defaultPoolMax = 2;
|
|
61
|
+
const reservedWorkflowConnections = 1;
|
|
62
|
+
const advisoryLockTimeoutMs = 5_000;
|
|
63
|
+
const advisoryLockPollIntervalMs = 25;
|
|
64
|
+
const normalizeLockKeys = (lockKeys) => [...new Set(lockKeys)].sort((left, right) => {
|
|
65
|
+
const leftIsCart = left.startsWith("affiliate-cart:");
|
|
66
|
+
const rightIsCart = right.startsWith("affiliate-cart:");
|
|
67
|
+
if (leftIsCart !== rightIsCart)
|
|
68
|
+
return leftIsCart ? -1 : 1;
|
|
69
|
+
return left.localeCompare(right);
|
|
70
|
+
});
|
|
71
|
+
const combinedFailure = (failures, message) => {
|
|
72
|
+
if (failures.length === 1)
|
|
73
|
+
return failures[0];
|
|
74
|
+
return new AggregateError(failures, message);
|
|
75
|
+
};
|
|
76
|
+
const lockCapacity = (knex) => {
|
|
77
|
+
const configuredMaxValue = knex.client.pool?.max ?? knex.client.config?.pool?.max ?? defaultPoolMax;
|
|
78
|
+
const configuredMax = Number.isFinite(configuredMaxValue)
|
|
79
|
+
? Math.floor(configuredMaxValue)
|
|
80
|
+
: defaultPoolMax;
|
|
81
|
+
if (configuredMax <= reservedWorkflowConnections) {
|
|
82
|
+
throw new Error("Affiliate operation locking requires a PostgreSQL pool with at least two connections.");
|
|
83
|
+
}
|
|
84
|
+
return configuredMax - reservedWorkflowConnections;
|
|
85
|
+
};
|
|
86
|
+
const connectionPermits = (knex) => {
|
|
87
|
+
const client = knex.client;
|
|
88
|
+
let permits = schedulerByClient.get(client);
|
|
89
|
+
if (!permits) {
|
|
90
|
+
permits = new KeyedConnectionPermits(lockCapacity(knex));
|
|
91
|
+
schedulerByClient.set(client, permits);
|
|
92
|
+
}
|
|
93
|
+
return permits;
|
|
94
|
+
};
|
|
95
|
+
const queryBoolean = (result, field) => {
|
|
96
|
+
const rows = result
|
|
97
|
+
?.rows;
|
|
98
|
+
return rows?.[0]?.[field] === true;
|
|
99
|
+
};
|
|
100
|
+
const wait = async (milliseconds) => await new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
101
|
+
const acquireAdvisoryLock = async (connection, lockKey) => {
|
|
102
|
+
const deadline = Date.now() + advisoryLockTimeoutMs;
|
|
103
|
+
while (true) {
|
|
104
|
+
const result = await connection.query("select pg_try_advisory_lock(hashtext($1)) as acquired", [lockKey]);
|
|
105
|
+
if (queryBoolean(result, "acquired"))
|
|
106
|
+
return;
|
|
107
|
+
const remaining = deadline - Date.now();
|
|
108
|
+
if (remaining <= 0) {
|
|
109
|
+
throw new AffiliateOperationLockTimeoutError(lockKey);
|
|
110
|
+
}
|
|
111
|
+
await wait(Math.min(advisoryLockPollIntervalMs, remaining));
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const discardConnection = async (knex, connection, reason, failures) => {
|
|
115
|
+
// Close immediately so an ambiguous query cannot leave a server lock alive.
|
|
116
|
+
// Knex rejects the marker during later pool validation; pg's callback-based
|
|
117
|
+
// end() completes immediately when the client is already ended.
|
|
118
|
+
connection.__knex__disposed =
|
|
119
|
+
reason instanceof Error
|
|
120
|
+
? reason
|
|
121
|
+
: new Error("Affiliate operation lock connection was discarded.");
|
|
122
|
+
try {
|
|
123
|
+
await knex.client.destroyRawConnection(connection);
|
|
124
|
+
}
|
|
125
|
+
catch (destroyError) {
|
|
126
|
+
failures.push(destroyError);
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const pool = knex.client.pool;
|
|
130
|
+
if (!pool?.release) {
|
|
131
|
+
throw new Error("Tarn cannot release the discarded Affiliate operation lock connection.");
|
|
132
|
+
}
|
|
133
|
+
// Tarn returns false only when this exact resource is already absent from
|
|
134
|
+
// `used`, so either result leaves no occupied pool slot.
|
|
135
|
+
pool.release(connection);
|
|
136
|
+
}
|
|
137
|
+
catch (poolReleaseError) {
|
|
138
|
+
failures.push(poolReleaseError);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const releaseAdvisoryLocks = async (connection, lockKeys) => {
|
|
142
|
+
for (const lockKey of [...lockKeys].reverse()) {
|
|
143
|
+
const unlockResult = await connection.query("select pg_advisory_unlock(hashtext($1)) as unlocked", [lockKey]);
|
|
144
|
+
if (!queryBoolean(unlockResult, "unlocked")) {
|
|
145
|
+
throw new Error(`PostgreSQL did not release Affiliate lock "${lockKey}".`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const releaseAfterFailedAcquisition = async (knex, connection, acquisitionError, acquiredLockKeys) => {
|
|
150
|
+
const failures = [acquisitionError];
|
|
151
|
+
if (acquisitionError instanceof AffiliateOperationLockTimeoutError) {
|
|
152
|
+
try {
|
|
153
|
+
await releaseAdvisoryLocks(connection, acquiredLockKeys);
|
|
154
|
+
await knex.client.releaseConnection(connection);
|
|
155
|
+
}
|
|
156
|
+
catch (unlockError) {
|
|
157
|
+
failures.push(unlockError);
|
|
158
|
+
await discardConnection(knex, connection, unlockError, failures);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// A failed query has an unknown server-side outcome.
|
|
163
|
+
await discardConnection(knex, connection, acquisitionError, failures);
|
|
164
|
+
}
|
|
165
|
+
throw combinedFailure(failures, "Affiliate operation lock acquisition failed");
|
|
166
|
+
};
|
|
167
|
+
const affiliateCartLockKey = (cartId) => `affiliate-cart:${cartId}`;
|
|
168
|
+
exports.affiliateCartLockKey = affiliateCartLockKey;
|
|
169
|
+
const isAffiliateCartOperationLockKeyHeld = (lockKey) => heldOperationLocks.getStore()?.has(lockKey) ?? false;
|
|
170
|
+
exports.isAffiliateCartOperationLockKeyHeld = isAffiliateCartOperationLockKeyHeld;
|
|
171
|
+
const runAsIndependentAffiliateOperation = async (operation) => await heldOperationLocks.run(new Set(), operation);
|
|
172
|
+
exports.runAsIndependentAffiliateOperation = runAsIndependentAffiliateOperation;
|
|
173
|
+
const affiliateSagaLockKey = (affiliateId) => `affiliate-saga:${affiliateId}`;
|
|
174
|
+
exports.affiliateSagaLockKey = affiliateSagaLockKey;
|
|
175
|
+
const affiliateProgramSagaLockKey = () => "affiliate-program-saga";
|
|
176
|
+
exports.affiliateProgramSagaLockKey = affiliateProgramSagaLockKey;
|
|
177
|
+
const acquireAffiliateOperationLocks = async (container, lockKeys) => {
|
|
178
|
+
const normalizedLockKeys = normalizeLockKeys(lockKeys);
|
|
179
|
+
const inheritedLocks = heldOperationLocks.getStore();
|
|
180
|
+
const acquiredLockKeys = normalizedLockKeys.filter((lockKey) => !inheritedLocks?.has(lockKey));
|
|
181
|
+
if (!acquiredLockKeys.length) {
|
|
182
|
+
return {
|
|
183
|
+
run: (operation) => operation(),
|
|
184
|
+
release: async () => undefined,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const knex = container.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
188
|
+
const releasePermit = await connectionPermits(knex).acquire(acquiredLockKeys);
|
|
189
|
+
let connection;
|
|
190
|
+
try {
|
|
191
|
+
connection = await knex.client.acquireConnection();
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
releasePermit();
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
const lockedKeys = [];
|
|
198
|
+
try {
|
|
199
|
+
for (const lockKey of acquiredLockKeys) {
|
|
200
|
+
await acquireAdvisoryLock(connection, lockKey);
|
|
201
|
+
lockedKeys.push(lockKey);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (acquisitionError) {
|
|
205
|
+
try {
|
|
206
|
+
return await releaseAfterFailedAcquisition(knex, connection, acquisitionError, lockedKeys);
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
releasePermit();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const operationLocks = new Set(inheritedLocks ?? []);
|
|
213
|
+
for (const lockKey of acquiredLockKeys) {
|
|
214
|
+
operationLocks.add(lockKey);
|
|
215
|
+
}
|
|
216
|
+
let releasePromise;
|
|
217
|
+
return {
|
|
218
|
+
run: (operation) => heldOperationLocks.run(operationLocks, operation),
|
|
219
|
+
release: () => {
|
|
220
|
+
releasePromise ??= (async () => {
|
|
221
|
+
const failures = [];
|
|
222
|
+
for (const lockKey of acquiredLockKeys) {
|
|
223
|
+
operationLocks.delete(lockKey);
|
|
224
|
+
}
|
|
225
|
+
let unlocked = false;
|
|
226
|
+
try {
|
|
227
|
+
await releaseAdvisoryLocks(connection, acquiredLockKeys);
|
|
228
|
+
unlocked = true;
|
|
229
|
+
}
|
|
230
|
+
catch (unlockError) {
|
|
231
|
+
failures.push(unlockError);
|
|
232
|
+
await discardConnection(knex, connection, unlockError, failures);
|
|
233
|
+
}
|
|
234
|
+
if (unlocked) {
|
|
235
|
+
try {
|
|
236
|
+
await knex.client.releaseConnection(connection);
|
|
237
|
+
}
|
|
238
|
+
catch (releaseError) {
|
|
239
|
+
failures.push(releaseError);
|
|
240
|
+
await discardConnection(knex, connection, releaseError, failures);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
releasePermit();
|
|
244
|
+
if (failures.length) {
|
|
245
|
+
throw combinedFailure(failures, "Affiliate operation lease cleanup failed");
|
|
246
|
+
}
|
|
247
|
+
})();
|
|
248
|
+
return releasePromise;
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
exports.acquireAffiliateOperationLocks = acquireAffiliateOperationLocks;
|
|
253
|
+
const withAffiliateOperationLocks = async (container, lockKeys, operation) => {
|
|
254
|
+
const normalizedLockKeys = normalizeLockKeys(lockKeys);
|
|
255
|
+
const inheritedLocks = heldOperationLocks.getStore();
|
|
256
|
+
const acquiredLockKeys = normalizedLockKeys.filter((lockKey) => !inheritedLocks?.has(lockKey));
|
|
257
|
+
if (!acquiredLockKeys.length)
|
|
258
|
+
return await operation();
|
|
259
|
+
const knex = container.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
260
|
+
const releasePermit = await connectionPermits(knex).acquire(acquiredLockKeys);
|
|
261
|
+
let operationLocks;
|
|
262
|
+
try {
|
|
263
|
+
const connection = await knex.client.acquireConnection();
|
|
264
|
+
const lockedKeys = [];
|
|
265
|
+
try {
|
|
266
|
+
for (const lockKey of acquiredLockKeys) {
|
|
267
|
+
await acquireAdvisoryLock(connection, lockKey);
|
|
268
|
+
lockedKeys.push(lockKey);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (acquisitionError) {
|
|
272
|
+
return await releaseAfterFailedAcquisition(knex, connection, acquisitionError, lockedKeys);
|
|
273
|
+
}
|
|
274
|
+
operationLocks = new Set(inheritedLocks ?? []);
|
|
275
|
+
for (const lockKey of acquiredLockKeys) {
|
|
276
|
+
operationLocks.add(lockKey);
|
|
277
|
+
}
|
|
278
|
+
const failures = [];
|
|
279
|
+
let result;
|
|
280
|
+
try {
|
|
281
|
+
result = await heldOperationLocks.run(operationLocks, operation);
|
|
282
|
+
}
|
|
283
|
+
catch (operationError) {
|
|
284
|
+
failures.push(operationError);
|
|
285
|
+
}
|
|
286
|
+
for (const lockKey of acquiredLockKeys) {
|
|
287
|
+
operationLocks.delete(lockKey);
|
|
288
|
+
}
|
|
289
|
+
let unlocked = false;
|
|
290
|
+
try {
|
|
291
|
+
await releaseAdvisoryLocks(connection, acquiredLockKeys);
|
|
292
|
+
unlocked = true;
|
|
293
|
+
}
|
|
294
|
+
catch (unlockError) {
|
|
295
|
+
failures.push(unlockError);
|
|
296
|
+
await discardConnection(knex, connection, unlockError, failures);
|
|
297
|
+
}
|
|
298
|
+
if (unlocked) {
|
|
299
|
+
await knex.client.releaseConnection(connection);
|
|
300
|
+
}
|
|
301
|
+
if (failures.length) {
|
|
302
|
+
throw combinedFailure(failures, "Affiliate operation cleanup failed");
|
|
303
|
+
}
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
finally {
|
|
307
|
+
for (const lockKey of acquiredLockKeys) {
|
|
308
|
+
operationLocks?.delete(lockKey);
|
|
309
|
+
}
|
|
310
|
+
releasePermit();
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
exports.withAffiliateOperationLocks = withAffiliateOperationLocks;
|
|
314
|
+
const withAffiliateCartOperationLock = async (container, cartId, operation) => await (0, exports.withAffiliateOperationLocks)(container, [(0, exports.affiliateCartLockKey)(cartId)], operation);
|
|
315
|
+
exports.withAffiliateCartOperationLock = withAffiliateCartOperationLock;
|
|
316
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWZmaWxpYXRlLWNhcnQtb3BlcmF0aW9uLWxvY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvdXRpbHMvYWZmaWxpYXRlLWNhcnQtb3BlcmF0aW9uLWxvY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsdURBQXFEO0FBR3JELHFEQUFzRTtBQXlCdEUsTUFBTSxzQkFBc0I7SUFLMUIsWUFBNkIsUUFBZ0I7UUFBaEIsYUFBUSxHQUFSLFFBQVEsQ0FBUTtRQUpyQyxXQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ0YsZUFBVSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7UUFDL0IsWUFBTyxHQUFtQixFQUFFLENBQUM7SUFFRSxDQUFDO0lBRWpELEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBYztRQUMxQixPQUFPLE1BQU0sSUFBSSxPQUFPLENBQWEsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUMvQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3JDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNsQixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTyxPQUFPLENBQUMsSUFBYztRQUM1QixJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQztRQUNqQixLQUFLLE1BQU0sR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzlCLENBQUM7UUFDRCxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDbEIsQ0FBQztJQUVPLFFBQVE7UUFDZCxLQUFLLElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUksQ0FBQztZQUNsRCxJQUFJLElBQUksQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUNqQyxPQUFPO1lBQ1QsQ0FBQztZQUNELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDbkMsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUN4RCxLQUFLLElBQUksQ0FBQyxDQUFDO2dCQUNYLFNBQVM7WUFDWCxDQUFDO1lBRUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQzlCLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxDQUFDO1lBQ2pCLEtBQUssTUFBTSxHQUFHLElBQUksTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUM5QixJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUMzQixDQUFDO1lBQ0QsSUFBSSxRQUFRLEdBQUcsS0FBSyxDQUFDO1lBQ3JCLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFO2dCQUNsQixJQUFJLFFBQVE7b0JBQUUsT0FBTztnQkFDckIsUUFBUSxHQUFHLElBQUksQ0FBQztnQkFDaEIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDNUIsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBRUQsTUFBYSxrQ0FBbUMsU0FBUSxLQUFLO0lBQzNELFlBQVksT0FBZTtRQUN6QixLQUFLLENBQUMsaURBQWlELE9BQU8sSUFBSSxDQUFDLENBQUM7UUFDcEUsSUFBSSxDQUFDLElBQUksR0FBRyxvQ0FBb0MsQ0FBQztJQUNuRCxDQUFDO0NBQ0Y7QUFMRCxnRkFLQztBQUVELE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxvQ0FBaUIsRUFBdUIsQ0FBQztBQUN4RSxNQUFNLGlCQUFpQixHQUFHLElBQUksT0FBTyxFQUFrQyxDQUFDO0FBRXhFLE1BQU0sY0FBYyxHQUFHLENBQUMsQ0FBQztBQUN6QixNQUFNLDJCQUEyQixHQUFHLENBQUMsQ0FBQztBQUN0QyxNQUFNLHFCQUFxQixHQUFHLEtBQUssQ0FBQztBQUNwQyxNQUFNLDBCQUEwQixHQUFHLEVBQUUsQ0FBQztBQUV0QyxNQUFNLGlCQUFpQixHQUFHLENBQUMsUUFBMkIsRUFBWSxFQUFFLENBQ2xFLENBQUMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRTtJQUMxQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLGlCQUFpQixDQUFDLENBQUM7SUFDdEQsTUFBTSxXQUFXLEdBQUcsS0FBSyxDQUFDLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0lBQ3hELElBQUksVUFBVSxLQUFLLFdBQVc7UUFBRSxPQUFPLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMzRCxPQUFPLElBQUksQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDbkMsQ0FBQyxDQUFDLENBQUM7QUFFTCxNQUFNLGVBQWUsR0FBRyxDQUFDLFFBQW1CLEVBQUUsT0FBZSxFQUFXLEVBQUU7SUFDeEUsSUFBSSxRQUFRLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM5QyxPQUFPLElBQUksY0FBYyxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUMvQyxDQUFDLENBQUM7QUFFRixNQUFNLFlBQVksR0FBRyxDQUFDLElBQXNCLEVBQVUsRUFBRTtJQUN0RCxNQUFNLGtCQUFrQixHQUN0QixJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxHQUFHLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLEdBQUcsSUFBSSxjQUFjLENBQUM7SUFDM0UsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsQ0FBQztRQUN2RCxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztRQUNoQyxDQUFDLENBQUMsY0FBYyxDQUFDO0lBQ25CLElBQUksYUFBYSxJQUFJLDJCQUEyQixFQUFFLENBQUM7UUFDakQsTUFBTSxJQUFJLEtBQUssQ0FDYix1RkFBdUYsQ0FDeEYsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPLGFBQWEsR0FBRywyQkFBMkIsQ0FBQztBQUNyRCxDQUFDLENBQUM7QUFFRixNQUFNLGlCQUFpQixHQUFHLENBQUMsSUFBc0IsRUFBMEIsRUFBRTtJQUMzRSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBZ0IsQ0FBQztJQUNyQyxJQUFJLE9BQU8sR0FBRyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDNUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2IsT0FBTyxHQUFHLElBQUksc0JBQXNCLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDekQsaUJBQWlCLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBQ0QsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQyxDQUFDO0FBRUYsTUFBTSxZQUFZLEdBQUcsQ0FBQyxNQUFlLEVBQUUsS0FBYSxFQUFXLEVBQUU7SUFDL0QsTUFBTSxJQUFJLEdBQUksTUFBMkQ7UUFDdkUsRUFBRSxJQUFJLENBQUM7SUFDVCxPQUFPLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLEtBQUssSUFBSSxDQUFDO0FBQ3JDLENBQUMsQ0FBQztBQUVGLE1BQU0sSUFBSSxHQUFHLEtBQUssRUFBRSxZQUFvQixFQUFpQixFQUFFLENBQ3pELE1BQU0sSUFBSSxPQUFPLENBQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FBQztBQUUxRSxNQUFNLG1CQUFtQixHQUFHLEtBQUssRUFDL0IsVUFBa0MsRUFDbEMsT0FBZSxFQUNBLEVBQUU7SUFDakIsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLHFCQUFxQixDQUFDO0lBQ3BELE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDWixNQUFNLE1BQU0sR0FBRyxNQUFNLFVBQVUsQ0FBQyxLQUFLLENBQ25DLHVEQUF1RCxFQUN2RCxDQUFDLE9BQU8sQ0FBQyxDQUNWLENBQUM7UUFDRixJQUFJLFlBQVksQ0FBQyxNQUFNLEVBQUUsVUFBVSxDQUFDO1lBQUUsT0FBTztRQUU3QyxNQUFNLFNBQVMsR0FBRyxRQUFRLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ3hDLElBQUksU0FBUyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ25CLE1BQU0sSUFBSSxrQ0FBa0MsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN4RCxDQUFDO1FBQ0QsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQywwQkFBMEIsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDO0lBQzlELENBQUM7QUFDSCxDQUFDLENBQUM7QUFFRixNQUFNLGlCQUFpQixHQUFHLEtBQUssRUFDN0IsSUFBc0IsRUFDdEIsVUFBa0MsRUFDbEMsTUFBZSxFQUNmLFFBQW1CLEVBQ0osRUFBRTtJQUNqQiw0RUFBNEU7SUFDNUUsNEVBQTRFO0lBQzVFLGdFQUFnRTtJQUNoRSxVQUFVLENBQUMsZ0JBQWdCO1FBQ3pCLE1BQU0sWUFBWSxLQUFLO1lBQ3JCLENBQUMsQ0FBQyxNQUFNO1lBQ1IsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLG9EQUFvRCxDQUFDLENBQUM7SUFFdEUsSUFBSSxDQUFDO1FBQ0gsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLG9CQUFvQixDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ3JELENBQUM7SUFBQyxPQUFPLFlBQVksRUFBRSxDQUFDO1FBQ3RCLFFBQVEsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDOUIsQ0FBQztJQUVELElBQUksQ0FBQztRQUNILE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDO1FBQzlCLElBQUksQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUM7WUFDbkIsTUFBTSxJQUFJLEtBQUssQ0FDYix3RUFBd0UsQ0FDekUsQ0FBQztRQUNKLENBQUM7UUFDRCwwRUFBMEU7UUFDMUUseURBQXlEO1FBQ3pELElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDM0IsQ0FBQztJQUFDLE9BQU8sZ0JBQWdCLEVBQUUsQ0FBQztRQUMxQixRQUFRLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDbEMsQ0FBQztBQUNILENBQUMsQ0FBQztBQUVGLE1BQU0sb0JBQW9CLEdBQUcsS0FBSyxFQUNoQyxVQUFrQyxFQUNsQyxRQUEyQixFQUNaLEVBQUU7SUFDakIsS0FBSyxNQUFNLE9BQU8sSUFBSSxDQUFDLEdBQUcsUUFBUSxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztRQUM5QyxNQUFNLFlBQVksR0FBRyxNQUFNLFVBQVUsQ0FBQyxLQUFLLENBQ3pDLHFEQUFxRCxFQUNyRCxDQUFDLE9BQU8sQ0FBQyxDQUNWLENBQUM7UUFDRixJQUFJLENBQUMsWUFBWSxDQUFDLFlBQVksRUFBRSxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQzVDLE1BQU0sSUFBSSxLQUFLLENBQ2IsOENBQThDLE9BQU8sSUFBSSxDQUMxRCxDQUFDO1FBQ0osQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDLENBQUM7QUFFRixNQUFNLDZCQUE2QixHQUFHLEtBQUssRUFDekMsSUFBc0IsRUFDdEIsVUFBa0MsRUFDbEMsZ0JBQXlCLEVBQ3pCLGdCQUFtQyxFQUNuQixFQUFFO0lBQ2xCLE1BQU0sUUFBUSxHQUFHLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUNwQyxJQUFJLGdCQUFnQixZQUFZLGtDQUFrQyxFQUFFLENBQUM7UUFDbkUsSUFBSSxDQUFDO1lBQ0gsTUFBTSxvQkFBb0IsQ0FBQyxVQUFVLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztZQUN6RCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEQsQ0FBQztRQUFDLE9BQU8sV0FBVyxFQUFFLENBQUM7WUFDckIsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUMzQixNQUFNLGlCQUFpQixDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ25FLENBQUM7SUFDSCxDQUFDO1NBQU0sQ0FBQztRQUNOLHFEQUFxRDtRQUNyRCxNQUFNLGlCQUFpQixDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsZ0JBQWdCLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDeEUsQ0FBQztJQUNELE1BQU0sZUFBZSxDQUNuQixRQUFRLEVBQ1IsNkNBQTZDLENBQzlDLENBQUM7QUFDSixDQUFDLENBQUM7QUFFSyxNQUFNLG9CQUFvQixHQUFHLENBQUMsTUFBYyxFQUFVLEVBQUUsQ0FDN0Qsa0JBQWtCLE1BQU0sRUFBRSxDQUFDO0FBRGhCLFFBQUEsb0JBQW9CLHdCQUNKO0FBRXRCLE1BQU0sbUNBQW1DLEdBQUcsQ0FBQyxPQUFlLEVBQVcsRUFBRSxDQUM5RSxrQkFBa0IsQ0FBQyxRQUFRLEVBQUUsRUFBRSxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUksS0FBSyxDQUFDO0FBRDFDLFFBQUEsbUNBQW1DLHVDQUNPO0FBRWhELE1BQU0sa0NBQWtDLEdBQUcsS0FBSyxFQUNyRCxTQUEyQixFQUNmLEVBQUUsQ0FBQyxNQUFNLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxJQUFJLEdBQUcsRUFBRSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0FBRnZELFFBQUEsa0NBQWtDLHNDQUVxQjtBQUU3RCxNQUFNLG9CQUFvQixHQUFHLENBQUMsV0FBbUIsRUFBVSxFQUFFLENBQ2xFLGtCQUFrQixXQUFXLEVBQUUsQ0FBQztBQURyQixRQUFBLG9CQUFvQix3QkFDQztBQUUzQixNQUFNLDJCQUEyQixHQUFHLEdBQVcsRUFBRSxDQUN0RCx3QkFBd0IsQ0FBQztBQURkLFFBQUEsMkJBQTJCLCtCQUNiO0FBT3BCLE1BQU0sOEJBQThCLEdBQUcsS0FBSyxFQUNqRCxTQUEwQixFQUMxQixRQUEyQixFQUNXLEVBQUU7SUFDeEMsTUFBTSxrQkFBa0IsR0FBRyxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUN2RCxNQUFNLGNBQWMsR0FBRyxrQkFBa0IsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUNyRCxNQUFNLGdCQUFnQixHQUFHLGtCQUFrQixDQUFDLE1BQU0sQ0FDaEQsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsY0FBYyxFQUFFLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FDM0MsQ0FBQztJQUNGLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUM3QixPQUFPO1lBQ0wsR0FBRyxFQUFFLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FBQyxTQUFTLEVBQUU7WUFDL0IsT0FBTyxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUMsU0FBUztTQUMvQixDQUFDO0lBQ0osQ0FBQztJQUVELE1BQU0sSUFBSSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQzVCLGlDQUF5QixDQUFDLGFBQWEsQ0FDcEIsQ0FBQztJQUN0QixNQUFNLGFBQWEsR0FBRyxNQUFNLGlCQUFpQixDQUFDLElBQUksQ0FBQyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQzlFLElBQUksVUFBa0MsQ0FBQztJQUN2QyxJQUFJLENBQUM7UUFDSCxVQUFVLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGlCQUFpQixFQUFFLENBQUM7SUFDckQsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixhQUFhLEVBQUUsQ0FBQztRQUNoQixNQUFNLEtBQUssQ0FBQztJQUNkLENBQUM7SUFFRCxNQUFNLFVBQVUsR0FBYSxFQUFFLENBQUM7SUFDaEMsSUFBSSxDQUFDO1FBQ0gsS0FBSyxNQUFNLE9BQU8sSUFBSSxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3ZDLE1BQU0sbUJBQW1CLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQy9DLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDM0IsQ0FBQztJQUNILENBQUM7SUFBQyxPQUFPLGdCQUFnQixFQUFFLENBQUM7UUFDMUIsSUFBSSxDQUFDO1lBQ0gsT0FBTyxNQUFNLDZCQUE2QixDQUN4QyxJQUFJLEVBQ0osVUFBVSxFQUNWLGdCQUFnQixFQUNoQixVQUFVLENBQ1gsQ0FBQztRQUNKLENBQUM7Z0JBQVMsQ0FBQztZQUNULGFBQWEsRUFBRSxDQUFDO1FBQ2xCLENBQUM7SUFDSCxDQUFDO0lBQ0QsTUFBTSxjQUFjLEdBQUcsSUFBSSxHQUFHLENBQUMsY0FBYyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3JELEtBQUssTUFBTSxPQUFPLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztRQUN2QyxjQUFjLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzlCLENBQUM7SUFDRCxJQUFJLGNBQXlDLENBQUM7SUFFOUMsT0FBTztRQUNMLEdBQUcsRUFBRSxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQUMsa0JBQWtCLENBQUMsR0FBRyxDQUFDLGNBQWMsRUFBRSxTQUFTLENBQUM7UUFDckUsT0FBTyxFQUFFLEdBQUcsRUFBRTtZQUNaLGNBQWMsS0FBSyxDQUFDLEtBQUssSUFBSSxFQUFFO2dCQUM3QixNQUFNLFFBQVEsR0FBYyxFQUFFLENBQUM7Z0JBQy9CLEtBQUssTUFBTSxPQUFPLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztvQkFDdkMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztnQkFDakMsQ0FBQztnQkFDRCxJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUM7Z0JBQ3JCLElBQUksQ0FBQztvQkFDSCxNQUFNLG9CQUFvQixDQUFDLFVBQVUsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO29CQUN6RCxRQUFRLEdBQUcsSUFBSSxDQUFDO2dCQUNsQixDQUFDO2dCQUFDLE9BQU8sV0FBVyxFQUFFLENBQUM7b0JBQ3JCLFFBQVEsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7b0JBQzNCLE1BQU0saUJBQWlCLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQ25FLENBQUM7Z0JBRUQsSUFBSSxRQUFRLEVBQUUsQ0FBQztvQkFDYixJQUFJLENBQUM7d0JBQ0gsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxDQUFDO29CQUNsRCxDQUFDO29CQUFDLE9BQU8sWUFBWSxFQUFFLENBQUM7d0JBQ3RCLFFBQVEsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7d0JBQzVCLE1BQU0saUJBQWlCLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsUUFBUSxDQUFDLENBQUM7b0JBQ3BFLENBQUM7Z0JBQ0gsQ0FBQztnQkFFRCxhQUFhLEVBQUUsQ0FBQztnQkFDaEIsSUFBSSxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUM7b0JBQ3BCLE1BQU0sZUFBZSxDQUNuQixRQUFRLEVBQ1IsMENBQTBDLENBQzNDLENBQUM7Z0JBQ0osQ0FBQztZQUNILENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDTCxPQUFPLGNBQWMsQ0FBQztRQUN4QixDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUMsQ0FBQztBQXpGVyxRQUFBLDhCQUE4QixrQ0F5RnpDO0FBRUssTUFBTSwyQkFBMkIsR0FBRyxLQUFLLEVBQzlDLFNBQTBCLEVBQzFCLFFBQTJCLEVBQzNCLFNBQTJCLEVBQ2YsRUFBRTtJQUNkLE1BQU0sa0JBQWtCLEdBQUcsaUJBQWlCLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDdkQsTUFBTSxjQUFjLEdBQUcsa0JBQWtCLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDckQsTUFBTSxnQkFBZ0IsR0FBRyxrQkFBa0IsQ0FBQyxNQUFNLENBQ2hELENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDLGNBQWMsRUFBRSxHQUFHLENBQUMsT0FBTyxDQUFDLENBQzNDLENBQUM7SUFDRixJQUFJLENBQUMsZ0JBQWdCLENBQUMsTUFBTTtRQUFFLE9BQU8sTUFBTSxTQUFTLEVBQUUsQ0FBQztJQUV2RCxNQUFNLElBQUksR0FBRyxTQUFTLENBQUMsT0FBTyxDQUM1QixpQ0FBeUIsQ0FBQyxhQUFhLENBQ3BCLENBQUM7SUFDdEIsTUFBTSxhQUFhLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUM5RSxJQUFJLGNBQXVDLENBQUM7SUFFNUMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDekQsTUFBTSxVQUFVLEdBQWEsRUFBRSxDQUFDO1FBQ2hDLElBQUksQ0FBQztZQUNILEtBQUssTUFBTSxPQUFPLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztnQkFDdkMsTUFBTSxtQkFBbUIsQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQy9DLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDM0IsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLGdCQUFnQixFQUFFLENBQUM7WUFDMUIsT0FBTyxNQUFNLDZCQUE2QixDQUN4QyxJQUFJLEVBQ0osVUFBVSxFQUNWLGdCQUFnQixFQUNoQixVQUFVLENBQ1gsQ0FBQztRQUNKLENBQUM7UUFDRCxjQUFjLEdBQUcsSUFBSSxHQUFHLENBQUMsY0FBYyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQy9DLEtBQUssTUFBTSxPQUFPLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztZQUN2QyxjQUFjLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzlCLENBQUM7UUFDRCxNQUFNLFFBQVEsR0FBYyxFQUFFLENBQUM7UUFDL0IsSUFBSSxNQUFxQixDQUFDO1FBQzFCLElBQUksQ0FBQztZQUNILE1BQU0sR0FBRyxNQUFNLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxjQUFjLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDbkUsQ0FBQztRQUFDLE9BQU8sY0FBYyxFQUFFLENBQUM7WUFDeEIsUUFBUSxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUNoQyxDQUFDO1FBQ0QsS0FBSyxNQUFNLE9BQU8sSUFBSSxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3ZDLGNBQWMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDakMsQ0FBQztRQUNELElBQUksUUFBUSxHQUFHLEtBQUssQ0FBQztRQUNyQixJQUFJLENBQUM7WUFDSCxNQUFNLG9CQUFvQixDQUFDLFVBQVUsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1lBQ3pELFFBQVEsR0FBRyxJQUFJLENBQUM7UUFDbEIsQ0FBQztRQUFDLE9BQU8sV0FBVyxFQUFFLENBQUM7WUFDckIsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUMzQixNQUFNLGlCQUFpQixDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ25FLENBQUM7UUFFRCxJQUFJLFFBQVEsRUFBRSxDQUFDO1lBQ2IsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ2xELENBQUM7UUFFRCxJQUFJLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNwQixNQUFNLGVBQWUsQ0FBQyxRQUFRLEVBQUUsb0NBQW9DLENBQUMsQ0FBQztRQUN4RSxDQUFDO1FBQ0QsT0FBTyxNQUFXLENBQUM7SUFDckIsQ0FBQztZQUFTLENBQUM7UUFDVCxLQUFLLE1BQU0sT0FBTyxJQUFJLGdCQUFnQixFQUFFLENBQUM7WUFDdkMsY0FBYyxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNsQyxDQUFDO1FBQ0QsYUFBYSxFQUFFLENBQUM7SUFDbEIsQ0FBQztBQUNILENBQUMsQ0FBQztBQXZFVyxRQUFBLDJCQUEyQiwrQkF1RXRDO0FBRUssTUFBTSw4QkFBOEIsR0FBRyxLQUFLLEVBQ2pELFNBQTBCLEVBQzFCLE1BQWMsRUFDZCxTQUEyQixFQUNmLEVBQUUsQ0FDZCxNQUFNLElBQUEsbUNBQTJCLEVBQy9CLFNBQVMsRUFDVCxDQUFDLElBQUEsNEJBQW9CLEVBQUMsTUFBTSxDQUFDLENBQUMsRUFDOUIsU0FBUyxDQUNWLENBQUM7QUFUUyxRQUFBLDhCQUE4QixrQ0FTdkMifQ==
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.consumeAffiliatePromotionMutationAuthorization = exports.issueAffiliatePromotionMutationAuthorization = exports.affiliatePromotionAuthorizationInputKey = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
exports.affiliatePromotionAuthorizationInputKey = "__affiliate_promotion_mutation_authorization";
|
|
6
|
+
const pendingAuthorizations = new Map();
|
|
7
|
+
const authorizationTtlMs = 30_000;
|
|
8
|
+
const normalizedCodes = (codes) => codes.map((code) => code.trim().toUpperCase());
|
|
9
|
+
const scopesMatch = (authorization, scope) => {
|
|
10
|
+
const authorizedCodes = normalizedCodes(authorization.promo_codes);
|
|
11
|
+
const requestedCodes = normalizedCodes(scope.promo_codes);
|
|
12
|
+
return (authorization.cart_id === scope.cart_id &&
|
|
13
|
+
authorization.coupon_id === scope.coupon_id &&
|
|
14
|
+
authorization.promotion_id === scope.promotion_id &&
|
|
15
|
+
authorization.action === scope.action &&
|
|
16
|
+
authorizedCodes.length === requestedCodes.length &&
|
|
17
|
+
authorizedCodes.every((code, index) => code === requestedCodes[index]));
|
|
18
|
+
};
|
|
19
|
+
const issueAffiliatePromotionMutationAuthorization = (scope) => {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
for (const [token, authorization] of pendingAuthorizations) {
|
|
22
|
+
if (authorization.expires_at <= now)
|
|
23
|
+
pendingAuthorizations.delete(token);
|
|
24
|
+
}
|
|
25
|
+
const token = (0, node_crypto_1.randomBytes)(32).toString("base64url");
|
|
26
|
+
pendingAuthorizations.set(token, {
|
|
27
|
+
...scope,
|
|
28
|
+
promo_codes: [...scope.promo_codes],
|
|
29
|
+
expires_at: now + authorizationTtlMs,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
token,
|
|
33
|
+
revoke: () => {
|
|
34
|
+
pendingAuthorizations.delete(token);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
exports.issueAffiliatePromotionMutationAuthorization = issueAffiliatePromotionMutationAuthorization;
|
|
39
|
+
const consumeAffiliatePromotionMutationAuthorization = (token, scope) => {
|
|
40
|
+
if (typeof token !== "string")
|
|
41
|
+
return false;
|
|
42
|
+
const authorization = pendingAuthorizations.get(token);
|
|
43
|
+
pendingAuthorizations.delete(token);
|
|
44
|
+
return Boolean(authorization &&
|
|
45
|
+
authorization.expires_at > Date.now() &&
|
|
46
|
+
scopesMatch(authorization, scope));
|
|
47
|
+
};
|
|
48
|
+
exports.consumeAffiliatePromotionMutationAuthorization = consumeAffiliatePromotionMutationAuthorization;
|
|
49
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWZmaWxpYXRlLXByb21vdGlvbi1tdXRhdGlvbi1hdXRob3JpemF0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL2FmZmlsaWF0ZS1wcm9tb3Rpb24tbXV0YXRpb24tYXV0aG9yaXphdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2Q0FBMEM7QUFJN0IsUUFBQSx1Q0FBdUMsR0FDbEQsOENBQXVELENBQUM7QUFjMUQsTUFBTSxxQkFBcUIsR0FBRyxJQUFJLEdBQUcsRUFBZ0MsQ0FBQztBQUN0RSxNQUFNLGtCQUFrQixHQUFHLE1BQU0sQ0FBQztBQUVsQyxNQUFNLGVBQWUsR0FBRyxDQUFDLEtBQWUsRUFBWSxFQUFFLENBQ3BELEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO0FBRWpELE1BQU0sV0FBVyxHQUFHLENBQ2xCLGFBQW1DLEVBQ25DLEtBQW1ELEVBQzFDLEVBQUU7SUFDWCxNQUFNLGVBQWUsR0FBRyxlQUFlLENBQUMsYUFBYSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQ25FLE1BQU0sY0FBYyxHQUFHLGVBQWUsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDMUQsT0FBTyxDQUNMLGFBQWEsQ0FBQyxPQUFPLEtBQUssS0FBSyxDQUFDLE9BQU87UUFDdkMsYUFBYSxDQUFDLFNBQVMsS0FBSyxLQUFLLENBQUMsU0FBUztRQUMzQyxhQUFhLENBQUMsWUFBWSxLQUFLLEtBQUssQ0FBQyxZQUFZO1FBQ2pELGFBQWEsQ0FBQyxNQUFNLEtBQUssS0FBSyxDQUFDLE1BQU07UUFDckMsZUFBZSxDQUFDLE1BQU0sS0FBSyxjQUFjLENBQUMsTUFBTTtRQUNoRCxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsSUFBSSxLQUFLLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUN2RSxDQUFDO0FBQ0osQ0FBQyxDQUFDO0FBRUssTUFBTSw0Q0FBNEMsR0FBRyxDQUMxRCxLQUFtRCxFQUNoQixFQUFFO0lBQ3JDLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN2QixLQUFLLE1BQU0sQ0FBQyxLQUFLLEVBQUUsYUFBYSxDQUFDLElBQUkscUJBQXFCLEVBQUUsQ0FBQztRQUMzRCxJQUFJLGFBQWEsQ0FBQyxVQUFVLElBQUksR0FBRztZQUFFLHFCQUFxQixDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBRUQsTUFBTSxLQUFLLEdBQUcsSUFBQSx5QkFBVyxFQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUNwRCxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFO1FBQy9CLEdBQUcsS0FBSztRQUNSLFdBQVcsRUFBRSxDQUFDLEdBQUcsS0FBSyxDQUFDLFdBQVcsQ0FBQztRQUNuQyxVQUFVLEVBQUUsR0FBRyxHQUFHLGtCQUFrQjtLQUNyQyxDQUFDLENBQUM7SUFDSCxPQUFPO1FBQ0wsS0FBSztRQUNMLE1BQU0sRUFBRSxHQUFHLEVBQUU7WUFDWCxxQkFBcUIsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDdEMsQ0FBQztLQUNGLENBQUM7QUFDSixDQUFDLENBQUM7QUFwQlcsUUFBQSw0Q0FBNEMsZ0RBb0J2RDtBQUVLLE1BQU0sOENBQThDLEdBQUcsQ0FDNUQsS0FBYyxFQUNkLEtBQW1ELEVBQzFDLEVBQUU7SUFDWCxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVE7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUU1QyxNQUFNLGFBQWEsR0FBRyxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDdkQscUJBQXFCLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3BDLE9BQU8sT0FBTyxDQUNaLGFBQWE7UUFDYixhQUFhLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUU7UUFDckMsV0FBVyxDQUFDLGFBQWEsRUFBRSxLQUFLLENBQUMsQ0FDbEMsQ0FBQztBQUNKLENBQUMsQ0FBQztBQWJXLFFBQUEsOENBQThDLGtEQWF6RCJ9
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readCartPromotions = void 0;
|
|
4
|
+
const readCartPromotions = (promotions) => (promotions ?? []).filter((promotion) => typeof promotion === "object" &&
|
|
5
|
+
promotion !== null &&
|
|
6
|
+
typeof promotion.id === "string" &&
|
|
7
|
+
promotion.id.length > 0);
|
|
8
|
+
exports.readCartPromotions = readCartPromotions;
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FydC1wcm9tb3Rpb25zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL2NhcnQtcHJvbW90aW9ucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFjTyxNQUFNLGtCQUFrQixHQUFHLENBQ2hDLFVBQW1DLEVBQzlCLEVBQUUsQ0FDUCxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQ3ZCLENBQUMsU0FBUyxFQUFrQixFQUFFLENBQzVCLE9BQU8sU0FBUyxLQUFLLFFBQVE7SUFDN0IsU0FBUyxLQUFLLElBQUk7SUFDbEIsT0FBTyxTQUFTLENBQUMsRUFBRSxLQUFLLFFBQVE7SUFDaEMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUMxQixDQUFBO0FBVFUsUUFBQSxrQkFBa0Isc0JBUzVCIn0=
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Money = exports.getCurrencyDecimals = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
/**
|
|
6
|
+
* Fallback used when a currency is unknown to Medusa's static currency table.
|
|
7
|
+
* Most ISO currencies use 2 minor-unit digits; the exceptions (0 for JPY, 3 for
|
|
8
|
+
* KWD/BHD, etc.) are resolved from `defaultCurrencies` by `getCurrencyDecimals`.
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_CURRENCY_DECIMAL_PLACES = 2;
|
|
11
|
+
/**
|
|
12
|
+
* Minor-unit digit count for a currency, e.g. EUR/USD = 2, JPY = 0, KWD = 3.
|
|
13
|
+
* Sourced from Medusa's installed static currency data so commission rounding
|
|
14
|
+
* respects each currency's precision instead of assuming two decimals.
|
|
15
|
+
*/
|
|
16
|
+
const getCurrencyDecimals = (currencyCode) => utils_1.defaultCurrencies[currencyCode.toUpperCase()]?.decimal_digits ??
|
|
17
|
+
DEFAULT_CURRENCY_DECIMAL_PLACES;
|
|
18
|
+
exports.getCurrencyDecimals = getCurrencyDecimals;
|
|
19
|
+
/**
|
|
20
|
+
* Immutable value object for authoritative money arithmetic. Wraps MathBN
|
|
21
|
+
* (bignumber.js) so commission calculations never rely on native JS float
|
|
22
|
+
* operators - only the final `toRoundedNumber()` call touches a plain number.
|
|
23
|
+
*/
|
|
24
|
+
class Money {
|
|
25
|
+
constructor(value) {
|
|
26
|
+
this.value = value;
|
|
27
|
+
}
|
|
28
|
+
static of(value) {
|
|
29
|
+
return value instanceof Money ? value : new Money(utils_1.MathBN.convert(value));
|
|
30
|
+
}
|
|
31
|
+
static zero() {
|
|
32
|
+
return Money.of(0);
|
|
33
|
+
}
|
|
34
|
+
static sum(values) {
|
|
35
|
+
return values.reduce((total, value) => total.add(value), Money.zero());
|
|
36
|
+
}
|
|
37
|
+
add(value) {
|
|
38
|
+
return new Money(utils_1.MathBN.add(this.value, Money.of(value).value));
|
|
39
|
+
}
|
|
40
|
+
subtract(value) {
|
|
41
|
+
return new Money(utils_1.MathBN.sub(this.value, Money.of(value).value));
|
|
42
|
+
}
|
|
43
|
+
multiply(value) {
|
|
44
|
+
return new Money(utils_1.MathBN.mult(this.value, Money.of(value).value));
|
|
45
|
+
}
|
|
46
|
+
divide(value) {
|
|
47
|
+
return new Money(utils_1.MathBN.div(this.value, Money.of(value).value));
|
|
48
|
+
}
|
|
49
|
+
/** `this * ratePercent / 100`, e.g. a base amount times a commission rate. */
|
|
50
|
+
applyPercentageRate(ratePercent) {
|
|
51
|
+
return this.multiply(ratePercent).divide(100);
|
|
52
|
+
}
|
|
53
|
+
negate() {
|
|
54
|
+
return new Money(utils_1.MathBN.mult(this.value, -1));
|
|
55
|
+
}
|
|
56
|
+
abs() {
|
|
57
|
+
return new Money(utils_1.MathBN.abs(this.value));
|
|
58
|
+
}
|
|
59
|
+
min(value) {
|
|
60
|
+
return new Money(utils_1.MathBN.min(this.value, Money.of(value).value));
|
|
61
|
+
}
|
|
62
|
+
max(value) {
|
|
63
|
+
return new Money(utils_1.MathBN.max(this.value, Money.of(value).value));
|
|
64
|
+
}
|
|
65
|
+
isNegative() {
|
|
66
|
+
return utils_1.MathBN.lt(this.value, 0);
|
|
67
|
+
}
|
|
68
|
+
isPositive() {
|
|
69
|
+
return utils_1.MathBN.gt(this.value, 0);
|
|
70
|
+
}
|
|
71
|
+
toRoundedNumber(decimalPlaces = DEFAULT_CURRENCY_DECIMAL_PLACES) {
|
|
72
|
+
// MathBN.convert's second argument is ignored when it is falsy, so a
|
|
73
|
+
// 0-decimal currency (e.g. JPY) would not be rounded. Round on the
|
|
74
|
+
// BigNumber directly instead, which honors 0.
|
|
75
|
+
return this.value.decimalPlaces(decimalPlaces).toNumber();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.Money = Money;
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZXkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvdXRpbHMvbW9uZXkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEscURBQXFFO0FBR3JFOzs7O0dBSUc7QUFDSCxNQUFNLCtCQUErQixHQUFHLENBQUMsQ0FBQTtBQUV6Qzs7OztHQUlHO0FBQ0ksTUFBTSxtQkFBbUIsR0FBRyxDQUFDLFlBQW9CLEVBQVUsRUFBRSxDQUNsRSx5QkFBaUIsQ0FBQyxZQUFZLENBQUMsV0FBVyxFQUFFLENBQUMsRUFBRSxjQUFjO0lBQzdELCtCQUErQixDQUFBO0FBRnBCLFFBQUEsbUJBQW1CLHVCQUVDO0FBSWpDOzs7O0dBSUc7QUFDSCxNQUFhLEtBQUs7SUFDaEIsWUFBcUMsS0FBa0I7UUFBbEIsVUFBSyxHQUFMLEtBQUssQ0FBYTtJQUFHLENBQUM7SUFFM0QsTUFBTSxDQUFDLEVBQUUsQ0FBQyxLQUE2QjtRQUNyQyxPQUFPLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsY0FBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFBO0lBQzFFLENBQUM7SUFFRCxNQUFNLENBQUMsSUFBSTtRQUNULE9BQU8sS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUNwQixDQUFDO0lBRUQsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFrQztRQUMzQyxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQVEsQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFBO0lBQy9FLENBQUM7SUFFRCxHQUFHLENBQUMsS0FBNkI7UUFDL0IsT0FBTyxJQUFJLEtBQUssQ0FBQyxjQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFL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
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertPromotionCodeAvailable = exports.promotionCodeTakenMessage = exports.findPromotionsWithCodeIgnoringCase = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
/**
|
|
6
|
+
* Medusa's unique index on `promotion.code` is case-sensitive, while every
|
|
7
|
+
* code a customer types is looked up after normalization (the storefront
|
|
8
|
+
* upper-cases input, the affiliate plugin canonicalizes to upper case). Two
|
|
9
|
+
* promotions whose codes differ only in case are therefore one code from the
|
|
10
|
+
* customer's point of view and must never coexist - regardless of whether
|
|
11
|
+
* the Admin dashboard or an affiliate coupon created them.
|
|
12
|
+
*/
|
|
13
|
+
const escapeLikePattern = (value) => value.replace(/[\\%_]/g, (character) => `\\${character}`);
|
|
14
|
+
const findPromotionsWithCodeIgnoringCase = async (promotionService, code, options = {}) => {
|
|
15
|
+
const trimmed = code.trim();
|
|
16
|
+
if (!trimmed)
|
|
17
|
+
return [];
|
|
18
|
+
const excluded = new Set(options.except_promotion_ids ?? []);
|
|
19
|
+
const promotions = (await promotionService.listPromotions({ code: { $ilike: escapeLikePattern(trimmed) } }, { select: ["id", "code"], take: 10 }));
|
|
20
|
+
return promotions.filter((promotion) => !excluded.has(promotion.id) &&
|
|
21
|
+
(promotion.code ?? "").trim().toLowerCase() === trimmed.toLowerCase());
|
|
22
|
+
};
|
|
23
|
+
exports.findPromotionsWithCodeIgnoringCase = findPromotionsWithCodeIgnoringCase;
|
|
24
|
+
const promotionCodeTakenMessage = (code, owner) => `Der Code „${code}“ wird bereits von einer Werbeaktion verwendet („${owner.code ?? owner.id}“). Gutschein- und Promotion-Codes müssen sich auch ohne Beachtung der Groß-/Kleinschreibung unterscheiden.`;
|
|
25
|
+
exports.promotionCodeTakenMessage = promotionCodeTakenMessage;
|
|
26
|
+
const assertPromotionCodeAvailable = async (container, code, options = {}) => {
|
|
27
|
+
const promotionService = container.resolve(utils_1.Modules.PROMOTION);
|
|
28
|
+
const [owner] = await (0, exports.findPromotionsWithCodeIgnoringCase)(promotionService, code, options);
|
|
29
|
+
if (owner) {
|
|
30
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.DUPLICATE_ERROR, (0, exports.promotionCodeTakenMessage)(code, owner));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
exports.assertPromotionCodeAvailable = assertPromotionCodeAvailable;
|
|
34
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvbW90aW9uLWNvZGUtYXZhaWxhYmlsaXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL3Byb21vdGlvbi1jb2RlLWF2YWlsYWJpbGl0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFJQSxxREFBZ0U7QUFTaEU7Ozs7Ozs7R0FPRztBQUNILE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxLQUFhLEVBQVUsRUFBRSxDQUNsRCxLQUFLLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQUMsS0FBSyxTQUFTLEVBQUUsQ0FBQyxDQUFBO0FBRXBELE1BQU0sa0NBQWtDLEdBQUcsS0FBSyxFQUNyRCxnQkFBcUMsRUFDckMsSUFBWSxFQUNaLFVBQXdELEVBQUUsRUFDM0IsRUFBRTtJQUNqQyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUE7SUFDM0IsSUFBSSxDQUFDLE9BQU87UUFBRSxPQUFPLEVBQUUsQ0FBQTtJQUN2QixNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsb0JBQW9CLElBQUksRUFBRSxDQUFDLENBQUE7SUFDNUQsTUFBTSxVQUFVLEdBQUcsQ0FBQyxNQUFNLGdCQUFnQixDQUFDLGNBQWMsQ0FDdkQsRUFBRSxJQUFJLEVBQUUsRUFBRSxNQUFNLEVBQUUsaUJBQWlCLENBQUMsT0FBTyxDQUFDLEVBQUUsRUFBRSxFQUNoRCxFQUFFLE1BQU0sRUFBRSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQ3JDLENBQXlCLENBQUE7SUFDMUIsT0FBTyxVQUFVLENBQUMsTUFBTSxDQUN0QixDQUFDLFNBQVMsRUFBRSxFQUFFLENBQ1osQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7UUFDM0IsQ0FBQyxTQUFTLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxLQUFLLE9BQU8sQ0FBQyxXQUFXLEVBQUUsQ0FDeEUsQ0FBQTtBQUNILENBQUMsQ0FBQTtBQWpCWSxRQUFBLGtDQUFrQyxzQ0FpQjlDO0FBRU0sTUFBTSx5QkFBeUIsR0FBRyxDQUN2QyxJQUFZLEVBQ1osS0FBeUIsRUFDakIsRUFBRSxDQUNWLGFBQWEsSUFBSSxvREFDZixLQUFLLENBQUMsSUFBSSxJQUFJLEtBQUssQ0FBQyxFQUN0Qiw2R0FBNkcsQ0FBQTtBQU5sRyxRQUFBLHlCQUF5Qiw2QkFNeUU7QUFFeEcsTUFBTSw0QkFBNEIsR0FBRyxLQUFLLEVBQy9DLFNBQTBCLEVBQzFCLElBQVksRUFDWixVQUF3RCxFQUFFLEVBQzNDLEVBQUU7SUFDakIsTUFBTSxnQkFBZ0IsR0FBRyxTQUFTLENBQUMsT0FBTyxDQUN4QyxlQUFPLENBQUMsU0FBUyxDQUNTLENBQUE7SUFDNUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLE1BQU0sSUFBQSwwQ0FBa0MsRUFDdEQsZ0JBQWdCLEVBQ2hCLElBQUksRUFDSixPQUFPLENBQ1IsQ0FBQTtJQUNELElBQUksS0FBSyxFQUFFLENBQUM7UUFDVixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsZUFBZSxFQUNqQyxJQUFBLGlDQUF5QixFQUFDLElBQUksRUFBRSxLQUFLLENBQUMsQ0FDdkMsQ0FBQTtJQUNILENBQUM7QUFDSCxDQUFDLENBQUE7QUFuQlksUUFBQSw0QkFBNEIsZ0NBbUJ4QyJ9
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireLoginForRegisteredCheckoutEmail = exports.requireLoginForRegisteredPaymentEmail = exports.requireLoginForRegisteredCartEmail = exports.CUSTOMER_LOGIN_REQUIRED_MESSAGE = void 0;
|
|
4
|
+
exports.isCheckoutBlockedForRegisteredEmail = isCheckoutBlockedForRegisteredEmail;
|
|
5
|
+
exports.escapeLikePattern = escapeLikePattern;
|
|
6
|
+
exports.assertEmailAllowedForActor = assertEmailAllowedForActor;
|
|
7
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
8
|
+
exports.CUSTOMER_LOGIN_REQUIRED_MESSAGE = "Für diese E-Mail-Adresse besteht bereits ein Kundenkonto. Bitte melde dich an, um die Bestellung abzuschließen.";
|
|
9
|
+
function isCheckoutBlockedForRegisteredEmail(input) {
|
|
10
|
+
return (input.registeredCustomerIds.length > 0 &&
|
|
11
|
+
!input.registeredCustomerIds.includes(input.actorId ?? ""));
|
|
12
|
+
}
|
|
13
|
+
// ILIKE gives case-insensitive matching (Medusa does not normalize stored
|
|
14
|
+
// emails), but treats `%` and `_` as wildcards - and `_` is common in real
|
|
15
|
+
// addresses. Unescaped, "a_b@x.de" would match "aXb@x.de".
|
|
16
|
+
function escapeLikePattern(value) {
|
|
17
|
+
return value.replace(/[\\%_]/g, "\\$&");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* An email that belongs to a registered account may only be used for a
|
|
21
|
+
* checkout by a request authenticated as that account. Medusa core would
|
|
22
|
+
* otherwise link an anonymous cart to whichever customer record shares the
|
|
23
|
+
* email - the account itself included - and file the order, plus any affiliate
|
|
24
|
+
* own-order discount or commission tied to it, under that identity with
|
|
25
|
+
* nothing but the typed-in address as proof.
|
|
26
|
+
*
|
|
27
|
+
* The request's auth context is the only usable signal: cart.customer_id is
|
|
28
|
+
* set by that same core linking and so never proves a login. The storefront
|
|
29
|
+
* establishes a Medusa browser session at login precisely so that its
|
|
30
|
+
* browser-side cart and payment calls carry this identity.
|
|
31
|
+
*/
|
|
32
|
+
async function assertEmailAllowedForActor(container, email, actorId) {
|
|
33
|
+
// Same deliberately provider-agnostic normalization as normalizeAffiliateEmail.
|
|
34
|
+
const normalizedEmail = email?.trim().toLowerCase();
|
|
35
|
+
if (!normalizedEmail) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const customerService = container.resolve(utils_1.Modules.CUSTOMER);
|
|
39
|
+
const registeredCustomers = await customerService.listCustomers({ email: { $ilike: escapeLikePattern(normalizedEmail) }, has_account: true }, { select: ["id"] });
|
|
40
|
+
if (isCheckoutBlockedForRegisteredEmail({
|
|
41
|
+
registeredCustomerIds: registeredCustomers.map(({ id }) => id),
|
|
42
|
+
actorId,
|
|
43
|
+
})) {
|
|
44
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, exports.CUSTOMER_LOGIN_REQUIRED_MESSAGE);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function loadCartEmail(container, cartId) {
|
|
48
|
+
const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
49
|
+
const { data } = await query.graph({
|
|
50
|
+
entity: "cart",
|
|
51
|
+
fields: ["id", "email"],
|
|
52
|
+
filters: { id: cartId },
|
|
53
|
+
});
|
|
54
|
+
return data[0]?.email;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* First gate, on setting the email itself (cart create and update): refusing
|
|
58
|
+
* here keeps a cart with someone else's account email from ever existing, and
|
|
59
|
+
* puts the login prompt at the address step - before any payment details.
|
|
60
|
+
* Runs ahead of the core body validation, so the raw body is read defensively.
|
|
61
|
+
*/
|
|
62
|
+
const requireLoginForRegisteredCartEmail = async (req, _res, next) => {
|
|
63
|
+
const email = req.body?.email;
|
|
64
|
+
if (typeof email === "string") {
|
|
65
|
+
await assertEmailAllowedForActor(req.scope, email, req.auth_context?.actor_id);
|
|
66
|
+
}
|
|
67
|
+
next();
|
|
68
|
+
};
|
|
69
|
+
exports.requireLoginForRegisteredCartEmail = requireLoginForRegisteredCartEmail;
|
|
70
|
+
/**
|
|
71
|
+
* Second gate. Creating a payment session is what hands the browser a Stripe
|
|
72
|
+
* client secret, and Stripe charges on confirmPayment - before completeCart
|
|
73
|
+
* runs at all. Covers carts that got their email past the first gate (set
|
|
74
|
+
* before it existed, or by a server-side backfill).
|
|
75
|
+
*/
|
|
76
|
+
const requireLoginForRegisteredPaymentEmail = async (req, _res, next) => {
|
|
77
|
+
const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
78
|
+
const { data } = await query.graph({
|
|
79
|
+
entity: "payment_collection",
|
|
80
|
+
fields: ["id", "cart.email"],
|
|
81
|
+
filters: { id: req.params.id },
|
|
82
|
+
});
|
|
83
|
+
const cart = data[0]?.cart;
|
|
84
|
+
if (cart) {
|
|
85
|
+
await assertEmailAllowedForActor(req.scope, cart.email, req.auth_context?.actor_id);
|
|
86
|
+
}
|
|
87
|
+
next();
|
|
88
|
+
};
|
|
89
|
+
exports.requireLoginForRegisteredPaymentEmail = requireLoginForRegisteredPaymentEmail;
|
|
90
|
+
/**
|
|
91
|
+
* Backstop on completion, for a cart whose payment session was created before
|
|
92
|
+
* the gates existed. By this point a payment may already be authorized; the
|
|
93
|
+
* two gates above exist so that this one is never the first to refuse.
|
|
94
|
+
*/
|
|
95
|
+
const requireLoginForRegisteredCheckoutEmail = async (req, _res, next) => {
|
|
96
|
+
const email = await loadCartEmail(req.scope, req.params.id);
|
|
97
|
+
await assertEmailAllowedForActor(req.scope, email, req.auth_context?.actor_id);
|
|
98
|
+
next();
|
|
99
|
+
};
|
|
100
|
+
exports.requireLoginForRegisteredCheckoutEmail = requireLoginForRegisteredCheckoutEmail;
|
|
101
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnaXN0ZXJlZC1lbWFpbC1jaGVja291dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy91dGlscy9yZWdpc3RlcmVkLWVtYWlsLWNoZWNrb3V0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQXNCQSxrRkFRQztBQUtELDhDQUVDO0FBZUQsZ0VBK0JDO0FBMUVELHFEQUlrQztBQUVyQixRQUFBLCtCQUErQixHQUMxQyxpSEFBaUgsQ0FBQTtBQU1uSCxTQUFnQixtQ0FBbUMsQ0FBQyxLQUduRDtJQUNDLE9BQU8sQ0FDTCxLQUFLLENBQUMscUJBQXFCLENBQUMsTUFBTSxHQUFHLENBQUM7UUFDdEMsQ0FBQyxLQUFLLENBQUMscUJBQXFCLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDLENBQzNELENBQUE7QUFDSCxDQUFDO0FBRUQsMEVBQTBFO0FBQzFFLDJFQUEyRTtBQUMzRSwyREFBMkQ7QUFDM0QsU0FBZ0IsaUJBQWlCLENBQUMsS0FBYTtJQUM3QyxPQUFPLEtBQUssQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFBO0FBQ3pDLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSSxLQUFLLFVBQVUsMEJBQTBCLENBQzlDLFNBQTBCLEVBQzFCLEtBQWdDLEVBQ2hDLE9BQWtDO0lBRWxDLGdGQUFnRjtJQUNoRixNQUFNLGVBQWUsR0FBRyxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUE7SUFFbkQsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ3JCLE9BQU07SUFDUixDQUFDO0lBRUQsTUFBTSxlQUFlLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FDdkMsZUFBTyxDQUFDLFFBQVEsQ0FDUyxDQUFBO0lBQzNCLE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxlQUFlLENBQUMsYUFBYSxDQUM3RCxFQUFFLEtBQUssRUFBRSxFQUFFLE1BQU0sRUFB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
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveAffiliateService = void 0;
|
|
4
|
+
const affiliate_1 = require("../modules/affiliate");
|
|
5
|
+
const resolveAffiliateService = (container) => container.resolve(affiliate_1.AFFILIATE_MODULE);
|
|
6
|
+
exports.resolveAffiliateService = resolveAffiliateService;
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzb2x2ZS1zZXJ2aWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy91dGlscy9yZXNvbHZlLXNlcnZpY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLG9EQUF1RDtBQUdoRCxNQUFNLHVCQUF1QixHQUFHLENBQ3JDLFNBQTBCLEVBQ0YsRUFBRSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsNEJBQWdCLENBQUMsQ0FBQTtBQUZuRCxRQUFBLHVCQUF1QiwyQkFFNEIifQ==
|