@billing-io/designs 1.0.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/billing.js +880 -0
- package/dist/brand.d.ts +35 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +43 -0
- package/dist/brand.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/overlay/constants.d.ts +60 -0
- package/dist/overlay/constants.d.ts.map +1 -0
- package/dist/overlay/constants.js +77 -0
- package/dist/overlay/constants.js.map +1 -0
- package/dist/overlay/index.d.ts +3 -0
- package/dist/overlay/index.d.ts.map +1 -0
- package/dist/overlay/index.js +2 -0
- package/dist/overlay/index.js.map +1 -0
- package/dist/plans.d.ts +82 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +277 -0
- package/dist/plans.js.map +1 -0
- package/package.json +61 -0
- package/src/billing-js/billing.js +876 -0
- package/src/brand.ts +62 -0
- package/src/index.ts +68 -0
- package/src/overlay/constants.ts +99 -0
- package/src/overlay/index.ts +17 -0
- package/src/plans.ts +359 -0
|
@@ -0,0 +1,876 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* billing.js — Checkout overlay for billing.io
|
|
3
|
+
*
|
|
4
|
+
* Load via:
|
|
5
|
+
* <script src="https://js.billing.io/v1/billing.js"></script>
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* billing.openCheckout({
|
|
9
|
+
* checkoutId: "co_123",
|
|
10
|
+
* variation: "centered", // "centered" | "panel" | "bottom" | "fullscreen" | "popup"
|
|
11
|
+
* onSuccess: ({ checkoutId, txHash }) => {},
|
|
12
|
+
* onClose: () => {},
|
|
13
|
+
* onError: (err) => {}
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* billing.redirectToCheckout({ checkoutId: "co_123" });
|
|
17
|
+
*/
|
|
18
|
+
(function () {
|
|
19
|
+
"use strict";
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Config
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
var BILLING_HOST = (function () {
|
|
26
|
+
if (document.currentScript && document.currentScript.getAttribute("data-host")) {
|
|
27
|
+
return document.currentScript.getAttribute("data-host");
|
|
28
|
+
}
|
|
29
|
+
if (window.__BILLING_HOST__) return window.__BILLING_HOST__;
|
|
30
|
+
if (document.currentScript && document.currentScript.src) {
|
|
31
|
+
try { return new URL(document.currentScript.src).origin; } catch (e) { /* noop */ }
|
|
32
|
+
}
|
|
33
|
+
return window.location.origin;
|
|
34
|
+
})();
|
|
35
|
+
|
|
36
|
+
var NAMESPACE = "billing";
|
|
37
|
+
var VERSION = "1.0.0";
|
|
38
|
+
var MSG_PREFIX = "billing:";
|
|
39
|
+
var Z_INDEX = 2147483647;
|
|
40
|
+
|
|
41
|
+
// Spring-like easing curves
|
|
42
|
+
var EASE_OUT_EXPO = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
43
|
+
var EASE_OUT_QUINT = "cubic-bezier(0.22, 1, 0.36, 1)";
|
|
44
|
+
var EASE_IN_EXPO = "cubic-bezier(0.7, 0, 0.84, 0)";
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// State
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
var _overlay = null;
|
|
51
|
+
var _iframe = null;
|
|
52
|
+
var _callbacks = {};
|
|
53
|
+
var _currentCheckoutId = null;
|
|
54
|
+
var _variation = parseInt(localStorage.getItem("billing_overlay_variation") || "1", 10);
|
|
55
|
+
var _isOpen = false;
|
|
56
|
+
var _lastStatus = null;
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Variation name ↔ number mapping
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
var VARIATION_NAMES = {
|
|
63
|
+
centered: 1, card: 1,
|
|
64
|
+
panel: 2, side: 2, sidepanel: 2,
|
|
65
|
+
bottom: 3, sheet: 3, bottomsheet: 3,
|
|
66
|
+
fullscreen: 4, full: 4, takeover: 4,
|
|
67
|
+
popup: 5, floating: 5, pill: 5,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function resolveVariation(v) {
|
|
71
|
+
if (typeof v === "number" && VARIATIONS[v]) return v;
|
|
72
|
+
if (typeof v === "string") {
|
|
73
|
+
var n = VARIATION_NAMES[v.toLowerCase().replace(/[\s_-]/g, "")];
|
|
74
|
+
if (n) return n;
|
|
75
|
+
}
|
|
76
|
+
return _variation;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Variation definitions
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
var VARIATIONS = {
|
|
84
|
+
// V1: Centered card (Stripe-like)
|
|
85
|
+
1: {
|
|
86
|
+
name: "Centered Card",
|
|
87
|
+
desc: "Classic centered modal, backdrop blur",
|
|
88
|
+
build: buildCenteredModal,
|
|
89
|
+
},
|
|
90
|
+
// V2: Right slide-in panel
|
|
91
|
+
2: {
|
|
92
|
+
name: "Side Panel",
|
|
93
|
+
desc: "Right-anchored slide-in sheet",
|
|
94
|
+
build: buildSidePanel,
|
|
95
|
+
},
|
|
96
|
+
// V3: Bottom sheet (mobile-first)
|
|
97
|
+
3: {
|
|
98
|
+
name: "Bottom Sheet",
|
|
99
|
+
desc: "Mobile-friendly bottom sheet",
|
|
100
|
+
build: buildBottomSheet,
|
|
101
|
+
},
|
|
102
|
+
// V4: Full-screen takeover
|
|
103
|
+
4: {
|
|
104
|
+
name: "Fullscreen",
|
|
105
|
+
desc: "Immersive full-screen overlay",
|
|
106
|
+
build: buildFullscreen,
|
|
107
|
+
},
|
|
108
|
+
// V5: Floating popup (compact)
|
|
109
|
+
5: {
|
|
110
|
+
name: "Floating Popup",
|
|
111
|
+
desc: "Compact bottom-right popup",
|
|
112
|
+
build: buildFloatingPopup,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// Shared helpers
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
function raf(fn) {
|
|
121
|
+
requestAnimationFrame(function () { requestAnimationFrame(fn); });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function styleIframe(iframe, css) {
|
|
125
|
+
iframe.style.cssText = css;
|
|
126
|
+
iframe.setAttribute("allow", "clipboard-write");
|
|
127
|
+
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-popups allow-forms allow-clipboard-write");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function lockScroll() {
|
|
131
|
+
document.body.style.overflow = "hidden";
|
|
132
|
+
document.body.style.touchAction = "none";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function unlockScroll() {
|
|
136
|
+
document.body.style.overflow = "";
|
|
137
|
+
document.body.style.touchAction = "";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function buildCloseButton(onClick) {
|
|
141
|
+
var btn = document.createElement("button");
|
|
142
|
+
btn.type = "button";
|
|
143
|
+
btn.setAttribute("aria-label", "Close checkout");
|
|
144
|
+
btn.style.cssText =
|
|
145
|
+
"width:32px;height:32px;border-radius:10px;border:none;" +
|
|
146
|
+
"background:#f3f4f6;cursor:pointer;display:flex;align-items:center;" +
|
|
147
|
+
"justify-content:center;transition:background 0.15s ease, transform 0.15s ease;" +
|
|
148
|
+
"color:#6b7280;font-family:-apple-system,BlinkMacSystemFont,sans-serif;" +
|
|
149
|
+
"outline:none;";
|
|
150
|
+
btn.innerHTML =
|
|
151
|
+
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">' +
|
|
152
|
+
'<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>';
|
|
153
|
+
btn.addEventListener("mouseenter", function () { btn.style.background = "#e5e7eb"; btn.style.transform = "scale(1.05)"; });
|
|
154
|
+
btn.addEventListener("mouseleave", function () { btn.style.background = "#f3f4f6"; btn.style.transform = "scale(1)"; });
|
|
155
|
+
btn.addEventListener("mousedown", function () { btn.style.transform = "scale(0.95)"; });
|
|
156
|
+
btn.addEventListener("mouseup", function () { btn.style.transform = "scale(1.05)"; });
|
|
157
|
+
btn.addEventListener("click", function (e) { e.stopPropagation(); onClick(); });
|
|
158
|
+
return btn;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function buildBrandBadge(size) {
|
|
162
|
+
var s = size || "md";
|
|
163
|
+
var dim = s === "sm" ? 18 : s === "lg" ? 24 : 20;
|
|
164
|
+
var br = s === "sm" ? 4 : s === "lg" ? 6 : 5;
|
|
165
|
+
return '<div style="width:' + dim + 'px;height:' + dim + 'px;background:#000;border-radius:' + br + 'px;display:flex;align-items:center;justify-content:center;overflow:hidden;">' +
|
|
166
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="' + dim + '" height="' + dim + '">' +
|
|
167
|
+
'<rect width="1024" height="1024" fill="#000"/>' +
|
|
168
|
+
'<path fill="#fff" d="M648.077,530.65c0,46.2-18.34,81.95-55,107.25-25.3,17.239-51.7,25.851-79.2,25.851-10.27,0-25.119-3.301-44.55-9.9-10.27-3.661-18.149-5.5-23.649-5.5-9.9,0-19.989,3.67-30.25,11-2.2,1.461-3.851,2.2-4.95,2.2-2.569,0-4.039-1.101-4.4-3.3-.369-2.2-.55-8.801-.55-19.801v-322.85c0-9.53-1.719-15.761-5.156-18.7-3.438-2.931-10.948-4.4-22.533-4.4-9.771,0-14.66-1.28-14.66-3.85s2.234-3.85,6.694-3.85c41.653,0,77.919-4.581,108.797-13.75l.559,8.8v162.25c14.618-20.161,34.899-30.25,60.853-30.25,31.427,0,57.285,11.095,77.567,33.274,20.281,22.189,30.43,50.695,30.43,85.525ZM566.127,532.851c0-71.131-14.85-106.7-44.55-106.7-16.5,0-28.6,7.7-36.3,23.1-2.939,5.87-4.675,12.745-5.225,20.625-.551,7.89-.825,30.345-.825,67.375v33.551c0,34.469,2.286,57.294,6.875,68.475,4.58,11.189,13.836,16.775,27.774,16.775,19.062,0,32.536-9.351,40.426-28.051,7.88-18.699,11.824-50.41,11.824-95.149Z"/>' +
|
|
169
|
+
'<path fill="#fff" d="M692.003,642.041c0,7.235-2.336,13.186-7,17.851-4.671,4.665-10.621,7-17.851,7s-13.3-2.396-18.2-7.175c-4.899-4.78-7.35-10.676-7.35-17.676,0-7.229,2.45-13.3,7.35-18.199s10.965-7.351,18.2-7.351c6.765,0,12.601,2.45,17.5,7.351,4.9,4.899,7.351,10.97,7.351,18.199Z"/>' +
|
|
170
|
+
'</svg></div>';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// V1: Centered Card — staged: backdrop fades+blurs → card rises from below
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
function buildCenteredModal(iframe, onClose) {
|
|
178
|
+
var root = document.createElement("div");
|
|
179
|
+
root.setAttribute("data-billing-overlay", "");
|
|
180
|
+
root.style.cssText =
|
|
181
|
+
"position:fixed;inset:0;z-index:" + Z_INDEX +
|
|
182
|
+
";display:flex;align-items:center;justify-content:center;padding:16px;";
|
|
183
|
+
|
|
184
|
+
// Backdrop — starts transparent + no blur
|
|
185
|
+
var backdrop = document.createElement("div");
|
|
186
|
+
backdrop.style.cssText =
|
|
187
|
+
"position:absolute;inset:0;" +
|
|
188
|
+
"background:rgba(0,0,0,0);" +
|
|
189
|
+
"backdrop-filter:blur(0px);-webkit-backdrop-filter:blur(0px);" +
|
|
190
|
+
"transition:background 0.35s ease, backdrop-filter 0.4s ease, -webkit-backdrop-filter 0.4s ease;";
|
|
191
|
+
backdrop.addEventListener("click", onClose);
|
|
192
|
+
root.appendChild(backdrop);
|
|
193
|
+
|
|
194
|
+
// Card — starts invisible, below, scaled down
|
|
195
|
+
var card = document.createElement("div");
|
|
196
|
+
card.style.cssText =
|
|
197
|
+
"position:relative;width:100%;max-width:460px;max-height:680px;" +
|
|
198
|
+
"border-radius:20px;overflow:hidden;background:#fff;" +
|
|
199
|
+
"box-shadow:0 0 0 1px rgba(0,0,0,0.04);" +
|
|
200
|
+
"opacity:0;transform:translateY(24px) scale(0.97);" +
|
|
201
|
+
"transition:opacity 0.4s ease, transform 0.5s " + EASE_OUT_EXPO + ", box-shadow 0.5s ease;";
|
|
202
|
+
|
|
203
|
+
// No close button on the card itself — the checkout page header already
|
|
204
|
+
// occupies that corner (Secure badge). Close via backdrop click or Escape.
|
|
205
|
+
|
|
206
|
+
styleIframe(iframe, "width:100%;height:100%;min-height:600px;border:none;display:block;");
|
|
207
|
+
card.appendChild(iframe);
|
|
208
|
+
root.appendChild(card);
|
|
209
|
+
|
|
210
|
+
// Stage 1 (0ms): backdrop fades in + blur
|
|
211
|
+
raf(function () {
|
|
212
|
+
backdrop.style.background = "rgba(0,0,0,0.4)";
|
|
213
|
+
backdrop.style.backdropFilter = "blur(12px)";
|
|
214
|
+
backdrop.style.webkitBackdropFilter = "blur(12px)";
|
|
215
|
+
|
|
216
|
+
// Stage 2 (120ms delay): card rises in
|
|
217
|
+
setTimeout(function () {
|
|
218
|
+
card.style.opacity = "1";
|
|
219
|
+
card.style.transform = "translateY(0) scale(1)";
|
|
220
|
+
card.style.boxShadow = "0 25px 60px -12px rgba(0,0,0,0.25), 0 0 0 1px rgba(0,0,0,0.05)";
|
|
221
|
+
}, 120);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
root._animateOut = function (cb) {
|
|
225
|
+
card.style.opacity = "0";
|
|
226
|
+
card.style.transform = "translateY(12px) scale(0.98)";
|
|
227
|
+
card.style.boxShadow = "0 0 0 1px rgba(0,0,0,0.04)";
|
|
228
|
+
|
|
229
|
+
setTimeout(function () {
|
|
230
|
+
backdrop.style.background = "rgba(0,0,0,0)";
|
|
231
|
+
backdrop.style.backdropFilter = "blur(0px)";
|
|
232
|
+
backdrop.style.webkitBackdropFilter = "blur(0px)";
|
|
233
|
+
}, 80);
|
|
234
|
+
|
|
235
|
+
setTimeout(cb, 380);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
return root;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// V2: Side Panel — staged: backdrop dims → panel slides in from right
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
|
|
245
|
+
function buildSidePanel(iframe, onClose) {
|
|
246
|
+
var root = document.createElement("div");
|
|
247
|
+
root.setAttribute("data-billing-overlay", "");
|
|
248
|
+
root.style.cssText = "position:fixed;inset:0;z-index:" + Z_INDEX + ";";
|
|
249
|
+
|
|
250
|
+
// Backdrop
|
|
251
|
+
var backdrop = document.createElement("div");
|
|
252
|
+
backdrop.style.cssText =
|
|
253
|
+
"position:absolute;inset:0;background:rgba(0,0,0,0);" +
|
|
254
|
+
"backdrop-filter:blur(0px);-webkit-backdrop-filter:blur(0px);" +
|
|
255
|
+
"transition:background 0.4s ease, backdrop-filter 0.45s ease, -webkit-backdrop-filter 0.45s ease;";
|
|
256
|
+
backdrop.addEventListener("click", onClose);
|
|
257
|
+
root.appendChild(backdrop);
|
|
258
|
+
|
|
259
|
+
// Panel
|
|
260
|
+
var panel = document.createElement("div");
|
|
261
|
+
panel.style.cssText =
|
|
262
|
+
"position:absolute;top:0;right:0;bottom:0;width:460px;max-width:100%;" +
|
|
263
|
+
"background:#fff;box-shadow:0 0 0 transparent;" +
|
|
264
|
+
"transform:translateX(100%);" +
|
|
265
|
+
"transition:transform 0.45s " + EASE_OUT_EXPO + ", box-shadow 0.45s ease;" +
|
|
266
|
+
"display:flex;flex-direction:column;";
|
|
267
|
+
|
|
268
|
+
// Header
|
|
269
|
+
var header = document.createElement("div");
|
|
270
|
+
header.style.cssText =
|
|
271
|
+
"display:flex;align-items:center;justify-content:space-between;" +
|
|
272
|
+
"padding:16px 20px;border-bottom:1px solid #e5e7eb;flex-shrink:0;background:#fff;" +
|
|
273
|
+
"opacity:0;transform:translateX(12px);" +
|
|
274
|
+
"transition:opacity 0.3s ease 0.25s, transform 0.4s " + EASE_OUT_EXPO + " 0.25s;";
|
|
275
|
+
|
|
276
|
+
var title = document.createElement("div");
|
|
277
|
+
title.style.cssText = "display:flex;align-items:center;gap:8px;";
|
|
278
|
+
title.innerHTML = buildBrandBadge("md") +
|
|
279
|
+
'<span style="font-size:14px;font-weight:600;color:#111827;font-family:-apple-system,BlinkMacSystemFont,sans-serif;">Secure Checkout</span>';
|
|
280
|
+
header.appendChild(title);
|
|
281
|
+
header.appendChild(buildCloseButton(onClose));
|
|
282
|
+
panel.appendChild(header);
|
|
283
|
+
|
|
284
|
+
styleIframe(iframe, "flex:1;width:100%;border:none;display:block;opacity:0;transition:opacity 0.35s ease 0.35s;");
|
|
285
|
+
panel.appendChild(iframe);
|
|
286
|
+
root.appendChild(panel);
|
|
287
|
+
|
|
288
|
+
// Stage 1: backdrop
|
|
289
|
+
raf(function () {
|
|
290
|
+
backdrop.style.background = "rgba(0,0,0,0.3)";
|
|
291
|
+
backdrop.style.backdropFilter = "blur(6px)";
|
|
292
|
+
backdrop.style.webkitBackdropFilter = "blur(6px)";
|
|
293
|
+
|
|
294
|
+
// Stage 2 (80ms): panel slides in
|
|
295
|
+
setTimeout(function () {
|
|
296
|
+
panel.style.transform = "translateX(0)";
|
|
297
|
+
panel.style.boxShadow = "-8px 0 40px rgba(0,0,0,0.12)";
|
|
298
|
+
header.style.opacity = "1";
|
|
299
|
+
header.style.transform = "translateX(0)";
|
|
300
|
+
iframe.style.opacity = "1";
|
|
301
|
+
}, 80);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
root._animateOut = function (cb) {
|
|
305
|
+
iframe.style.opacity = "0";
|
|
306
|
+
iframe.style.transition = "opacity 0.15s ease";
|
|
307
|
+
header.style.opacity = "0";
|
|
308
|
+
header.style.transform = "translateX(8px)";
|
|
309
|
+
header.style.transition = "opacity 0.15s ease, transform 0.2s ease";
|
|
310
|
+
|
|
311
|
+
setTimeout(function () {
|
|
312
|
+
panel.style.transform = "translateX(100%)";
|
|
313
|
+
panel.style.boxShadow = "0 0 0 transparent";
|
|
314
|
+
backdrop.style.background = "rgba(0,0,0,0)";
|
|
315
|
+
backdrop.style.backdropFilter = "blur(0px)";
|
|
316
|
+
backdrop.style.webkitBackdropFilter = "blur(0px)";
|
|
317
|
+
}, 60);
|
|
318
|
+
|
|
319
|
+
setTimeout(cb, 420);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
return root;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
// V3: Bottom Sheet — staged: backdrop → sheet rises with spring
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
|
|
329
|
+
function buildBottomSheet(iframe, onClose) {
|
|
330
|
+
var root = document.createElement("div");
|
|
331
|
+
root.setAttribute("data-billing-overlay", "");
|
|
332
|
+
root.style.cssText =
|
|
333
|
+
"position:fixed;inset:0;z-index:" + Z_INDEX +
|
|
334
|
+
";display:flex;align-items:flex-end;justify-content:center;";
|
|
335
|
+
|
|
336
|
+
// Backdrop
|
|
337
|
+
var backdrop = document.createElement("div");
|
|
338
|
+
backdrop.style.cssText =
|
|
339
|
+
"position:absolute;inset:0;background:rgba(0,0,0,0);" +
|
|
340
|
+
"backdrop-filter:blur(0px);-webkit-backdrop-filter:blur(0px);" +
|
|
341
|
+
"transition:background 0.35s ease, backdrop-filter 0.4s ease, -webkit-backdrop-filter 0.4s ease;";
|
|
342
|
+
backdrop.addEventListener("click", onClose);
|
|
343
|
+
root.appendChild(backdrop);
|
|
344
|
+
|
|
345
|
+
// Sheet
|
|
346
|
+
var sheet = document.createElement("div");
|
|
347
|
+
sheet.style.cssText =
|
|
348
|
+
"position:relative;width:100%;max-width:500px;max-height:92vh;" +
|
|
349
|
+
"border-radius:20px 20px 0 0;background:#fff;" +
|
|
350
|
+
"box-shadow:0 0 0 transparent;" +
|
|
351
|
+
"transform:translateY(105%);" +
|
|
352
|
+
"transition:transform 0.5s " + EASE_OUT_EXPO + ", box-shadow 0.5s ease;" +
|
|
353
|
+
"display:flex;flex-direction:column;overflow:hidden;";
|
|
354
|
+
|
|
355
|
+
// Drag handle
|
|
356
|
+
var handle = document.createElement("div");
|
|
357
|
+
handle.style.cssText =
|
|
358
|
+
"flex-shrink:0;display:flex;justify-content:center;padding:12px 0 4px;" +
|
|
359
|
+
"opacity:0;transition:opacity 0.3s ease 0.3s;";
|
|
360
|
+
handle.innerHTML = '<div style="width:36px;height:4px;border-radius:2px;background:#d1d5db;"></div>';
|
|
361
|
+
sheet.appendChild(handle);
|
|
362
|
+
|
|
363
|
+
// No close button overlaying the iframe — close via drag handle area,
|
|
364
|
+
// backdrop click, or Escape key. Keeps the checkout header clean.
|
|
365
|
+
|
|
366
|
+
styleIframe(iframe, "flex:1;width:100%;border:none;display:block;min-height:580px;");
|
|
367
|
+
sheet.appendChild(iframe);
|
|
368
|
+
root.appendChild(sheet);
|
|
369
|
+
|
|
370
|
+
// Stage 1: backdrop
|
|
371
|
+
raf(function () {
|
|
372
|
+
backdrop.style.background = "rgba(0,0,0,0.4)";
|
|
373
|
+
backdrop.style.backdropFilter = "blur(12px)";
|
|
374
|
+
backdrop.style.webkitBackdropFilter = "blur(12px)";
|
|
375
|
+
|
|
376
|
+
// Stage 2 (100ms): sheet springs up
|
|
377
|
+
setTimeout(function () {
|
|
378
|
+
sheet.style.transform = "translateY(0)";
|
|
379
|
+
sheet.style.boxShadow = "0 -10px 50px rgba(0,0,0,0.15)";
|
|
380
|
+
handle.style.opacity = "1";
|
|
381
|
+
}, 100);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
root._animateOut = function (cb) {
|
|
385
|
+
handle.style.opacity = "0";
|
|
386
|
+
handle.style.transition = "opacity 0.1s ease";
|
|
387
|
+
|
|
388
|
+
setTimeout(function () {
|
|
389
|
+
sheet.style.transform = "translateY(105%)";
|
|
390
|
+
sheet.style.boxShadow = "0 0 0 transparent";
|
|
391
|
+
}, 50);
|
|
392
|
+
|
|
393
|
+
setTimeout(function () {
|
|
394
|
+
backdrop.style.background = "rgba(0,0,0,0)";
|
|
395
|
+
backdrop.style.backdropFilter = "blur(0px)";
|
|
396
|
+
backdrop.style.webkitBackdropFilter = "blur(0px)";
|
|
397
|
+
}, 150);
|
|
398
|
+
|
|
399
|
+
setTimeout(cb, 450);
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
return root;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ---------------------------------------------------------------------------
|
|
406
|
+
// V4: Fullscreen — staged: bg wipes in → top bar drops → card fades up
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
|
|
409
|
+
function buildFullscreen(iframe, onClose) {
|
|
410
|
+
var root = document.createElement("div");
|
|
411
|
+
root.setAttribute("data-billing-overlay", "");
|
|
412
|
+
root.style.cssText =
|
|
413
|
+
"position:fixed;inset:0;z-index:" + Z_INDEX +
|
|
414
|
+
";background:rgba(247,248,250,0);display:flex;flex-direction:column;" +
|
|
415
|
+
"transition:background 0.4s ease;";
|
|
416
|
+
|
|
417
|
+
// Top bar
|
|
418
|
+
var topBar = document.createElement("div");
|
|
419
|
+
topBar.style.cssText =
|
|
420
|
+
"flex-shrink:0;display:flex;align-items:center;justify-content:space-between;" +
|
|
421
|
+
"padding:16px 24px;background:#fff;border-bottom:1px solid #e5e7eb;" +
|
|
422
|
+
"opacity:0;transform:translateY(-100%);" +
|
|
423
|
+
"transition:opacity 0.35s ease 0.15s, transform 0.45s " + EASE_OUT_EXPO + " 0.15s;";
|
|
424
|
+
|
|
425
|
+
var brand = document.createElement("div");
|
|
426
|
+
brand.style.cssText = "display:flex;align-items:center;gap:10px;";
|
|
427
|
+
brand.innerHTML = buildBrandBadge("lg") +
|
|
428
|
+
'<span style="font-size:15px;font-weight:600;color:#111827;font-family:-apple-system,BlinkMacSystemFont,sans-serif;">billing.io checkout</span>';
|
|
429
|
+
topBar.appendChild(brand);
|
|
430
|
+
|
|
431
|
+
var rightGroup = document.createElement("div");
|
|
432
|
+
rightGroup.style.cssText = "display:flex;align-items:center;gap:12px;";
|
|
433
|
+
|
|
434
|
+
var secureBadge = document.createElement("div");
|
|
435
|
+
secureBadge.style.cssText =
|
|
436
|
+
"display:flex;align-items:center;gap:6px;padding:6px 12px;" +
|
|
437
|
+
"background:#f3f4f6;border-radius:20px;font-size:12px;color:#4b5563;" +
|
|
438
|
+
"font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-weight:500;";
|
|
439
|
+
secureBadge.innerHTML =
|
|
440
|
+
'<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">' +
|
|
441
|
+
'<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>' +
|
|
442
|
+
'<span>Secure</span>';
|
|
443
|
+
rightGroup.appendChild(secureBadge);
|
|
444
|
+
rightGroup.appendChild(buildCloseButton(onClose));
|
|
445
|
+
topBar.appendChild(rightGroup);
|
|
446
|
+
root.appendChild(topBar);
|
|
447
|
+
|
|
448
|
+
// Iframe wrapper
|
|
449
|
+
var wrapper = document.createElement("div");
|
|
450
|
+
wrapper.style.cssText =
|
|
451
|
+
"flex:1;display:flex;align-items:center;justify-content:center;padding:24px;overflow:auto;";
|
|
452
|
+
|
|
453
|
+
var container = document.createElement("div");
|
|
454
|
+
container.style.cssText =
|
|
455
|
+
"width:100%;max-width:460px;height:100%;max-height:680px;" +
|
|
456
|
+
"border-radius:20px;overflow:hidden;background:#fff;" +
|
|
457
|
+
"box-shadow:0 0 0 1px rgba(0,0,0,0.03);" +
|
|
458
|
+
"opacity:0;transform:translateY(16px) scale(0.98);" +
|
|
459
|
+
"transition:opacity 0.45s ease 0.25s, transform 0.55s " + EASE_OUT_EXPO + " 0.25s, box-shadow 0.5s ease 0.25s;";
|
|
460
|
+
|
|
461
|
+
styleIframe(iframe, "width:100%;height:100%;border:none;display:block;");
|
|
462
|
+
container.appendChild(iframe);
|
|
463
|
+
wrapper.appendChild(container);
|
|
464
|
+
root.appendChild(wrapper);
|
|
465
|
+
|
|
466
|
+
// Stage 1: background wipe
|
|
467
|
+
raf(function () {
|
|
468
|
+
root.style.background = "rgba(247,248,250,1)";
|
|
469
|
+
|
|
470
|
+
// Stage 2 (100ms): top bar drops in
|
|
471
|
+
setTimeout(function () {
|
|
472
|
+
topBar.style.opacity = "1";
|
|
473
|
+
topBar.style.transform = "translateY(0)";
|
|
474
|
+
}, 100);
|
|
475
|
+
|
|
476
|
+
// Stage 3 (200ms): card rises
|
|
477
|
+
setTimeout(function () {
|
|
478
|
+
container.style.opacity = "1";
|
|
479
|
+
container.style.transform = "translateY(0) scale(1)";
|
|
480
|
+
container.style.boxShadow = "0 10px 40px -10px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.04)";
|
|
481
|
+
}, 200);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
root._animateOut = function (cb) {
|
|
485
|
+
container.style.opacity = "0";
|
|
486
|
+
container.style.transform = "translateY(8px) scale(0.99)";
|
|
487
|
+
container.style.transition = "opacity 0.2s ease, transform 0.25s ease";
|
|
488
|
+
|
|
489
|
+
setTimeout(function () {
|
|
490
|
+
topBar.style.opacity = "0";
|
|
491
|
+
topBar.style.transform = "translateY(-20px)";
|
|
492
|
+
topBar.style.transition = "opacity 0.2s ease, transform 0.25s ease";
|
|
493
|
+
}, 80);
|
|
494
|
+
|
|
495
|
+
setTimeout(function () {
|
|
496
|
+
root.style.background = "rgba(247,248,250,0)";
|
|
497
|
+
}, 150);
|
|
498
|
+
|
|
499
|
+
setTimeout(cb, 400);
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
return root;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// ---------------------------------------------------------------------------
|
|
506
|
+
// V5: Floating Popup — staged: pops up from bottom-right with bounce
|
|
507
|
+
// ---------------------------------------------------------------------------
|
|
508
|
+
|
|
509
|
+
function buildFloatingPopup(iframe, onClose) {
|
|
510
|
+
var root = document.createElement("div");
|
|
511
|
+
root.setAttribute("data-billing-overlay", "");
|
|
512
|
+
root.style.cssText = "position:fixed;inset:0;z-index:" + Z_INDEX + ";pointer-events:none;";
|
|
513
|
+
|
|
514
|
+
// Light scrim
|
|
515
|
+
var scrim = document.createElement("div");
|
|
516
|
+
scrim.style.cssText =
|
|
517
|
+
"position:absolute;inset:0;background:rgba(0,0,0,0);pointer-events:auto;" +
|
|
518
|
+
"transition:background 0.35s ease;";
|
|
519
|
+
scrim.addEventListener("click", onClose);
|
|
520
|
+
root.appendChild(scrim);
|
|
521
|
+
|
|
522
|
+
// Popup
|
|
523
|
+
var popup = document.createElement("div");
|
|
524
|
+
popup.style.cssText =
|
|
525
|
+
"position:absolute;bottom:24px;right:24px;" +
|
|
526
|
+
"width:420px;height:640px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);" +
|
|
527
|
+
"border-radius:16px;background:#fff;overflow:hidden;pointer-events:auto;" +
|
|
528
|
+
"box-shadow:0 0 0 1px rgba(0,0,0,0.04);" +
|
|
529
|
+
"opacity:0;transform:translateY(30px) scale(0.92);" +
|
|
530
|
+
"transition:opacity 0.35s ease, transform 0.5s " + EASE_OUT_EXPO + ", box-shadow 0.5s ease;" +
|
|
531
|
+
"display:flex;flex-direction:column;";
|
|
532
|
+
|
|
533
|
+
// Mini header
|
|
534
|
+
var header = document.createElement("div");
|
|
535
|
+
header.style.cssText =
|
|
536
|
+
"flex-shrink:0;display:flex;align-items:center;justify-content:space-between;" +
|
|
537
|
+
"padding:12px 16px;border-bottom:1px solid #f3f4f6;background:#fff;" +
|
|
538
|
+
"opacity:0;transition:opacity 0.25s ease 0.3s;";
|
|
539
|
+
|
|
540
|
+
var titleArea = document.createElement("div");
|
|
541
|
+
titleArea.style.cssText = "display:flex;align-items:center;gap:8px;";
|
|
542
|
+
titleArea.innerHTML = buildBrandBadge("sm") +
|
|
543
|
+
'<span style="font-size:13px;font-weight:600;color:#111827;font-family:-apple-system,BlinkMacSystemFont,sans-serif;">Checkout</span>';
|
|
544
|
+
header.appendChild(titleArea);
|
|
545
|
+
|
|
546
|
+
var closeBtn = buildCloseButton(onClose);
|
|
547
|
+
closeBtn.style.cssText =
|
|
548
|
+
"width:28px;height:28px;border-radius:8px;border:none;background:#f3f4f6;" +
|
|
549
|
+
"cursor:pointer;display:flex;align-items:center;justify-content:center;" +
|
|
550
|
+
"transition:background 0.15s ease, transform 0.15s ease;color:#6b7280;" +
|
|
551
|
+
"outline:none;";
|
|
552
|
+
closeBtn.innerHTML =
|
|
553
|
+
'<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">' +
|
|
554
|
+
'<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>';
|
|
555
|
+
header.appendChild(closeBtn);
|
|
556
|
+
popup.appendChild(header);
|
|
557
|
+
|
|
558
|
+
styleIframe(iframe, "flex:1;width:100%;border:none;display:block;");
|
|
559
|
+
popup.appendChild(iframe);
|
|
560
|
+
root.appendChild(popup);
|
|
561
|
+
|
|
562
|
+
// Stage 1: light scrim
|
|
563
|
+
raf(function () {
|
|
564
|
+
scrim.style.background = "rgba(0,0,0,0.08)";
|
|
565
|
+
|
|
566
|
+
// Stage 2 (60ms): popup springs up
|
|
567
|
+
setTimeout(function () {
|
|
568
|
+
popup.style.opacity = "1";
|
|
569
|
+
popup.style.transform = "translateY(0) scale(1)";
|
|
570
|
+
popup.style.boxShadow = "0 20px 50px -10px rgba(0,0,0,0.25), 0 0 0 1px rgba(0,0,0,0.06)";
|
|
571
|
+
header.style.opacity = "1";
|
|
572
|
+
}, 60);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
root._animateOut = function (cb) {
|
|
576
|
+
header.style.opacity = "0";
|
|
577
|
+
header.style.transition = "opacity 0.12s ease";
|
|
578
|
+
|
|
579
|
+
setTimeout(function () {
|
|
580
|
+
popup.style.opacity = "0";
|
|
581
|
+
popup.style.transform = "translateY(20px) scale(0.95)";
|
|
582
|
+
popup.style.transition = "opacity 0.2s ease, transform 0.25s " + EASE_IN_EXPO;
|
|
583
|
+
scrim.style.background = "rgba(0,0,0,0)";
|
|
584
|
+
}, 50);
|
|
585
|
+
|
|
586
|
+
setTimeout(cb, 300);
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
return root;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// ---------------------------------------------------------------------------
|
|
593
|
+
// Dev-only variation switcher
|
|
594
|
+
// ---------------------------------------------------------------------------
|
|
595
|
+
|
|
596
|
+
function buildVariationSwitcher() {
|
|
597
|
+
if (typeof localStorage === "undefined") return;
|
|
598
|
+
var isDev =
|
|
599
|
+
window.location.hostname === "localhost" ||
|
|
600
|
+
window.location.hostname === "127.0.0.1" ||
|
|
601
|
+
(document.currentScript && document.currentScript.hasAttribute("data-dev"));
|
|
602
|
+
if (!isDev) return;
|
|
603
|
+
|
|
604
|
+
var existing = document.getElementById("billing-variation-switcher");
|
|
605
|
+
if (existing) existing.remove();
|
|
606
|
+
|
|
607
|
+
var switcher = document.createElement("div");
|
|
608
|
+
switcher.id = "billing-variation-switcher";
|
|
609
|
+
switcher.style.cssText =
|
|
610
|
+
"position:fixed;top:16px;left:16px;z-index:" + (Z_INDEX - 1) +
|
|
611
|
+
";background:#fff;border-radius:14px;padding:14px 16px;" +
|
|
612
|
+
"box-shadow:0 4px 24px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.06);" +
|
|
613
|
+
"font-family:-apple-system,BlinkMacSystemFont,Inter,sans-serif;font-size:13px;" +
|
|
614
|
+
"max-width:260px;";
|
|
615
|
+
|
|
616
|
+
var title = document.createElement("div");
|
|
617
|
+
title.style.cssText = "font-weight:600;color:#111827;margin-bottom:10px;font-size:11px;text-transform:uppercase;letter-spacing:0.6px;";
|
|
618
|
+
title.textContent = "Overlay Variation";
|
|
619
|
+
switcher.appendChild(title);
|
|
620
|
+
|
|
621
|
+
var list = document.createElement("div");
|
|
622
|
+
list.style.cssText = "display:flex;flex-direction:column;gap:4px;";
|
|
623
|
+
|
|
624
|
+
Object.keys(VARIATIONS).forEach(function (key) {
|
|
625
|
+
var v = VARIATIONS[key];
|
|
626
|
+
var btn = document.createElement("button");
|
|
627
|
+
btn.type = "button";
|
|
628
|
+
var isActive = parseInt(key, 10) === _variation;
|
|
629
|
+
btn.style.cssText =
|
|
630
|
+
"display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:8px;" +
|
|
631
|
+
"border:1.5px solid " + (isActive ? "#111827" : "#e5e7eb") + ";" +
|
|
632
|
+
"background:" + (isActive ? "#111827" : "#fff") + ";" +
|
|
633
|
+
"color:" + (isActive ? "#fff" : "#374151") + ";" +
|
|
634
|
+
"cursor:pointer;font-size:12px;font-family:inherit;text-align:left;" +
|
|
635
|
+
"transition:all 0.2s ease;width:100%;outline:none;";
|
|
636
|
+
btn.innerHTML =
|
|
637
|
+
'<span style="font-weight:700;min-width:14px;">' + key + ".</span> " +
|
|
638
|
+
'<span>' + v.name + '</span>' +
|
|
639
|
+
'<span style="margin-left:auto;font-size:10px;opacity:0.5;">' + v.desc + '</span>';
|
|
640
|
+
btn.addEventListener("mouseenter", function () {
|
|
641
|
+
if (!isActive) { btn.style.borderColor = "#9ca3af"; btn.style.background = "#f9fafb"; }
|
|
642
|
+
});
|
|
643
|
+
btn.addEventListener("mouseleave", function () {
|
|
644
|
+
if (!isActive) { btn.style.borderColor = "#e5e7eb"; btn.style.background = "#fff"; }
|
|
645
|
+
});
|
|
646
|
+
btn.addEventListener("click", function () {
|
|
647
|
+
_variation = parseInt(key, 10);
|
|
648
|
+
localStorage.setItem("billing_overlay_variation", key);
|
|
649
|
+
buildVariationSwitcher();
|
|
650
|
+
if (_isOpen && _currentCheckoutId) {
|
|
651
|
+
var cbs = Object.assign({}, _callbacks);
|
|
652
|
+
closeOverlay(true);
|
|
653
|
+
setTimeout(function () {
|
|
654
|
+
openCheckout(Object.assign({ checkoutId: _currentCheckoutId }, cbs));
|
|
655
|
+
}, 150);
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
list.appendChild(btn);
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
switcher.appendChild(list);
|
|
662
|
+
|
|
663
|
+
var hint = document.createElement("div");
|
|
664
|
+
hint.style.cssText = "margin-top:10px;font-size:10px;color:#9ca3af;line-height:1.4;";
|
|
665
|
+
hint.textContent = "Dev only \u2014 not shown in production";
|
|
666
|
+
switcher.appendChild(hint);
|
|
667
|
+
|
|
668
|
+
document.body.appendChild(switcher);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// ---------------------------------------------------------------------------
|
|
672
|
+
// PostMessage listener
|
|
673
|
+
// ---------------------------------------------------------------------------
|
|
674
|
+
|
|
675
|
+
function handleMessage(event) {
|
|
676
|
+
var data;
|
|
677
|
+
try {
|
|
678
|
+
if (typeof event.data === "string") {
|
|
679
|
+
if (event.data.indexOf(MSG_PREFIX) !== 0) return;
|
|
680
|
+
data = JSON.parse(event.data.slice(MSG_PREFIX.length));
|
|
681
|
+
} else if (event.data && event.data.type && typeof event.data.type === "string" && event.data.type.indexOf(MSG_PREFIX) === 0) {
|
|
682
|
+
data = event.data;
|
|
683
|
+
data.type = data.type.slice(MSG_PREFIX.length);
|
|
684
|
+
} else {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
} catch (e) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (!data || !data.type) return;
|
|
692
|
+
|
|
693
|
+
switch (data.type) {
|
|
694
|
+
case "status":
|
|
695
|
+
_lastStatus = data.status;
|
|
696
|
+
if (data.status === "confirmed" || data.status === "success") {
|
|
697
|
+
if (_callbacks.onSuccess) {
|
|
698
|
+
_callbacks.onSuccess({
|
|
699
|
+
checkoutId: _currentCheckoutId,
|
|
700
|
+
txHash: data.txHash || null,
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
setTimeout(function () { closeOverlay(); }, 2500);
|
|
704
|
+
}
|
|
705
|
+
break;
|
|
706
|
+
|
|
707
|
+
case "error":
|
|
708
|
+
if (_callbacks.onError) {
|
|
709
|
+
_callbacks.onError({
|
|
710
|
+
message: data.message || "Checkout error",
|
|
711
|
+
code: data.code || "unknown",
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
break;
|
|
715
|
+
|
|
716
|
+
case "loaded":
|
|
717
|
+
if (_iframe) _iframe.style.opacity = "1";
|
|
718
|
+
break;
|
|
719
|
+
|
|
720
|
+
case "close":
|
|
721
|
+
closeOverlay();
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// ---------------------------------------------------------------------------
|
|
727
|
+
// Core API
|
|
728
|
+
// ---------------------------------------------------------------------------
|
|
729
|
+
|
|
730
|
+
function openCheckout(options) {
|
|
731
|
+
if (!options || !options.checkoutId) {
|
|
732
|
+
throw new Error("billing.openCheckout: checkoutId is required");
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// If already open with same checkout, bring to front
|
|
736
|
+
if (_isOpen && _currentCheckoutId === options.checkoutId && _overlay) {
|
|
737
|
+
_overlay.style.display = "";
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (_isOpen) {
|
|
742
|
+
closeOverlay(true);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
_currentCheckoutId = options.checkoutId;
|
|
746
|
+
_callbacks = {
|
|
747
|
+
onSuccess: options.onSuccess || null,
|
|
748
|
+
onClose: options.onClose || null,
|
|
749
|
+
onError: options.onError || null,
|
|
750
|
+
};
|
|
751
|
+
_lastStatus = null;
|
|
752
|
+
|
|
753
|
+
// Resolve which variation to use
|
|
754
|
+
// Priority: options.variation > dev switcher > default
|
|
755
|
+
var variationNum = options.variation
|
|
756
|
+
? resolveVariation(options.variation)
|
|
757
|
+
: _variation;
|
|
758
|
+
|
|
759
|
+
// Build iframe
|
|
760
|
+
var url = BILLING_HOST + "/checkout/" + encodeURIComponent(options.checkoutId);
|
|
761
|
+
url += (url.indexOf("?") === -1 ? "?" : "&") + "embed=1";
|
|
762
|
+
|
|
763
|
+
_iframe = document.createElement("iframe");
|
|
764
|
+
_iframe.src = url;
|
|
765
|
+
_iframe.setAttribute("title", "billing.io Secure Checkout");
|
|
766
|
+
_iframe.style.opacity = "0";
|
|
767
|
+
_iframe.style.transition = "opacity 0.4s ease";
|
|
768
|
+
_iframe.addEventListener("load", function () {
|
|
769
|
+
_iframe.style.opacity = "1";
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
// Build overlay
|
|
773
|
+
var variation = VARIATIONS[variationNum] || VARIATIONS[1];
|
|
774
|
+
_overlay = variation.build(_iframe, function () { closeOverlay(); });
|
|
775
|
+
|
|
776
|
+
document.body.appendChild(_overlay);
|
|
777
|
+
lockScroll();
|
|
778
|
+
_isOpen = true;
|
|
779
|
+
|
|
780
|
+
window.addEventListener("message", handleMessage);
|
|
781
|
+
document.addEventListener("keydown", handleKeydown);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function closeOverlay(silent) {
|
|
785
|
+
if (!_overlay) return;
|
|
786
|
+
|
|
787
|
+
var overlay = _overlay;
|
|
788
|
+
_isOpen = false;
|
|
789
|
+
|
|
790
|
+
if (overlay._animateOut) {
|
|
791
|
+
overlay._animateOut(function () {
|
|
792
|
+
if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
|
793
|
+
});
|
|
794
|
+
} else {
|
|
795
|
+
if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
unlockScroll();
|
|
799
|
+
|
|
800
|
+
if (!silent && _callbacks.onClose) {
|
|
801
|
+
_callbacks.onClose();
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
_overlay = null;
|
|
805
|
+
_iframe = null;
|
|
806
|
+
document.removeEventListener("keydown", handleKeydown);
|
|
807
|
+
window.removeEventListener("message", handleMessage);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function redirectToCheckout(options) {
|
|
811
|
+
if (!options || !options.checkoutId) {
|
|
812
|
+
throw new Error("billing.redirectToCheckout: checkoutId is required");
|
|
813
|
+
}
|
|
814
|
+
window.location.href = BILLING_HOST + "/checkout/" + encodeURIComponent(options.checkoutId);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function handleKeydown(e) {
|
|
818
|
+
if (e.key === "Escape") closeOverlay();
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// ---------------------------------------------------------------------------
|
|
822
|
+
// Public API
|
|
823
|
+
// ---------------------------------------------------------------------------
|
|
824
|
+
|
|
825
|
+
var billingApi = {
|
|
826
|
+
version: VERSION,
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Open the checkout overlay.
|
|
830
|
+
* @param {Object} options
|
|
831
|
+
* @param {string} options.checkoutId - The checkout ID (e.g. "co_123")
|
|
832
|
+
* @param {string|number} [options.variation] - Overlay style: "centered"|"panel"|"bottom"|"fullscreen"|"popup" or 1-5
|
|
833
|
+
* @param {Function} [options.onSuccess] - Called with { checkoutId, txHash }
|
|
834
|
+
* @param {Function} [options.onClose] - Called when the overlay is closed
|
|
835
|
+
* @param {Function} [options.onError] - Called with { message, code }
|
|
836
|
+
*/
|
|
837
|
+
openCheckout: openCheckout,
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Redirect the page to the hosted checkout.
|
|
841
|
+
* @param {Object} options
|
|
842
|
+
* @param {string} options.checkoutId - The checkout ID
|
|
843
|
+
*/
|
|
844
|
+
redirectToCheckout: redirectToCheckout,
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Close the overlay programmatically.
|
|
848
|
+
*/
|
|
849
|
+
close: function () { closeOverlay(); },
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Set the default overlay variation (1-5). Dev use only.
|
|
853
|
+
* @param {number|string} v - Variation number or name
|
|
854
|
+
*/
|
|
855
|
+
setVariation: function (v) {
|
|
856
|
+
var n = resolveVariation(v);
|
|
857
|
+
if (VARIATIONS[n]) {
|
|
858
|
+
_variation = n;
|
|
859
|
+
localStorage.setItem("billing_overlay_variation", String(n));
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
|
|
863
|
+
/** Get current default variation number. */
|
|
864
|
+
getVariation: function () { return _variation; },
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
if (typeof window !== "undefined") {
|
|
868
|
+
window[NAMESPACE] = billingApi;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (document.readyState === "loading") {
|
|
872
|
+
document.addEventListener("DOMContentLoaded", buildVariationSwitcher);
|
|
873
|
+
} else {
|
|
874
|
+
buildVariationSwitcher();
|
|
875
|
+
}
|
|
876
|
+
})();
|