@shoppexio/storefront 1.0.78 → 1.0.79
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/CHANGELOG.md +9 -0
- package/dist/index.cjs +52 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +52 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.79
|
|
4
|
+
|
|
5
|
+
- Attribution is now decided by the policy of the code that EARNED the hold, never by the one that just arrived. `captureAffiliateFromUrl()` sends the held code so the server can report its mode, and a first-click hold survives an affiliate on a last-click tier landing afterwards.
|
|
6
|
+
- `applyAffiliateCode()` applies the same rules as a `?ref=` landing. It stores the code with the shop's (or tier's) real mode and window instead of a fixed 30-day last-click entry, and it no longer overrides a standing first-click hold — a themed "apply code" field was previously a way around the rule.
|
|
7
|
+
- `applyAffiliateCode()` reports what is ATTRIBUTED: `data.affiliate_code` is the winning code and the new `data.applied` says whether that is the one you passed. Passing `data.affiliate_code` to `checkout()` therefore stays correct in both cases.
|
|
8
|
+
- `validateAffiliateCode()` passes the resolved `attribution_mode`, `attribution_window_days` and `held_attribution_mode` through instead of dropping them.
|
|
9
|
+
- `getStoredAffiliate()` also returns `expiresAt`, so a hold can be restored without extending the window it earned.
|
|
10
|
+
- A transport failure or an HTTP error such as a rate limit no longer returns the arriving code while storage still holds another: both paths answer with the code that keeps the attribution.
|
|
11
|
+
|
|
3
12
|
## 1.0.78
|
|
4
13
|
|
|
5
14
|
- `captureAffiliateFromUrl()` honours the shop's attribution mode (ADR-0067). Under `FIRST_CLICK` a held, unexpired attribution wins and a newer `?ref=` no longer replaces it; the landing is still reported so click statistics stay complete. `LAST_CLICK` is unchanged and remains the default.
|
package/dist/index.cjs
CHANGED
|
@@ -19317,6 +19317,9 @@ function safeWrite(value) {
|
|
|
19317
19317
|
} catch {
|
|
19318
19318
|
}
|
|
19319
19319
|
}
|
|
19320
|
+
function restoreStoredAffiliate(value) {
|
|
19321
|
+
safeWrite(value);
|
|
19322
|
+
}
|
|
19320
19323
|
function setAffiliateCode(code, ttlDays = DEFAULT_TTL_DAYS, mode) {
|
|
19321
19324
|
const normalized = normalizeAffiliateCode(code);
|
|
19322
19325
|
if (!normalized) {
|
|
@@ -19341,7 +19344,7 @@ function getStoredAffiliate() {
|
|
|
19341
19344
|
const code = normalizeAffiliateCode(stored.code);
|
|
19342
19345
|
if (!code) return null;
|
|
19343
19346
|
const windowDays = typeof stored.windowDays === "number" && stored.windowDays > 0 ? stored.windowDays : DEFAULT_TTL_DAYS;
|
|
19344
|
-
return { code, mode: normalizeMode(stored.mode), windowDays };
|
|
19347
|
+
return { code, mode: normalizeMode(stored.mode), windowDays, expiresAt: stored.expiresAt };
|
|
19345
19348
|
}
|
|
19346
19349
|
function clearAffiliateCode() {
|
|
19347
19350
|
if (typeof window === "undefined") return;
|
|
@@ -19432,11 +19435,13 @@ async function validateAffiliateCode(code) {
|
|
|
19432
19435
|
};
|
|
19433
19436
|
}
|
|
19434
19437
|
const config2 = getConfig();
|
|
19438
|
+
const held = getStoredAffiliate();
|
|
19435
19439
|
const response = await post(
|
|
19436
19440
|
"/v1/storefront/affiliates/resolve",
|
|
19437
19441
|
{
|
|
19438
19442
|
shop_slug: config2.storeSlug,
|
|
19439
|
-
code: normalizedCode
|
|
19443
|
+
code: normalizedCode,
|
|
19444
|
+
...held && held.code !== normalizedCode ? { held_code: held.code } : {}
|
|
19440
19445
|
},
|
|
19441
19446
|
{ retries: 0 }
|
|
19442
19447
|
);
|
|
@@ -19464,15 +19469,36 @@ async function validateAffiliateCode(code) {
|
|
|
19464
19469
|
...response.data.program_enabled !== void 0 ? { program_enabled: response.data.program_enabled } : {},
|
|
19465
19470
|
affiliate_code: normalizeAffiliateCode(response.data.affiliate_code),
|
|
19466
19471
|
discount_active: Boolean(response.data.discount_active),
|
|
19467
|
-
discount_percent: Number(response.data.discount_percent ?? 0)
|
|
19472
|
+
discount_percent: Number(response.data.discount_percent ?? 0),
|
|
19473
|
+
// Passed through rather than dropped: a caller that stores the code has
|
|
19474
|
+
// no other way to learn the policy, and defaulting to 30-day last-click
|
|
19475
|
+
// would quietly discard a first-click tier and any custom window.
|
|
19476
|
+
attribution_mode: normalizeMode(response.data.attribution_mode),
|
|
19477
|
+
attribution_window_days: typeof response.data.attribution_window_days === "number" && response.data.attribution_window_days >= 1 ? Math.trunc(response.data.attribution_window_days) : DEFAULT_TTL_DAYS,
|
|
19478
|
+
held_attribution_mode: response.data.held_attribution_mode === "FIRST_CLICK" || response.data.held_attribution_mode === "LAST_CLICK" ? response.data.held_attribution_mode : null
|
|
19468
19479
|
},
|
|
19469
19480
|
...response.message ? { message: response.message } : {}
|
|
19470
19481
|
};
|
|
19471
19482
|
}
|
|
19472
19483
|
async function applyAffiliateCode(code) {
|
|
19484
|
+
const held = getStoredAffiliate();
|
|
19473
19485
|
const result = await validateAffiliateCode(code);
|
|
19474
19486
|
if (result.success && result.data?.affiliate_code) {
|
|
19475
|
-
|
|
19487
|
+
const applied = result.data.affiliate_code;
|
|
19488
|
+
const holdsOtherCode = held !== null && held.code !== applied;
|
|
19489
|
+
const heldMode = holdsOtherCode ? result.data.held_attribution_mode ?? held.mode : null;
|
|
19490
|
+
if (heldMode === "FIRST_CLICK" && held) {
|
|
19491
|
+
return {
|
|
19492
|
+
...result,
|
|
19493
|
+
data: { ...result.data, affiliate_code: held.code, applied: false }
|
|
19494
|
+
};
|
|
19495
|
+
}
|
|
19496
|
+
setAffiliateCode(
|
|
19497
|
+
applied,
|
|
19498
|
+
result.data.attribution_window_days ?? DEFAULT_TTL_DAYS,
|
|
19499
|
+
result.data.attribution_mode
|
|
19500
|
+
);
|
|
19501
|
+
return { ...result, data: { ...result.data, applied: true } };
|
|
19476
19502
|
}
|
|
19477
19503
|
return result;
|
|
19478
19504
|
}
|
|
@@ -19489,7 +19515,8 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
19489
19515
|
code = normalizeAffiliateCode(code);
|
|
19490
19516
|
if (!code) return null;
|
|
19491
19517
|
const held = getStoredAffiliate();
|
|
19492
|
-
const
|
|
19518
|
+
const holdsOtherCode = held !== null && held.code !== code;
|
|
19519
|
+
const mayHoldFirstClick = holdsOtherCode && held.mode === "FIRST_CLICK";
|
|
19493
19520
|
if (!mayHoldFirstClick) {
|
|
19494
19521
|
setAffiliateCode(code, held?.windowDays ?? DEFAULT_TTL_DAYS, held?.mode);
|
|
19495
19522
|
}
|
|
@@ -19498,13 +19525,29 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
19498
19525
|
const config2 = getConfig();
|
|
19499
19526
|
const res = await post(
|
|
19500
19527
|
"/v1/storefront/affiliates/attribution",
|
|
19501
|
-
{
|
|
19528
|
+
{
|
|
19529
|
+
shop_slug: config2.storeSlug,
|
|
19530
|
+
code,
|
|
19531
|
+
// Sent so the server can report the policy of the attribution we
|
|
19532
|
+
// already hold. Its mode — not the arriving code's — decides whether
|
|
19533
|
+
// that hold may be released.
|
|
19534
|
+
...held ? { held_code: held.code } : {}
|
|
19535
|
+
},
|
|
19502
19536
|
{ retries: 0 }
|
|
19503
19537
|
);
|
|
19504
19538
|
const serverMode = res.success && res.data?.attribution_mode ? normalizeMode(res.data.attribution_mode) : null;
|
|
19505
19539
|
const serverWindow = res.success && typeof res.data?.attribution_window_days === "number" ? res.data.attribution_window_days : DEFAULT_TTL_DAYS;
|
|
19506
|
-
const
|
|
19507
|
-
|
|
19540
|
+
const heldMode = res.success && res.data?.held_attribution_mode ? normalizeMode(res.data.held_attribution_mode) : null;
|
|
19541
|
+
const holdsFirstClick = holdsOtherCode && (heldMode ?? held?.mode) === "FIRST_CLICK";
|
|
19542
|
+
if (holdsFirstClick && held) {
|
|
19543
|
+
if (!mayHoldFirstClick) {
|
|
19544
|
+
restoreStoredAffiliate({
|
|
19545
|
+
code: held.code,
|
|
19546
|
+
expiresAt: held.expiresAt,
|
|
19547
|
+
windowDays: held.windowDays,
|
|
19548
|
+
mode: heldMode ?? "FIRST_CLICK"
|
|
19549
|
+
});
|
|
19550
|
+
}
|
|
19508
19551
|
return held.code;
|
|
19509
19552
|
}
|
|
19510
19553
|
if (res.success && res.data?.accepted && res.data.affiliate_code) {
|
|
@@ -19515,7 +19558,7 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
19515
19558
|
clearAffiliateCode();
|
|
19516
19559
|
return null;
|
|
19517
19560
|
}
|
|
19518
|
-
return code;
|
|
19561
|
+
return mayHoldFirstClick ? held.code : code;
|
|
19519
19562
|
} catch {
|
|
19520
19563
|
return mayHoldFirstClick ? held.code : code;
|
|
19521
19564
|
}
|