@crediball/elements 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-MWB56UEW.js → chunk-YCLIZG7D.js} +120 -6
- package/dist/crediball-elements.iife.js +123 -6
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1 -1
- package/dist/register.js +1 -1
- package/package.json +2 -2
|
@@ -187,6 +187,7 @@ var CrediballLowCreditWarningElement = class extends CrediballElement {
|
|
|
187
187
|
// src/paywall.ts
|
|
188
188
|
import { formatMoney, fetchReferral } from "@crediball/core";
|
|
189
189
|
var CURRENCY_SYMBOLS = { EUR: "\u20AC", USD: "$", GBP: "\xA3" };
|
|
190
|
+
var PERIOD_SUFFIX = { weekly: "wk", monthly: "mo", yearly: "yr" };
|
|
190
191
|
function currencySymbolFor(currency) {
|
|
191
192
|
return CURRENCY_SYMBOLS[currency.toUpperCase()] ?? currency;
|
|
192
193
|
}
|
|
@@ -269,6 +270,7 @@ var STYLE = `
|
|
|
269
270
|
padding: 10px 18px;
|
|
270
271
|
border-radius: var(--crediball-radius, 9999px);
|
|
271
272
|
}
|
|
273
|
+
p.custom-preview { margin: 6px 0 0; font-size: 12px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
272
274
|
p.error { margin: 8px 0 0; font-size: 13px; color: var(--crediball-accent, #0066cc); }
|
|
273
275
|
button.close {
|
|
274
276
|
width: 100%;
|
|
@@ -283,8 +285,29 @@ var STYLE = `
|
|
|
283
285
|
padding: 11px 22px;
|
|
284
286
|
border-radius: var(--crediball-radius, 9999px);
|
|
285
287
|
}
|
|
286
|
-
.
|
|
287
|
-
.
|
|
288
|
+
.autotopup { margin-top: 16px; }
|
|
289
|
+
.autotopup-toggle {
|
|
290
|
+
appearance: none; border: none; cursor: pointer; background: transparent; padding: 0;
|
|
291
|
+
font: inherit; font-size: 12px; color: var(--crediball-ink-muted, #7a7a7a); text-decoration: underline;
|
|
292
|
+
}
|
|
293
|
+
.autotopup-status { margin: 0; font-size: 12px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
294
|
+
.autotopup-warning { margin: 0; font-size: 12px; color: var(--crediball-accent, #0066cc); }
|
|
295
|
+
.autotopup-row { display: flex; gap: 8px; align-items: center; }
|
|
296
|
+
.autotopup-row select, .autotopup-row input {
|
|
297
|
+
font: inherit; font-size: 13px; padding: 6px 10px;
|
|
298
|
+
border-radius: var(--crediball-radius, 9999px); border: 1px solid var(--crediball-hairline, #e0e0e0);
|
|
299
|
+
background: var(--crediball-canvas, #ffffff); color: var(--crediball-ink, #1d1d1f);
|
|
300
|
+
}
|
|
301
|
+
.autotopup-row select { flex: 1; min-width: 0; }
|
|
302
|
+
.autotopup-row input { width: 72px; }
|
|
303
|
+
button.autotopup-submit {
|
|
304
|
+
appearance: none; border: none; cursor: pointer; flex: none;
|
|
305
|
+
background: var(--crediball-accent, #0066cc); color: var(--crediball-on-accent, #ffffff);
|
|
306
|
+
font: inherit; font-size: 13px; padding: 6px 14px; border-radius: var(--crediball-radius, 9999px);
|
|
307
|
+
}
|
|
308
|
+
.autotopup-hint { margin: 6px 0 0; font-size: 11px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
309
|
+
.referral { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--crediball-hairline, #e0e0e0); }
|
|
310
|
+
.referral-label { margin-bottom: 8px; font-size: 12px; font-weight: 600; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
288
311
|
.referral-row { display: flex; gap: 8px; align-items: center; }
|
|
289
312
|
.referral-row .link {
|
|
290
313
|
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
@@ -304,6 +327,7 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
304
327
|
super();
|
|
305
328
|
this.referral = null;
|
|
306
329
|
this.referralCopied = false;
|
|
330
|
+
this.autoTopupOpen = false;
|
|
307
331
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
308
332
|
}
|
|
309
333
|
static get observedAttributes() {
|
|
@@ -377,7 +401,7 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
377
401
|
(p) => `
|
|
378
402
|
<button class="option" data-kind="plan" data-id="${escapeHtml(p.id)}">
|
|
379
403
|
<span>${escapeHtml(p.name)}</span>
|
|
380
|
-
<span class="meta">${formatMoney(p.price, currency)}/${p.period
|
|
404
|
+
<span class="meta">${formatMoney(p.price, currency)}/${PERIOD_SUFFIX[p.period] ?? p.period} \xB7 ${formatCredits(p.credits)} cr</span>
|
|
381
405
|
</button>`
|
|
382
406
|
).join("")}
|
|
383
407
|
</div>` : ""}
|
|
@@ -399,16 +423,23 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
399
423
|
<span class="sym">${escapeHtml(currencySymbolFor(currency))}</span>
|
|
400
424
|
<input class="custom-input" inputmode="decimal" placeholder="${custom?.minEur ?? ""}" aria-label="Custom amount" />
|
|
401
425
|
<button class="custom-add" type="button">${escapeHtml(addButtonText)}</button>
|
|
402
|
-
</div
|
|
426
|
+
</div>
|
|
427
|
+
<p class="custom-preview" hidden></p>` : ""}
|
|
428
|
+
${this.renderAutoTopup(packages, pkgs?.autoTopup, currency)}
|
|
403
429
|
<p class="error" hidden></p>
|
|
430
|
+
<button class="close" part="close" type="button">Not now</button>
|
|
404
431
|
${this.referral?.link ? `<div class="referral">
|
|
405
|
-
<div class="
|
|
432
|
+
<div class="referral-label">${escapeHtml(
|
|
433
|
+
(this.getAttribute("referral-label") ?? "Refer a friend \xB7 earn {credits} credits").replace(
|
|
434
|
+
"{credits}",
|
|
435
|
+
formatCredits(this.referral.rewardCredits ?? 0)
|
|
436
|
+
)
|
|
437
|
+
)}</div>
|
|
406
438
|
<div class="referral-row">
|
|
407
439
|
<div class="link" title="${escapeHtml(this.referral.link)}">${escapeHtml(this.referral.link)}</div>
|
|
408
440
|
<button class="referral-copy" type="button">${this.referralCopied ? "Copied!" : "Copy"}</button>
|
|
409
441
|
</div>
|
|
410
442
|
</div>` : ""}
|
|
411
|
-
<button class="close" part="close" type="button">Not now</button>
|
|
412
443
|
</div>
|
|
413
444
|
</div>
|
|
414
445
|
`;
|
|
@@ -443,6 +474,18 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
443
474
|
const input = this.shadow.querySelector(".custom-input");
|
|
444
475
|
const addBtn = this.shadow.querySelector(".custom-add");
|
|
445
476
|
const errEl = this.shadow.querySelector(".error");
|
|
477
|
+
const previewEl = this.shadow.querySelector(".custom-preview");
|
|
478
|
+
const rate = custom?.rate;
|
|
479
|
+
input?.addEventListener("input", () => {
|
|
480
|
+
if (!previewEl) return;
|
|
481
|
+
const amount = Number(input.value);
|
|
482
|
+
if (!rate || rate <= 0 || !Number.isFinite(amount) || amount <= 0) {
|
|
483
|
+
previewEl.hidden = true;
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
previewEl.hidden = false;
|
|
487
|
+
previewEl.textContent = `\u2248 ${formatCredits(Math.round(amount * rate))} credits`;
|
|
488
|
+
});
|
|
446
489
|
const submit = () => {
|
|
447
490
|
const amount = Number(input?.value);
|
|
448
491
|
const sym = currencySymbolFor(currency);
|
|
@@ -465,6 +508,66 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
465
508
|
if (e.key === "Enter") submit();
|
|
466
509
|
});
|
|
467
510
|
}
|
|
511
|
+
this.shadow.querySelector("[data-open-autotopup]")?.addEventListener("click", () => {
|
|
512
|
+
this.autoTopupOpen = true;
|
|
513
|
+
this.render();
|
|
514
|
+
});
|
|
515
|
+
this.shadow.querySelector(".autotopup-submit")?.addEventListener("click", () => {
|
|
516
|
+
const packageId = this.shadow.querySelector(".autotopup-package")?.value;
|
|
517
|
+
const thresholdCredits = Number(
|
|
518
|
+
this.shadow.querySelector(".autotopup-threshold")?.value
|
|
519
|
+
);
|
|
520
|
+
if (!packageId || !Number.isFinite(thresholdCredits) || thresholdCredits < 0) return;
|
|
521
|
+
if (this.emit("crediball-enable-autotopup", { packageId, thresholdCredits })) {
|
|
522
|
+
void this.startAutoTopupCheckout(packageId, thresholdCredits);
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
const fixBtn = this.shadow.querySelector("[data-fix-package]");
|
|
526
|
+
fixBtn?.addEventListener("click", () => {
|
|
527
|
+
const packageId = fixBtn.dataset.fixPackage;
|
|
528
|
+
const thresholdCredits = Number(fixBtn.dataset.fixThreshold);
|
|
529
|
+
if (!packageId || !Number.isFinite(thresholdCredits)) return;
|
|
530
|
+
if (this.emit("crediball-enable-autotopup", { packageId, thresholdCredits })) {
|
|
531
|
+
void this.startAutoTopupCheckout(packageId, thresholdCredits);
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
/** HTML for the auto-topup opt-in row / status line / needs_auth-fix prompt. */
|
|
536
|
+
renderAutoTopup(packages, autoTopup, currency) {
|
|
537
|
+
if (!packages.length) return "";
|
|
538
|
+
if (autoTopup?.enabled && autoTopup.status === "active") {
|
|
539
|
+
const pkg = packages.find((p) => p.id === autoTopup.packageId);
|
|
540
|
+
return `<div class="autotopup"><p class="autotopup-status">Auto top-up is on \u2014 buying ${escapeHtml(
|
|
541
|
+
pkg?.name ?? "your package"
|
|
542
|
+
)} whenever your balance drops below ${formatCredits(autoTopup.thresholdCredits)} credits.</p></div>`;
|
|
543
|
+
}
|
|
544
|
+
if (autoTopup?.enabled && (autoTopup.status === "needs_auth" || autoTopup.status === "failed")) {
|
|
545
|
+
const message = autoTopup.status === "needs_auth" ? "Auto top-up needs you to re-confirm your payment method." : "Auto top-up's last charge failed.";
|
|
546
|
+
return `<div class="autotopup">
|
|
547
|
+
<p class="autotopup-warning">${escapeHtml(message)}</p>
|
|
548
|
+
<button class="autotopup-toggle" type="button" data-fix-package="${escapeHtml(
|
|
549
|
+
autoTopup.packageId
|
|
550
|
+
)}" data-fix-threshold="${autoTopup.thresholdCredits}">Fix auto top-up</button>
|
|
551
|
+
</div>`;
|
|
552
|
+
}
|
|
553
|
+
if (this.autoTopupOpen) {
|
|
554
|
+
const cheapest = packages.reduce((min, p) => p.price < min.price ? p : min, packages[0]);
|
|
555
|
+
return `<div class="autotopup">
|
|
556
|
+
<div class="autotopup-row">
|
|
557
|
+
<select class="autotopup-package" aria-label="Package">
|
|
558
|
+
${packages.map(
|
|
559
|
+
(p) => `<option value="${escapeHtml(p.id)}">${escapeHtml(p.name)} \xB7 ${formatMoney(p.price, currency)}</option>`
|
|
560
|
+
).join("")}
|
|
561
|
+
</select>
|
|
562
|
+
<input class="autotopup-threshold" inputmode="numeric" aria-label="Threshold credits" value="${cheapest.credits}" />
|
|
563
|
+
<button class="autotopup-submit" type="button">Turn on</button>
|
|
564
|
+
</div>
|
|
565
|
+
<p class="autotopup-hint">Buy that package automatically whenever your balance drops below the credit amount above.</p>
|
|
566
|
+
</div>`;
|
|
567
|
+
}
|
|
568
|
+
return `<div class="autotopup">
|
|
569
|
+
<button class="autotopup-toggle" type="button" data-open-autotopup>Keep me topped up automatically</button>
|
|
570
|
+
</div>`;
|
|
468
571
|
}
|
|
469
572
|
/** Copy the invite link to the clipboard, with a 2s "Copied!" confirmation. */
|
|
470
573
|
async copyReferral() {
|
|
@@ -499,6 +602,17 @@ var CrediballPaywallElement = class extends CrediballElement {
|
|
|
499
602
|
this.client.createSubscriptionCheckout({ planId, returnPath: this.returnPath() })
|
|
500
603
|
);
|
|
501
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* Start the built-in auto-topup setup Checkout and redirect. No money changes
|
|
607
|
+
* hands here — it only saves a payment method Crediball charges later,
|
|
608
|
+
* off-session, whenever the balance drops below `thresholdCredits`.
|
|
609
|
+
*/
|
|
610
|
+
startAutoTopupCheckout(packageId, thresholdCredits) {
|
|
611
|
+
if (!this.client) return Promise.resolve();
|
|
612
|
+
return this.redirect(
|
|
613
|
+
this.client.createAutoTopupCheckout({ packageId, thresholdCredits, returnPath: this.returnPath() })
|
|
614
|
+
);
|
|
615
|
+
}
|
|
502
616
|
async redirect(session) {
|
|
503
617
|
try {
|
|
504
618
|
const { url } = await session;
|
|
@@ -133,6 +133,9 @@ var Crediball = (() => {
|
|
|
133
133
|
this.createSubscriptionCheckout = (input) => {
|
|
134
134
|
return this.postCheckout("/public/subscriptions/checkout", input);
|
|
135
135
|
};
|
|
136
|
+
this.createAutoTopupCheckout = (input) => {
|
|
137
|
+
return this.postCheckout("/public/autotopup/setup", input);
|
|
138
|
+
};
|
|
136
139
|
this.refresh = async () => {
|
|
137
140
|
const { publishableKey, userId } = this.config;
|
|
138
141
|
const headers = { Authorization: `Bearer ${publishableKey}` };
|
|
@@ -598,6 +601,7 @@ var Crediball = (() => {
|
|
|
598
601
|
|
|
599
602
|
// src/paywall.ts
|
|
600
603
|
var CURRENCY_SYMBOLS = { EUR: "\u20AC", USD: "$", GBP: "\xA3" };
|
|
604
|
+
var PERIOD_SUFFIX = { weekly: "wk", monthly: "mo", yearly: "yr" };
|
|
601
605
|
function currencySymbolFor(currency) {
|
|
602
606
|
return CURRENCY_SYMBOLS[currency.toUpperCase()] ?? currency;
|
|
603
607
|
}
|
|
@@ -680,6 +684,7 @@ var Crediball = (() => {
|
|
|
680
684
|
padding: 10px 18px;
|
|
681
685
|
border-radius: var(--crediball-radius, 9999px);
|
|
682
686
|
}
|
|
687
|
+
p.custom-preview { margin: 6px 0 0; font-size: 12px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
683
688
|
p.error { margin: 8px 0 0; font-size: 13px; color: var(--crediball-accent, #0066cc); }
|
|
684
689
|
button.close {
|
|
685
690
|
width: 100%;
|
|
@@ -694,8 +699,29 @@ var Crediball = (() => {
|
|
|
694
699
|
padding: 11px 22px;
|
|
695
700
|
border-radius: var(--crediball-radius, 9999px);
|
|
696
701
|
}
|
|
697
|
-
.
|
|
698
|
-
.
|
|
702
|
+
.autotopup { margin-top: 16px; }
|
|
703
|
+
.autotopup-toggle {
|
|
704
|
+
appearance: none; border: none; cursor: pointer; background: transparent; padding: 0;
|
|
705
|
+
font: inherit; font-size: 12px; color: var(--crediball-ink-muted, #7a7a7a); text-decoration: underline;
|
|
706
|
+
}
|
|
707
|
+
.autotopup-status { margin: 0; font-size: 12px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
708
|
+
.autotopup-warning { margin: 0; font-size: 12px; color: var(--crediball-accent, #0066cc); }
|
|
709
|
+
.autotopup-row { display: flex; gap: 8px; align-items: center; }
|
|
710
|
+
.autotopup-row select, .autotopup-row input {
|
|
711
|
+
font: inherit; font-size: 13px; padding: 6px 10px;
|
|
712
|
+
border-radius: var(--crediball-radius, 9999px); border: 1px solid var(--crediball-hairline, #e0e0e0);
|
|
713
|
+
background: var(--crediball-canvas, #ffffff); color: var(--crediball-ink, #1d1d1f);
|
|
714
|
+
}
|
|
715
|
+
.autotopup-row select { flex: 1; min-width: 0; }
|
|
716
|
+
.autotopup-row input { width: 72px; }
|
|
717
|
+
button.autotopup-submit {
|
|
718
|
+
appearance: none; border: none; cursor: pointer; flex: none;
|
|
719
|
+
background: var(--crediball-accent, #0066cc); color: var(--crediball-on-accent, #ffffff);
|
|
720
|
+
font: inherit; font-size: 13px; padding: 6px 14px; border-radius: var(--crediball-radius, 9999px);
|
|
721
|
+
}
|
|
722
|
+
.autotopup-hint { margin: 6px 0 0; font-size: 11px; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
723
|
+
.referral { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--crediball-hairline, #e0e0e0); }
|
|
724
|
+
.referral-label { margin-bottom: 8px; font-size: 12px; font-weight: 600; color: var(--crediball-ink-muted, #7a7a7a); }
|
|
699
725
|
.referral-row { display: flex; gap: 8px; align-items: center; }
|
|
700
726
|
.referral-row .link {
|
|
701
727
|
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
@@ -715,6 +741,7 @@ var Crediball = (() => {
|
|
|
715
741
|
super();
|
|
716
742
|
this.referral = null;
|
|
717
743
|
this.referralCopied = false;
|
|
744
|
+
this.autoTopupOpen = false;
|
|
718
745
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
719
746
|
}
|
|
720
747
|
static get observedAttributes() {
|
|
@@ -788,7 +815,7 @@ var Crediball = (() => {
|
|
|
788
815
|
(p) => `
|
|
789
816
|
<button class="option" data-kind="plan" data-id="${escapeHtml(p.id)}">
|
|
790
817
|
<span>${escapeHtml(p.name)}</span>
|
|
791
|
-
<span class="meta">${formatMoney(p.price, currency)}/${p.period
|
|
818
|
+
<span class="meta">${formatMoney(p.price, currency)}/${PERIOD_SUFFIX[p.period] ?? p.period} \xB7 ${formatCredits(p.credits)} cr</span>
|
|
792
819
|
</button>`
|
|
793
820
|
).join("")}
|
|
794
821
|
</div>` : ""}
|
|
@@ -810,16 +837,23 @@ var Crediball = (() => {
|
|
|
810
837
|
<span class="sym">${escapeHtml(currencySymbolFor(currency))}</span>
|
|
811
838
|
<input class="custom-input" inputmode="decimal" placeholder="${custom?.minEur ?? ""}" aria-label="Custom amount" />
|
|
812
839
|
<button class="custom-add" type="button">${escapeHtml(addButtonText)}</button>
|
|
813
|
-
</div
|
|
840
|
+
</div>
|
|
841
|
+
<p class="custom-preview" hidden></p>` : ""}
|
|
842
|
+
${this.renderAutoTopup(packages, pkgs?.autoTopup, currency)}
|
|
814
843
|
<p class="error" hidden></p>
|
|
844
|
+
<button class="close" part="close" type="button">Not now</button>
|
|
815
845
|
${this.referral?.link ? `<div class="referral">
|
|
816
|
-
<div class="
|
|
846
|
+
<div class="referral-label">${escapeHtml(
|
|
847
|
+
(this.getAttribute("referral-label") ?? "Refer a friend \xB7 earn {credits} credits").replace(
|
|
848
|
+
"{credits}",
|
|
849
|
+
formatCredits(this.referral.rewardCredits ?? 0)
|
|
850
|
+
)
|
|
851
|
+
)}</div>
|
|
817
852
|
<div class="referral-row">
|
|
818
853
|
<div class="link" title="${escapeHtml(this.referral.link)}">${escapeHtml(this.referral.link)}</div>
|
|
819
854
|
<button class="referral-copy" type="button">${this.referralCopied ? "Copied!" : "Copy"}</button>
|
|
820
855
|
</div>
|
|
821
856
|
</div>` : ""}
|
|
822
|
-
<button class="close" part="close" type="button">Not now</button>
|
|
823
857
|
</div>
|
|
824
858
|
</div>
|
|
825
859
|
`;
|
|
@@ -854,6 +888,18 @@ var Crediball = (() => {
|
|
|
854
888
|
const input = this.shadow.querySelector(".custom-input");
|
|
855
889
|
const addBtn = this.shadow.querySelector(".custom-add");
|
|
856
890
|
const errEl = this.shadow.querySelector(".error");
|
|
891
|
+
const previewEl = this.shadow.querySelector(".custom-preview");
|
|
892
|
+
const rate = custom?.rate;
|
|
893
|
+
input?.addEventListener("input", () => {
|
|
894
|
+
if (!previewEl) return;
|
|
895
|
+
const amount = Number(input.value);
|
|
896
|
+
if (!rate || rate <= 0 || !Number.isFinite(amount) || amount <= 0) {
|
|
897
|
+
previewEl.hidden = true;
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
previewEl.hidden = false;
|
|
901
|
+
previewEl.textContent = `\u2248 ${formatCredits(Math.round(amount * rate))} credits`;
|
|
902
|
+
});
|
|
857
903
|
const submit = () => {
|
|
858
904
|
const amount = Number(input?.value);
|
|
859
905
|
const sym = currencySymbolFor(currency);
|
|
@@ -876,6 +922,66 @@ var Crediball = (() => {
|
|
|
876
922
|
if (e.key === "Enter") submit();
|
|
877
923
|
});
|
|
878
924
|
}
|
|
925
|
+
this.shadow.querySelector("[data-open-autotopup]")?.addEventListener("click", () => {
|
|
926
|
+
this.autoTopupOpen = true;
|
|
927
|
+
this.render();
|
|
928
|
+
});
|
|
929
|
+
this.shadow.querySelector(".autotopup-submit")?.addEventListener("click", () => {
|
|
930
|
+
const packageId = this.shadow.querySelector(".autotopup-package")?.value;
|
|
931
|
+
const thresholdCredits = Number(
|
|
932
|
+
this.shadow.querySelector(".autotopup-threshold")?.value
|
|
933
|
+
);
|
|
934
|
+
if (!packageId || !Number.isFinite(thresholdCredits) || thresholdCredits < 0) return;
|
|
935
|
+
if (this.emit("crediball-enable-autotopup", { packageId, thresholdCredits })) {
|
|
936
|
+
void this.startAutoTopupCheckout(packageId, thresholdCredits);
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
const fixBtn = this.shadow.querySelector("[data-fix-package]");
|
|
940
|
+
fixBtn?.addEventListener("click", () => {
|
|
941
|
+
const packageId = fixBtn.dataset.fixPackage;
|
|
942
|
+
const thresholdCredits = Number(fixBtn.dataset.fixThreshold);
|
|
943
|
+
if (!packageId || !Number.isFinite(thresholdCredits)) return;
|
|
944
|
+
if (this.emit("crediball-enable-autotopup", { packageId, thresholdCredits })) {
|
|
945
|
+
void this.startAutoTopupCheckout(packageId, thresholdCredits);
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
/** HTML for the auto-topup opt-in row / status line / needs_auth-fix prompt. */
|
|
950
|
+
renderAutoTopup(packages, autoTopup, currency) {
|
|
951
|
+
if (!packages.length) return "";
|
|
952
|
+
if (autoTopup?.enabled && autoTopup.status === "active") {
|
|
953
|
+
const pkg = packages.find((p) => p.id === autoTopup.packageId);
|
|
954
|
+
return `<div class="autotopup"><p class="autotopup-status">Auto top-up is on \u2014 buying ${escapeHtml(
|
|
955
|
+
pkg?.name ?? "your package"
|
|
956
|
+
)} whenever your balance drops below ${formatCredits(autoTopup.thresholdCredits)} credits.</p></div>`;
|
|
957
|
+
}
|
|
958
|
+
if (autoTopup?.enabled && (autoTopup.status === "needs_auth" || autoTopup.status === "failed")) {
|
|
959
|
+
const message = autoTopup.status === "needs_auth" ? "Auto top-up needs you to re-confirm your payment method." : "Auto top-up's last charge failed.";
|
|
960
|
+
return `<div class="autotopup">
|
|
961
|
+
<p class="autotopup-warning">${escapeHtml(message)}</p>
|
|
962
|
+
<button class="autotopup-toggle" type="button" data-fix-package="${escapeHtml(
|
|
963
|
+
autoTopup.packageId
|
|
964
|
+
)}" data-fix-threshold="${autoTopup.thresholdCredits}">Fix auto top-up</button>
|
|
965
|
+
</div>`;
|
|
966
|
+
}
|
|
967
|
+
if (this.autoTopupOpen) {
|
|
968
|
+
const cheapest = packages.reduce((min, p) => p.price < min.price ? p : min, packages[0]);
|
|
969
|
+
return `<div class="autotopup">
|
|
970
|
+
<div class="autotopup-row">
|
|
971
|
+
<select class="autotopup-package" aria-label="Package">
|
|
972
|
+
${packages.map(
|
|
973
|
+
(p) => `<option value="${escapeHtml(p.id)}">${escapeHtml(p.name)} \xB7 ${formatMoney(p.price, currency)}</option>`
|
|
974
|
+
).join("")}
|
|
975
|
+
</select>
|
|
976
|
+
<input class="autotopup-threshold" inputmode="numeric" aria-label="Threshold credits" value="${cheapest.credits}" />
|
|
977
|
+
<button class="autotopup-submit" type="button">Turn on</button>
|
|
978
|
+
</div>
|
|
979
|
+
<p class="autotopup-hint">Buy that package automatically whenever your balance drops below the credit amount above.</p>
|
|
980
|
+
</div>`;
|
|
981
|
+
}
|
|
982
|
+
return `<div class="autotopup">
|
|
983
|
+
<button class="autotopup-toggle" type="button" data-open-autotopup>Keep me topped up automatically</button>
|
|
984
|
+
</div>`;
|
|
879
985
|
}
|
|
880
986
|
/** Copy the invite link to the clipboard, with a 2s "Copied!" confirmation. */
|
|
881
987
|
async copyReferral() {
|
|
@@ -910,6 +1016,17 @@ var Crediball = (() => {
|
|
|
910
1016
|
this.client.createSubscriptionCheckout({ planId, returnPath: this.returnPath() })
|
|
911
1017
|
);
|
|
912
1018
|
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Start the built-in auto-topup setup Checkout and redirect. No money changes
|
|
1021
|
+
* hands here — it only saves a payment method Crediball charges later,
|
|
1022
|
+
* off-session, whenever the balance drops below `thresholdCredits`.
|
|
1023
|
+
*/
|
|
1024
|
+
startAutoTopupCheckout(packageId, thresholdCredits) {
|
|
1025
|
+
if (!this.client) return Promise.resolve();
|
|
1026
|
+
return this.redirect(
|
|
1027
|
+
this.client.createAutoTopupCheckout({ packageId, thresholdCredits, returnPath: this.returnPath() })
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
913
1030
|
async redirect(session) {
|
|
914
1031
|
try {
|
|
915
1032
|
const { url } = await session;
|
package/dist/index.d.ts
CHANGED
|
@@ -112,6 +112,7 @@ declare class CrediballPaywallElement extends CrediballElement {
|
|
|
112
112
|
private shadow;
|
|
113
113
|
private referral;
|
|
114
114
|
private referralCopied;
|
|
115
|
+
private autoTopupOpen;
|
|
115
116
|
static get observedAttributes(): string[];
|
|
116
117
|
constructor();
|
|
117
118
|
connectedCallback(): void;
|
|
@@ -121,6 +122,8 @@ declare class CrediballPaywallElement extends CrediballElement {
|
|
|
121
122
|
open(): void;
|
|
122
123
|
close(): void;
|
|
123
124
|
protected render(): void;
|
|
125
|
+
/** HTML for the auto-topup opt-in row / status line / needs_auth-fix prompt. */
|
|
126
|
+
private renderAutoTopup;
|
|
124
127
|
/** Copy the invite link to the clipboard, with a 2s "Copied!" confirmation. */
|
|
125
128
|
private copyReferral;
|
|
126
129
|
/** Dispatch a cancelable option event; returns true when the host did NOT preventDefault. */
|
|
@@ -130,6 +133,12 @@ declare class CrediballPaywallElement extends CrediballElement {
|
|
|
130
133
|
private startCheckout;
|
|
131
134
|
/** Start the built-in recurring subscription Checkout and redirect. */
|
|
132
135
|
private startSubscriptionCheckout;
|
|
136
|
+
/**
|
|
137
|
+
* Start the built-in auto-topup setup Checkout and redirect. No money changes
|
|
138
|
+
* hands here — it only saves a payment method Crediball charges later,
|
|
139
|
+
* off-session, whenever the balance drops below `thresholdCredits`.
|
|
140
|
+
*/
|
|
141
|
+
private startAutoTopupCheckout;
|
|
133
142
|
private redirect;
|
|
134
143
|
}
|
|
135
144
|
|
package/dist/index.js
CHANGED
package/dist/register.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crediball/elements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Framework-agnostic web components for showing Crediball credits inside your AI app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Filippo Rezzadore <filipporezzadore@gmail.com>",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"prepublishOnly": "npm run build"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@crediball/core": "^0.
|
|
59
|
+
"@crediball/core": "^0.7.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"tsup": "^8.3.5",
|