@pybara/pybara-js 0.1.0 → 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/dist/pybara-js.js CHANGED
@@ -1,401 +1,253 @@
1
1
  /* Pybara.js - pybara.com */
2
- import { n as e, r as t, t as n } from "./admin-D2vUTEg1.js";
3
- //#region node_modules/@pybara/pybara-ux/dist/config.js
4
- var r = {
5
- Z_INDEX: 999999,
6
- BACKGROUND: "rgba(0, 0, 0, 0.7)"
7
- }, i = {
8
- OVERLAY_FADE_IN_MS: 200,
9
- PANEL_OPEN_MS: 300,
10
- PANEL_CLOSE_MS: 200,
11
- OVERLAY_FADE_OUT_DELAY_MS: 50,
12
- OVERLAY_FADE_OUT_MS: 150
13
- }, a = 0, o = 0, s = 0;
14
- function c(e) {
15
- e.preventDefault();
16
- }
17
- function l(e) {
18
- e.preventDefault();
19
- }
20
- function u(e) {
21
- e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || [
22
- "ArrowUp",
23
- "ArrowDown",
24
- "PageUp",
25
- "PageDown",
26
- "Home",
27
- "End",
28
- " "
29
- ].includes(e.key) && e.preventDefault();
30
- }
31
- function d() {
32
- window.scrollTo(o, s);
33
- }
34
- function f() {
35
- a === 0 && (o = window.scrollX, s = window.scrollY, window.addEventListener("wheel", c, { passive: !1 }), window.addEventListener("touchmove", l, { passive: !1 }), window.addEventListener("keydown", u), window.addEventListener("scroll", d)), a += 1;
36
- }
37
- function p() {
38
- a !== 0 && (--a, a === 0 && (window.removeEventListener("wheel", c), window.removeEventListener("touchmove", l), window.removeEventListener("keydown", u), window.removeEventListener("scroll", d)));
39
- }
40
- //#endregion
41
- //#region node_modules/@pybara/pybara-ux/dist/modal.js
42
- function m() {
43
- return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
44
- }
45
- var h = [
46
- "DESTINATION_REFUSED",
47
- "INVALID_BENEFICIARY",
48
- "NO_WALLET_AVAILABLE",
49
- "SITE_MISMATCH"
50
- ];
51
- function g(e) {
52
- return h.includes(e) ? e : "UNKNOWN";
53
- }
54
- var _ = class extends Error {
55
- constructor(e, t) {
56
- super(t), this.name = "EmbeddedError", this.code = e, Object.setPrototypeOf(this, new.target.prototype);
57
- }
58
- };
59
- function v(e) {
60
- return typeof e == "object" && !!e && typeof e.type == "string" && e.type.startsWith("pybara_embedded_");
61
- }
62
- var y = class {
63
- constructor(e) {
64
- this.overlay = null, this.iframe = null, this.spinner = null, this.handleMessage = null, this.handleEscapeKey = null, this.closable = !0, this.iframeLoaded = !1, this.pendingPaymentData = null, this.config = e, this.walletOrigin = new URL(e.walletUrl).origin, this.createIframe();
65
- }
66
- createIframe() {
67
- let e = document.createElement("div");
68
- e.className = "pybara-checkout-overlay", e.style.cssText = `
69
- position: fixed;
70
- top: 0;
71
- left: 0;
72
- right: 0;
73
- bottom: 0;
74
- background: ${r.BACKGROUND};
75
- z-index: ${r.Z_INDEX};
76
- display: none;
77
- align-items: center;
78
- justify-content: center;
79
- overscroll-behavior: none;
80
- user-select: none;
81
- `;
82
- let t = document.createElement("iframe");
83
- t.src = `${this.config.walletUrl}/pay?mode=embedded`, t.title = "Pybara Wallet", t.style.cssText = "\n width: 100%;\n max-width: 500px;\n height: 0;\n max-height: 90vh;\n border: none;\n border-radius: 20px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);\n overscroll-behavior: none;\n ";
84
- let n = document.createElement("div");
85
- n.setAttribute("aria-hidden", "true"), n.style.cssText = "\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n ", n.innerHTML = "\n <svg viewBox=\"0 0 50 50\" width=\"50\" height=\"50\">\n <circle cx=\"25\" cy=\"25\" r=\"20\" fill=\"none\" stroke=\"white\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-dasharray=\"80\" stroke-dashoffset=\"20\"/>\n </svg>\n ", n.querySelector("svg")?.animate([{ transform: "rotate(0deg)" }, { transform: "rotate(360deg)" }], {
86
- duration: 1e3,
87
- iterations: Infinity
88
- }), t.addEventListener("load", () => {
89
- this.iframeLoaded = !0, this.pendingPaymentData &&= (this.sendInitMessage(this.pendingPaymentData), null);
90
- }, { once: !0 }), this.handleMessage = (e) => {
91
- if (e.origin === this.walletOrigin && e.source === t.contentWindow && v(e.data)) switch (e.data.type) {
92
- case "pybara_embedded_result":
93
- this.config.onSuccess?.({
94
- blockIndex: e.data.blockIndex,
95
- reference: e.data.reference,
96
- payerPrincipal: e.data.payerPrincipal,
97
- token: e.data.token
98
- });
99
- break;
100
- case "pybara_embedded_error":
101
- this.close(), this.config.onError?.(new _(g(e.data.code), e.data.message));
102
- break;
103
- case "pybara_embedded_closed":
104
- this.close(), this.config.onClose?.();
105
- break;
106
- case "pybara_embedded_resize":
107
- t.style.height !== `${e.data.height}px` && (t.style.height = `${e.data.height}px`), this.spinner && (this.spinner.remove(), this.spinner = null, m() || t.animate([{
108
- transform: "translateY(20px)",
109
- opacity: 0
110
- }, {
111
- transform: "none",
112
- opacity: 1
113
- }], {
114
- duration: i.PANEL_OPEN_MS,
115
- easing: "ease"
116
- }));
117
- break;
118
- case "pybara_embedded_closable": this.closable = e.data.closable;
119
- }
120
- }, window.addEventListener("message", this.handleMessage), this.handleEscapeKey = (e) => {
121
- e.key === "Escape" && this.closable && (this.close(), this.config.onClose?.());
122
- }, document.addEventListener("keydown", this.handleEscapeKey), e.appendChild(n), e.appendChild(t), document.body.appendChild(e), this.overlay = e, this.iframe = t, this.spinner = n;
123
- }
124
- sendInitMessage(e) {
125
- let t = {
126
- type: "pybara_embedded_init",
127
- beneficiary: e.modalData.beneficiary,
128
- salt: e.modalData.salt,
129
- amountUSD: e.modalData.amountUSD,
130
- reference: e.modalData.reference,
131
- context: this.config.context,
132
- ...e.recurring ? { recurring: e.recurring } : {},
133
- ...e.sdkFields ? { sdkFields: e.sdkFields } : {},
134
- ...this.config.thankYouMessage ? { thankYouMessage: this.config.thankYouMessage } : {}
135
- };
136
- this.iframe?.contentWindow?.postMessage(t, this.walletOrigin);
137
- }
138
- open() {
139
- this.overlay && this.overlay.style.display !== "flex" && (f(), this.overlay.style.display = "flex", m() || this.overlay.animate([{ opacity: 0 }, { opacity: 1 }], {
140
- duration: i.OVERLAY_FADE_IN_MS,
141
- easing: "ease"
142
- }));
143
- }
144
- show(e) {
145
- this.open(), this.iframeLoaded ? this.sendInitMessage(e) : this.pendingPaymentData = e;
146
- }
147
- renderTerminalState(e) {
148
- this.close(), f();
149
- let t = document.createElement("div");
150
- t.className = "pybara-checkout-overlay", t.style.cssText = `
151
- position: fixed;
152
- top: 0;
153
- left: 0;
154
- right: 0;
155
- bottom: 0;
156
- background: ${r.BACKGROUND};
157
- z-index: ${r.Z_INDEX};
158
- display: flex;
159
- align-items: center;
160
- justify-content: center;
161
- user-select: none;
162
- `;
163
- let n = document.createElement("div");
164
- n.style.cssText = "\n background: #fff;\n border-radius: 12px;\n color: #666;\n padding: 2rem;\n line-height: 1.4;\n max-width: 360px;\n text-align: center;\n font-family: system-ui, sans-serif;\n user-select: text;\n ", n.textContent = e, t.appendChild(n), t.addEventListener("click", (e) => {
165
- e.target === t && (t.remove(), p(), this.config.onClose?.());
166
- }), document.body.appendChild(t), this.overlay = t;
167
- }
168
- close() {
169
- this.handleMessage &&= (window.removeEventListener("message", this.handleMessage), null), this.handleEscapeKey &&= (document.removeEventListener("keydown", this.handleEscapeKey), null), p();
170
- let e = this.overlay, t = this.iframe;
171
- this.overlay = null, this.iframe = null, this.spinner = null, this.iframeLoaded = !1, this.pendingPaymentData = null, this.closable = !0, e && (e.style.display === "none" || m() ? e.remove() : (e.style.pointerEvents = "none", t?.animate([{
172
- transform: "scale(1)",
173
- opacity: 1
174
- }, {
175
- transform: "scale(0.95)",
176
- opacity: 0
177
- }], {
178
- duration: i.PANEL_CLOSE_MS,
179
- easing: "ease-out",
180
- fill: "forwards"
181
- }), e.animate([{ opacity: 1 }, { opacity: 0 }], {
182
- duration: i.OVERLAY_FADE_OUT_MS,
183
- delay: i.OVERLAY_FADE_OUT_DELAY_MS,
184
- easing: "ease-out",
185
- fill: "forwards"
186
- }).finished.then(() => e.remove(), () => e.remove())));
187
- }
188
- }, b = {
189
- every2min: 120,
190
- weekly: 604800,
191
- monthly: 2592e3,
192
- yearly: 31536e3
193
- }, x = 2592e3, S = [
194
- 3,
195
- 6,
196
- 12,
197
- 24
198
- ], C = .5, w = {
2
+ import { n as e, r as t, t as n } from "./admin-B1pVP25-.js";
3
+ import { t as r } from "./modal-BCDMoDW9.js";
4
+ var i = {
199
5
  widget: "donate-widget",
200
6
  recurring: "donate-widget-recurring"
201
- }, T = (e) => {
202
- if (!e || !e.includes("-")) return !0;
203
- let [t = 0, n = 0] = e.split("-").map(Number), [r = 0, i = 0] = "1-0".split("-").map(Number);
204
- return t < r || t === r && n < i;
205
7
  };
206
- async function E(t, n, r) {
207
- let i = `https://${e[t].admin_core}.raw.ic0.app`, a = { "Content-Type": "application/json" }, o = null;
8
+ function a(e, t) {
9
+ let n = (e) => {
10
+ let t = /^(\d+)-(\d+)$/.exec(e);
11
+ return t ? [Number(t[1]), Number(t[2])] : null;
12
+ }, r = n(e), i = n(t);
13
+ return !r || !i ? !1 : r[0] > i[0] || r[0] === i[0] && r[1] >= i[1];
14
+ }
15
+ async function o(e) {
16
+ let t = await fetch(e);
17
+ if (!t.ok) throw Error(`HTTP ${t.status} from ${e}`);
18
+ return await t.json();
19
+ }
20
+ async function s(t, n, r) {
21
+ let s = e[t], c = `https://${s.admin_core}.raw.icp0.io/widget-config`, l = `https://${s.admin_regs}.raw.icp0.io/widget-terms?site_url=${encodeURIComponent(n)}&principal=${encodeURIComponent(r)}`, u, d;
208
22
  try {
209
- let [e, t] = await Promise.all([fetch(`${i}/api/widget-terms?site_url=${encodeURIComponent(n)}&principal=${encodeURIComponent(r)}`, { headers: a }), fetch(`${i}/api/kill-switches`, { headers: a })]);
210
- if (t.ok && (o = (await t.json()).switches?.find((e) => (e.key === w.widget || e.key === w.recurring) && e.enabled === !1) ?? null, o)) return {
211
- show: !1,
212
- reason: `This integration is temporarily unavailable (${o.key})`
213
- };
214
- if (!e.ok) return {
215
- show: !1,
216
- reason: `Site not verified (${e.status})`
217
- };
218
- let s = await e.json();
219
- if (s.terms?.signed !== !0 || T(s.terms?.version)) {
220
- let e = s.terms?.superseded_version;
221
- return {
222
- show: !1,
223
- reason: e ? `Widget terms v${e} now apply — accept them in Pybara Wallet (Manage › Sites)` : "This site has not accepted the widget’s terms in Pybara Wallet (Manage › Sites)"
224
- };
225
- }
23
+ [u, d] = await Promise.all([o(c), o(l)]);
24
+ } catch (e) {
226
25
  return {
227
- show: !0,
228
- reason: null
229
- };
230
- } catch {
231
- return o ? {
232
26
  show: !1,
233
- reason: `This integration is temporarily unavailable (${o.key})`
234
- } : {
235
- show: !0,
236
- reason: null
27
+ reason: `could not read the widget's configuration (${e instanceof Error ? e.message : String(e)})`
237
28
  };
238
29
  }
30
+ let f = u.kill_switch?.plugins ?? {};
31
+ return f[i.widget]?.enabled === !0 ? !d.signed || !d.version ? {
32
+ show: !1,
33
+ reason: `${n} has not accepted the widget's terms in Pybara Wallet (Manage › Sites › Add a site without a plugin)`
34
+ } : a(d.version, "1-0") ? {
35
+ show: !0,
36
+ recurring: f[i.recurring]?.enabled === !0
37
+ } : {
38
+ show: !1,
39
+ reason: `this widget version requires terms 1-0, and ${n} accepted ${d.version}: accept the new terms in Pybara Wallet (Manage › Sites)`
40
+ } : {
41
+ show: !1,
42
+ reason: "the widget is switched off by Pybara for now"
43
+ };
239
44
  }
240
45
  //#endregion
241
46
  //#region src/types.ts
242
- var D = class extends Error {
47
+ var c = class extends Error {
243
48
  constructor(e, t) {
244
49
  super(t), this.name = "PybaraError", this.code = e;
245
50
  }
246
- }, O = class {
51
+ }, l = /^(0|[1-9]\d*)(\.\d+)?$/, u = /^[a-z0-9-]{1,64}$/, d = class {
247
52
  constructor(e) {
248
- this.adminClient = null, this.fiatRate = 1, this.modal = null, this.gate = null, this.isOpen = !1, this.activePayment = null, this.config = A(e), this.ready = this.init();
53
+ this.adminClient = null, this.modal = null, this.gateOutcome = null, this.isOpen = !1, this.completedPayment = !1, this.activePayment = null, this.config = m(e), this.ready = this.init();
54
+ }
55
+ get gate() {
56
+ return this.gateOutcome;
249
57
  }
250
58
  async open(e) {
251
- if (await this.ready, this.gate && !this.gate.show) throw new D("GATE_REFUSED", this.gate.reason ?? "This site cannot accept Pybara payments yet.");
252
- let t = e.amount;
253
- if (!Number.isFinite(t) || t <= 0) throw new D("INVALID_CONFIG", `Invalid amount: ${e.amount}`);
254
- let n = e.currency ?? this.config.currency, r = t * (n === "USD" ? 1 : this.fiatRate);
255
- if (r < .5) throw new D("BELOW_MINIMUM", `Minimum payment is $${C} USD.`);
256
- let i = k(e);
59
+ if (await this.ready, this.gateOutcome && !this.gateOutcome.show) throw new c("GATE_REFUSED", this.gateOutcome.reason ?? "This site cannot accept Pybara payments yet.");
60
+ let t = f(e.amount);
61
+ if (t === null) throw new c("INVALID_CONFIG", `open(): "amount" must be a decimal string in the currency's major units, e.g. "12.34", or a positive number — got ${JSON.stringify(e.amount)}`);
62
+ let n = e.currency ?? this.config.currency;
63
+ if (!n) throw new c("INVALID_CONFIG", "open(): \"currency\" is required — pass it to open(), or set it once in Pybara.checkout()");
64
+ if (!/^[A-Z]{3}$/.test(n)) throw new c("INVALID_CONFIG", `open(): "currency" must be an uppercase ISO 4217 code, e.g. "EUR" — got ${JSON.stringify(n)}`);
65
+ if (e.durationMonths != null && !e.frequency) throw new c("INVALID_CONFIG", "open(): \"durationMonths\" needs a \"frequency\"");
66
+ let r = e.frequency ? {
67
+ frequency: e.frequency,
68
+ ...e.durationMonths == null ? {} : { durationMonths: e.durationMonths }
69
+ } : void 0;
70
+ if (r && this.gateOutcome && this.gateOutcome.show && !this.gateOutcome.recurring) throw new c("GATE_REFUSED", "Recurring payments are temporarily unavailable.");
257
71
  if (this.isOpen) return;
258
72
  this.isOpen = !0;
259
- let a = !1;
73
+ let i = !1;
260
74
  try {
261
75
  await this.adminClient?.getRecipientDataWithCache(this.config.siteUrl);
262
76
  } catch (e) {
263
- this.config.debug && console.warn("🧩 pybara-js: recipient access validation failed:", e), a = !0;
77
+ this.config.debug && console.warn("🧩 pybara-js: recipient access validation failed:", e), i = !0;
264
78
  }
265
79
  this.modal || this.initCheckoutModal();
266
- let o = this.modal;
267
- if (!o) throw this.isOpen = !1, new D("CHECKOUT_FAILED", "The checkout modal could not be initialised.");
268
- a ? (this.isOpen = !1, o.renderTerminalState("This recipient is currently unable to accept donations."), this.initCheckoutModal()) : (this.activePayment = e, o.show({
269
- modalData: {
270
- amountUSD: r,
271
- beneficiary: this.config.recipientPrincipal,
272
- salt: j(),
273
- reference: this.config.siteName
274
- },
275
- recurring: i,
276
- sdkFields: this.buildSdkFields(e, t, n, r, i != null)
277
- }));
80
+ let a = this.modal;
81
+ if (!a) throw this.isOpen = !1, new c("CHECKOUT_FAILED", "The checkout modal could not be initialised.");
82
+ if (i) this.isOpen = !1, a.renderTerminalState("This recipient is currently unable to accept donations."), this.initCheckoutModal();
83
+ else {
84
+ this.activePayment = e;
85
+ try {
86
+ a.show({
87
+ modalData: {
88
+ beneficiary: this.config.recipientPrincipal,
89
+ salt: h(),
90
+ reference: this.config.siteName
91
+ },
92
+ recurring: r,
93
+ checkout: this.checkoutFor(e, t, n)
94
+ });
95
+ } catch (e) {
96
+ throw this.isOpen = !1, this.activePayment = null, g(e);
97
+ }
98
+ }
278
99
  }
279
100
  close() {
280
101
  this.modal?.close();
281
102
  }
282
103
  async init() {
283
104
  let t = this.config.staging ? "staging" : "prod", r = this.config.debug;
284
- r && console.log("🧩 pybara-js: initializing checkout…"), this.adminClient = new n({
105
+ r && console.log("🧩 pybara-js: initializing checkout…"), this.preconnectWallet(t), this.initCheckoutModal();
106
+ let i = new n({
285
107
  adminCoreCanisterId: e[t].admin_core,
286
108
  debug: r
287
109
  });
288
- try {
289
- await this.adminClient.init(), await this.adminClient.getPybaraAdminConfig(), r && console.log("🧩 pybara-js: admin client ready"), this.adminClient.getRecipientDataWithCache(this.config.siteUrl).catch((e) => {
290
- r && console.warn("🧩 pybara-js: recipient prewarm failed:", e);
291
- }), this.fiatRate = await this.adminClient.getFiatRate(this.config.currency) ?? 1;
292
- } catch (e) {
293
- r && console.error("🧩 pybara-js: admin client init failed:", e);
294
- }
295
- this.config.pluginVersion || (this.gate = await E(t, window.location.origin, this.config.recipientPrincipal), !this.gate.show && r && console.warn("🧩 pybara-js: gate refused —", this.gate.reason)), this.preconnectWallet(t), this.initCheckoutModal();
110
+ this.adminClient = i;
111
+ let a = (async () => {
112
+ try {
113
+ await i.init(), await i.getPybaraAdminConfig(), r && console.log("🧩 pybara-js: admin client ready"), i.getRecipientDataWithCache(this.config.siteUrl).catch((e) => {
114
+ r && console.warn("🧩 pybara-js: recipient prewarm failed:", e);
115
+ });
116
+ } catch (e) {
117
+ r && console.error("🧩 pybara-js: admin client init failed:", e);
118
+ }
119
+ })(), o = (async () => {
120
+ this.config.pluginVersion || (this.gateOutcome = await s(t, this.config.siteUrl, this.config.recipientPrincipal), !this.gateOutcome.show && r && console.warn("🧩 pybara-js: gate refused —", this.gateOutcome.reason));
121
+ })();
122
+ await Promise.all([a, o]);
296
123
  }
297
124
  initCheckoutModal() {
298
- let e = this.config.staging ? "staging" : "prod", n = this.config, r = this;
299
- this.modal = new y({
125
+ let e = this.config.staging ? "staging" : "prod", n = this.config, i = this;
126
+ this.modal = new r({
300
127
  walletUrl: t[e],
301
128
  context: "donation",
302
129
  thankYouMessage: n.thankYouMessage,
303
130
  onSuccess: (e) => {
304
- let t = r.activePayment, i = t ? k(t) : void 0, a = {
131
+ i.completedPayment = !0;
132
+ let t = i.activePayment, r = t?.frequency != null, a = {
305
133
  blockIndex: e.blockIndex,
306
134
  reference: e.reference,
307
135
  token: e.token,
308
- recurring: i != null,
136
+ recurring: r,
309
137
  frequency: t?.frequency,
310
- durationMonths: i?.durationMonths,
311
- isUnlimited: i?.totalPulls === 0,
138
+ durationMonths: r ? t?.durationMonths ?? void 0 : void 0,
139
+ isUnlimited: r && t?.durationMonths == null,
312
140
  payerPrincipal: e.payerPrincipal,
313
141
  recipientPrincipal: n.recipientPrincipal
314
142
  };
315
143
  t?.onSuccess?.(a);
316
144
  },
317
145
  onError: (e) => {
318
- r.isOpen = !1;
319
- let t = r.activePayment;
320
- r.activePayment = null, t?.onError?.(M(e)), r.modal = null, r.initCheckoutModal();
146
+ i.isOpen = !1;
147
+ let t = i.activePayment;
148
+ i.activePayment = null, t?.onError?.(g(e)), i.modal = null, i.initCheckoutModal();
321
149
  },
322
150
  onClose: () => {
323
- r.isOpen = !1;
324
- let e = r.activePayment;
325
- r.activePayment = null, e?.onClose?.(), n.thankYouRedirectUrl ? window.location.href = n.thankYouRedirectUrl : (r.modal = null, r.initCheckoutModal());
151
+ i.isOpen = !1;
152
+ let e = i.activePayment;
153
+ i.activePayment = null, e?.onClose?.(), i.completedPayment && n.thankYouRedirectUrl ? window.location.href = n.thankYouRedirectUrl : (i.completedPayment = !1, i.modal = null, i.initCheckoutModal());
326
154
  }
327
155
  });
328
156
  }
329
- buildSdkFields(e, t, n, r, i) {
330
- let a = this.config, o = [
331
- ["platform", "pybara-js"],
332
- ["site_url", a.siteUrl],
333
- ["site_name", a.siteName],
334
- ["donation_amount_usd", r.toFixed(2)],
335
- ["donation_amount_local", t.toFixed(2)],
336
- ["donation_local_currency", n],
337
- ["donation_type", i ? "recurring" : "one-time"],
338
- ["site_country", a.siteCountry ?? ""],
339
- ["notify_email", a.notifyEmail ?? ""],
340
- ["notify_donations", a.notifyDonations ?? !0 ? "1" : "0"],
341
- ["donor_is_anonymous", e.donorIsAnonymous ? "1" : "0"],
342
- ["donor_message", e.donorMessage ?? ""],
343
- ["wants_tax_receipt", e.wantsTaxReceipt ? "1" : "0"],
344
- ["donor_first_name", e.donorFirstName ?? ""],
345
- ["donor_last_name", e.donorLastName ?? ""],
346
- ["donor_email", e.donorEmail ?? ""],
347
- ["donor_company", e.donorCompany ?? ""],
348
- ["donor_country", e.donorCountry ?? ""],
349
- ["donor_address_line1", e.donorAddressLine1 ?? ""],
350
- ["donor_address_line2", e.donorAddressLine2 ?? ""],
351
- ["donor_address_city", e.donorAddressCity ?? ""],
352
- ["donor_address_state", e.donorAddressState ?? ""],
353
- ["donor_address_postal_code", e.donorAddressPostalCode ?? ""],
354
- ["donor_address_country", e.donorAddressCountry ?? ""]
355
- ];
356
- return e.donorTosVersion && (o.push(["donor_tos_version", e.donorTosVersion]), o.push(["donor_tos_accepted_at", e.donorTosAcceptedAt ?? ""])), a.recipientTosVersion && (o.push(["recipient_tos_version", a.recipientTosVersion]), o.push(["recipient_tos_accepted_at", a.recipientTosAcceptedAt ?? ""])), a.pluginVersion && o.push(["plugin_version", a.pluginVersion]), o;
157
+ checkoutFor(e, t, n) {
158
+ let r = this.config;
159
+ return {
160
+ v: 1,
161
+ amount: t,
162
+ currency: n,
163
+ site: {
164
+ url: r.siteUrl,
165
+ name: r.siteName,
166
+ country: r.siteCountry
167
+ },
168
+ platform: r.platform ?? "pybara-js",
169
+ pluginVersion: r.pluginVersion,
170
+ siteTerms: {
171
+ version: r.recipientTosVersion,
172
+ acceptedAt: r.recipientTosAcceptedAt
173
+ },
174
+ notify: {
175
+ email: r.notifyEmail,
176
+ enabled: r.notifyDonations
177
+ },
178
+ donation: {
179
+ donor: {
180
+ email: e.donorEmail,
181
+ firstName: e.donorFirstName,
182
+ lastName: e.donorLastName,
183
+ company: e.donorCompany,
184
+ country: e.donorCountry,
185
+ address: {
186
+ line1: e.donorAddressLine1,
187
+ line2: e.donorAddressLine2,
188
+ city: e.donorAddressCity,
189
+ state: e.donorAddressState,
190
+ postalCode: e.donorAddressPostalCode,
191
+ country: e.donorAddressCountry
192
+ },
193
+ tos: {
194
+ version: e.donorTosVersion,
195
+ acceptedAt: e.donorTosAcceptedAt
196
+ }
197
+ },
198
+ message: e.donorMessage,
199
+ isAnonymous: e.donorIsAnonymous,
200
+ wantsTaxReceipt: e.wantsTaxReceipt
201
+ }
202
+ };
357
203
  }
358
204
  preconnectWallet(e) {
359
205
  let n = document.createElement("link");
360
206
  n.rel = "preconnect", n.href = new URL(t[e]).origin, n.crossOrigin = "anonymous", document.head.appendChild(n);
361
207
  }
362
208
  };
363
- function k(e) {
364
- if (!e.frequency) return;
365
- let t = b[e.frequency], n = e.durationMonths;
366
- if (n == null) return {
367
- frequency: e.frequency,
368
- totalPulls: 0
369
- };
370
- if (!S.includes(n)) throw new D("INVALID_CONFIG", `durationMonths must be one of ${S.join(", ")} — got ${n}.`);
371
- return {
372
- frequency: e.frequency,
373
- totalPulls: Math.ceil(n * x / t),
374
- durationMonths: n
375
- };
209
+ function f(e) {
210
+ let t = typeof e == "number" && Number.isFinite(e) ? String(e) : e;
211
+ return typeof t != "string" || !l.test(t) || !/[1-9]/.test(t) ? null : t;
376
212
  }
377
- function A(e) {
378
- if (!e.recipientPrincipal) throw new D("INVALID_CONFIG", "Pybara.checkout: \"recipientPrincipal\" is required");
379
- if (!e.siteUrl) throw new D("INVALID_CONFIG", "Pybara.checkout: \"siteUrl\" is required");
380
- if (!e.siteName) throw new D("INVALID_CONFIG", "Pybara.checkout: \"siteName\" is required");
213
+ function p(e) {
214
+ let t = e.trim();
215
+ if (!t) return null;
216
+ let n;
217
+ try {
218
+ n = new URL(t.includes("://") ? t : `https://${t}`);
219
+ } catch {
220
+ return null;
221
+ }
222
+ return n.protocol !== "https:" && n.protocol !== "http:" ? null : n.origin + n.pathname.replace(/\/+$/, "");
223
+ }
224
+ function m(e) {
225
+ if (!e.recipientPrincipal) throw new c("INVALID_CONFIG", "Pybara.checkout: \"recipientPrincipal\" is required");
226
+ if (!e.siteUrl) throw new c("INVALID_CONFIG", "Pybara.checkout: \"siteUrl\" is required");
227
+ let t = p(e.siteUrl);
228
+ if (!t) throw new c("INVALID_CONFIG", `Pybara.checkout: "siteUrl" is not a valid http(s) URL: ${e.siteUrl}`);
229
+ if (!e.siteName) throw new c("INVALID_CONFIG", "Pybara.checkout: \"siteName\" is required");
230
+ if (e.currency != null && !/^[A-Z]{3}$/.test(e.currency)) throw new c("INVALID_CONFIG", `Pybara.checkout: "currency" must be an uppercase ISO 4217 code, e.g. "EUR" — got ${JSON.stringify(e.currency)}`);
231
+ if (e.platform != null && !u.test(e.platform)) throw new c("INVALID_CONFIG", "Pybara.checkout: \"platform\" must be a lowercase slug (a-z, 0-9, -), at most 64 characters");
381
232
  return {
382
233
  ...e,
383
- currency: e.currency ?? "USD",
234
+ siteUrl: t,
384
235
  staging: e.staging ?? !1,
385
236
  debug: e.debug ?? !1
386
237
  };
387
238
  }
388
- function j() {
239
+ function h() {
389
240
  let e = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
390
241
  return btoa(String.fromCharCode(...e));
391
242
  }
392
- function M(e) {
243
+ function g(e) {
393
244
  return e instanceof Error ? e : Error(String(e));
394
245
  }
395
246
  //#endregion
396
247
  //#region src/index.ts
397
- var N = { checkout(e) {
398
- return new O(e);
399
- } };
248
+ function _(e) {
249
+ return new d(e);
250
+ }
251
+ var v = { checkout: _ };
400
252
  //#endregion
401
- export { N as Pybara, O as PybaraCheckout, D as PybaraError };
253
+ export { v as Pybara, d as PybaraCheckout, c as PybaraError, _ as checkout };
package/dist/types.d.ts CHANGED
@@ -14,7 +14,7 @@ export interface CheckoutConfig {
14
14
  siteUrl: string;
15
15
  /** Site display name — shown in the checkout frame and stored as the subscription name on recurring payments. */
16
16
  siteName: string;
17
- /** Fiat currency the amounts passed to open() are in (ISO 4217, e.g. 'EUR'). Default 'USD'. */
17
+ /** The currency open() charges in when its own `currency` is absent (ISO 4217, e.g. 'EUR'): set it once on a single-currency site. No default — open() refuses when neither is set. */
18
18
  currency?: string;
19
19
  /** Replaces the success screen's default title. */
20
20
  thankYouMessage?: string;
@@ -22,6 +22,8 @@ export interface CheckoutConfig {
22
22
  thankYouRedirectUrl?: string;
23
23
  /** Site base country (ISO 3166-1 alpha-2). */
24
24
  siteCountry?: string;
25
+ /** The integration surface reported in the payment log's `platform` field: a lowercase slug (a-z, 0-9, -), at most 64 characters — default 'pybara-js'. First-party consumers set their own (e.g. 'pybara-donate'). A label, never used for a decision. */
26
+ platform?: string;
25
27
  /** Site operator's contact email — terms warnings, per-payment notification emails. */
26
28
  notifyEmail?: string;
27
29
  /** false silences the per-payment notification email at notifyEmail. Default true. */
@@ -39,13 +41,17 @@ export interface CheckoutConfig {
39
41
  }
40
42
  /** One payment attempt — the params of a single open() call. */
41
43
  export interface PaymentParams {
42
- /** Amount in the checkout's currency (or this call's currency override). */
43
- amount: number;
44
- /** Per-call fiat override (ISO 4217) — defaults to the checkout's currency. */
44
+ /**
45
+ * The amount in the currency's major units, as a decimal string — "12.34" for €12.34, "1000" for ¥1,000 — as PayPal's API takes it.
46
+ * A number is accepted and turned into its decimal text (12.34 → "12.34").
47
+ * The checkout reads it with the currency's ISO 4217 minor units, and refuses more decimals than the currency holds ("12.345" EUR, "1000.5" JPY), so the page never needs to know them.
48
+ */
49
+ amount: string | number;
50
+ /** The currency charged (ISO 4217, e.g. 'EUR') — the checkout config's when absent; one of the two is required. A multi-currency site passes it on every call. */
45
51
  currency?: string;
46
52
  /** Recurring frequency — omit for a one-time payment. */
47
53
  frequency?: Frequency;
48
- /** Recurring length in months: 3 | 6 | 12 | 24. Omit with frequency for an unlimited subscription. */
54
+ /** Recurring length in months: 3 | 6 | 12 | 24, checked by the checkout. Omit with frequency for an unlimited subscription. */
49
55
  durationMonths?: number;
50
56
  /** Hide the payer's identity on the public record. */
51
57
  donorIsAnonymous?: boolean;
@@ -95,8 +101,11 @@ export interface CheckoutResult {
95
101
  /** The recipient's wallet principal — the checkout config's, echoed for convenience. */
96
102
  recipientPrincipal: string;
97
103
  }
98
- /** Typed error codes — mirrors the EmbeddedError pattern in pybara-ux. */
99
- export type PybaraErrorCode = 'INVALID_CONFIG' | 'GATE_REFUSED' | 'RECIPIENT_SUSPENDED' | 'BELOW_MINIMUM' | 'CHECKOUT_FAILED';
104
+ /**
105
+ * Typed codes for what Pybara.checkout() and open() throw before the checkout opens, as EmbeddedError's codes are for what the checkout reports once open.
106
+ * An amount below the minimum is not one: the checkout shows the minimum itself, in the payer's currency.
107
+ */
108
+ export type PybaraErrorCode = 'INVALID_CONFIG' | 'GATE_REFUSED' | 'RECIPIENT_SUSPENDED' | 'CHECKOUT_FAILED';
100
109
  export declare class PybaraError extends Error {
101
110
  readonly code: PybaraErrorCode;
102
111
  constructor(code: PybaraErrorCode, message: string);
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}