@pybara/pybara-js 0.2.0 → 0.4.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,236 +1,42 @@
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-DCwwkKZ7.js";
4
+ var i = {
199
5
  widget: "donate-widget",
200
6
  recurring: "donate-widget-recurring"
201
7
  };
202
- function T(e, t) {
8
+ function a(e, t) {
203
9
  let n = (e) => {
204
10
  let t = /^(\d+)-(\d+)$/.exec(e);
205
11
  return t ? [Number(t[1]), Number(t[2])] : null;
206
12
  }, r = n(e), i = n(t);
207
13
  return !r || !i ? !1 : r[0] > i[0] || r[0] === i[0] && r[1] >= i[1];
208
14
  }
209
- async function E(e) {
15
+ async function o(e) {
210
16
  let t = await fetch(e);
211
17
  if (!t.ok) throw Error(`HTTP ${t.status} from ${e}`);
212
18
  return await t.json();
213
19
  }
214
- async function D(t, n, r) {
215
- let i = e[t], a = `https://${i.admin_core}.raw.icp0.io/widget-config`, o = `https://${i.admin_regs}.raw.icp0.io/widget-terms?site_url=${encodeURIComponent(n)}&principal=${encodeURIComponent(r)}`, s, c;
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;
216
22
  try {
217
- [s, c] = await Promise.all([E(a), E(o)]);
23
+ [u, d] = await Promise.all([o(c), o(l)]);
218
24
  } catch (e) {
219
25
  return {
220
26
  show: !1,
221
27
  reason: `could not read the widget's configuration (${e instanceof Error ? e.message : String(e)})`
222
28
  };
223
29
  }
224
- let l = s.kill_switch?.plugins ?? {};
225
- return l[w.widget]?.enabled === !0 ? !c.signed || !c.version ? {
30
+ let f = u.kill_switch?.plugins ?? {};
31
+ return f[i.widget]?.enabled === !0 ? !d.signed || !d.version ? {
226
32
  show: !1,
227
33
  reason: `${n} has not accepted the widget's terms in Pybara Wallet (Manage › Sites › Add a site without a plugin)`
228
- } : T(c.version, "1-0") ? {
34
+ } : a(d.version, "1-0") ? {
229
35
  show: !0,
230
- recurring: l[w.recurring]?.enabled === !0
36
+ recurring: f[i.recurring]?.enabled === !0
231
37
  } : {
232
38
  show: !1,
233
- reason: `this widget version requires terms 1-0, and ${n} accepted ${c.version}: accept the new terms in Pybara Wallet (Manage › Sites)`
39
+ reason: `this widget version requires terms 1-0, and ${n} accepted ${d.version}: accept the new terms in Pybara Wallet (Manage › Sites)`
234
40
  } : {
235
41
  show: !1,
236
42
  reason: "the widget is switched off by Pybara for now"
@@ -238,143 +44,173 @@ async function D(t, n, r) {
238
44
  }
239
45
  //#endregion
240
46
  //#region src/types.ts
241
- var O = class extends Error {
47
+ var c = class extends Error {
242
48
  constructor(e, t) {
243
49
  super(t), this.name = "PybaraError", this.code = e;
244
50
  }
245
- }, k = class {
51
+ }, l = /^(0|[1-9]\d*)(\.\d+)?$/, u = /^[a-z0-9-]{1,64}$/, d = class {
246
52
  constructor(e) {
247
- this.adminClient = null, this.fiatRate = 1, this.modal = null, this.gate = null, this.isOpen = !1, this.activePayment = null, this.config = M(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;
248
57
  }
249
58
  async open(e) {
250
- if (await this.ready, this.gate && !this.gate.show) throw new O("GATE_REFUSED", this.gate.reason ?? "This site cannot accept Pybara payments yet.");
251
- let t = e.amount;
252
- if (!Number.isFinite(t) || t <= 0) throw new O("INVALID_CONFIG", `Invalid amount: ${e.amount}`);
253
- let n = e.currency ?? this.config.currency, r = t * (n === "USD" ? 1 : this.fiatRate);
254
- if (r < .5) throw new O("BELOW_MINIMUM", `Minimum payment is $${C} USD.`);
255
- let i = A(e);
256
- if (i && this.gate && this.gate.show && !this.gate.recurring) throw new O("GATE_REFUSED", "Recurring payments are temporarily unavailable.");
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 O("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: N(),
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 D(t, this.config.siteUrl, 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 ? A(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?.(P(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 A(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 O("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 j(e) {
213
+ function p(e) {
378
214
  let t = e.trim();
379
215
  if (!t) return null;
380
216
  let n;
@@ -385,32 +221,33 @@ function j(e) {
385
221
  }
386
222
  return n.protocol !== "https:" && n.protocol !== "http:" ? null : n.origin + n.pathname.replace(/\/+$/, "");
387
223
  }
388
- function M(e) {
389
- if (!e.recipientPrincipal) throw new O("INVALID_CONFIG", "Pybara.checkout: \"recipientPrincipal\" is required");
390
- if (!e.siteUrl) throw new O("INVALID_CONFIG", "Pybara.checkout: \"siteUrl\" is required");
391
- let t = j(e.siteUrl);
392
- if (!t) throw new O("INVALID_CONFIG", `Pybara.checkout: "siteUrl" is not a valid http(s) URL: ${e.siteUrl}`);
393
- if (!e.siteName) throw new O("INVALID_CONFIG", "Pybara.checkout: \"siteName\" is required");
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");
394
232
  return {
395
233
  ...e,
396
234
  siteUrl: t,
397
- currency: e.currency ?? "USD",
398
235
  staging: e.staging ?? !1,
399
236
  debug: e.debug ?? !1
400
237
  };
401
238
  }
402
- function N() {
239
+ function h() {
403
240
  let e = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
404
241
  return btoa(String.fromCharCode(...e));
405
242
  }
406
- function P(e) {
243
+ function g(e) {
407
244
  return e instanceof Error ? e : Error(String(e));
408
245
  }
409
246
  //#endregion
410
247
  //#region src/index.ts
411
- function F(e) {
412
- return new k(e);
248
+ function _(e) {
249
+ return new d(e);
413
250
  }
414
- var I = { checkout: F };
251
+ var v = { checkout: _ };
415
252
  //#endregion
416
- export { I as Pybara, k as PybaraCheckout, O as PybaraError, F as checkout };
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":"AAAA;;;;;GAKG;AAEH,8IAA8I;AAC9I,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEtE,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC7B,uKAAuK;IACvK,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gIAAgI;IAChI,OAAO,EAAE,MAAM,CAAC;IAChB,iHAAiH;IACjH,QAAQ,EAAE,MAAM,CAAC;IACjB,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2FAA2F;IAC3F,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,6KAA6K;IAC7K,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mHAAmH;IACnH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,wFAAwF;IACxF,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,gEAAgE;AAChE,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,sGAAsG;IACtG,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wHAAwH;IACxH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qDAAqD;IACrD,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,oEAAoE;IACpE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,wHAAwH;IACxH,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,WAAW,EAAE,OAAO,CAAC;IACrB,oCAAoC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GACvB,gBAAgB,GAChB,cAAc,GACd,qBAAqB,GACrB,eAAe,GACf,iBAAiB,CAAC;AAEtB,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;gBAEnB,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM;CAKnD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}