@mj-biz-apps/orders-server 0.0.1 → 5.2.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/LICENSE +183 -0
- package/README.md +47 -2
- package/dist/CheckoutServerExtension.d.ts +96 -0
- package/dist/CheckoutServerExtension.d.ts.map +1 -0
- package/dist/CheckoutServerExtension.js +554 -0
- package/dist/CheckoutServerExtension.js.map +1 -0
- package/dist/PaymentWebhookExtension.d.ts +17 -0
- package/dist/PaymentWebhookExtension.d.ts.map +1 -0
- package/dist/PaymentWebhookExtension.js +151 -0
- package/dist/PaymentWebhookExtension.js.map +1 -0
- package/dist/__tests__/CheckoutServerExtension.test.d.ts +2 -0
- package/dist/__tests__/CheckoutServerExtension.test.d.ts.map +1 -0
- package/dist/__tests__/CheckoutServerExtension.test.js +257 -0
- package/dist/__tests__/CheckoutServerExtension.test.js.map +1 -0
- package/dist/__tests__/checkout-edge-policy.test.d.ts +2 -0
- package/dist/__tests__/checkout-edge-policy.test.d.ts.map +1 -0
- package/dist/__tests__/checkout-edge-policy.test.js +70 -0
- package/dist/__tests__/checkout-edge-policy.test.js.map +1 -0
- package/dist/__tests__/checkout-host-page.test.d.ts +2 -0
- package/dist/__tests__/checkout-host-page.test.d.ts.map +1 -0
- package/dist/__tests__/checkout-host-page.test.js +86 -0
- package/dist/__tests__/checkout-host-page.test.js.map +1 -0
- package/dist/__tests__/server-extensions-manifest.test.d.ts +2 -0
- package/dist/__tests__/server-extensions-manifest.test.d.ts.map +1 -0
- package/dist/__tests__/server-extensions-manifest.test.js +29 -0
- package/dist/__tests__/server-extensions-manifest.test.js.map +1 -0
- package/dist/checkout-edge-policy.d.ts +44 -0
- package/dist/checkout-edge-policy.d.ts.map +1 -0
- package/dist/checkout-edge-policy.js +84 -0
- package/dist/checkout-edge-policy.js.map +1 -0
- package/dist/checkout-host-page.d.ts +43 -0
- package/dist/checkout-host-page.d.ts.map +1 -0
- package/dist/checkout-host-page.js +445 -0
- package/dist/checkout-host-page.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -0
- package/dist/custom/generate-invoice.action.d.ts +59 -0
- package/dist/custom/generate-invoice.action.d.ts.map +1 -0
- package/dist/custom/generate-invoice.action.js +208 -0
- package/dist/custom/generate-invoice.action.js.map +1 -0
- package/dist/custom/open-payment-intent.action.d.ts +39 -0
- package/dist/custom/open-payment-intent.action.d.ts.map +1 -0
- package/dist/custom/open-payment-intent.action.js +169 -0
- package/dist/custom/open-payment-intent.action.js.map +1 -0
- package/dist/custom/send-document.action.d.ts +78 -0
- package/dist/custom/send-document.action.d.ts.map +1 -0
- package/dist/custom/send-document.action.js +277 -0
- package/dist/custom/send-document.action.js.map +1 -0
- package/dist/generated/class-registrations-manifest.d.ts +7 -0
- package/dist/generated/class-registrations-manifest.d.ts.map +1 -0
- package/dist/generated/class-registrations-manifest.js +7 -0
- package/dist/generated/class-registrations-manifest.js.map +1 -0
- package/dist/generated/generated.d.ts +3536 -0
- package/dist/generated/generated.d.ts.map +1 -0
- package/dist/generated/generated.js +18371 -0
- package/dist/generated/generated.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +105 -0
- package/dist/index.js.map +1 -0
- package/dist/server-extensions-manifest.d.ts +14 -0
- package/dist/server-extensions-manifest.d.ts.map +1 -0
- package/dist/server-extensions-manifest.js +15 -0
- package/dist/server-extensions-manifest.js.map +1 -0
- package/dist/services/invoice-renderer.d.ts +80 -0
- package/dist/services/invoice-renderer.d.ts.map +1 -0
- package/dist/services/invoice-renderer.js +103 -0
- package/dist/services/invoice-renderer.js.map +1 -0
- package/package.json +65 -5
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public checkout host page — a self-contained HTML document that drives
|
|
3
|
+
* `OrdersCheckoutEdge` POSTs from a browser with no Explorer shell and no
|
|
4
|
+
* custom-element bundle.
|
|
5
|
+
*
|
|
6
|
+
* All per-request values reach the boot script via HTML-escaped `data-*`
|
|
7
|
+
* attributes (Forms host-page XSS rule): nothing attacker-controlled is
|
|
8
|
+
* interpolated into the inline `<script>`.
|
|
9
|
+
*/
|
|
10
|
+
/** Headers for the public checkout host / error pages (CSP is nonce-based). */
|
|
11
|
+
export function checkoutHostSecurityHeaders(nonce) {
|
|
12
|
+
const n = nonce.replace(/[^A-Za-z0-9+/=_-]/g, '');
|
|
13
|
+
const csp = [
|
|
14
|
+
"default-src 'none'",
|
|
15
|
+
"base-uri 'none'",
|
|
16
|
+
"form-action 'none'",
|
|
17
|
+
"frame-ancestors 'none'",
|
|
18
|
+
`script-src 'nonce-${n}' 'self' https://js.stripe.com`,
|
|
19
|
+
`style-src 'nonce-${n}' 'self'`,
|
|
20
|
+
"img-src 'self' data: https://*.stripe.com",
|
|
21
|
+
'frame-src https://js.stripe.com https://*.js.stripe.com https://hooks.stripe.com https://*.stripe.com https://*.hcaptcha.com https://newassets.hcaptcha.com',
|
|
22
|
+
"connect-src 'self' https://api.stripe.com https://m.stripe.network",
|
|
23
|
+
].join('; ');
|
|
24
|
+
return {
|
|
25
|
+
'Content-Security-Policy': csp,
|
|
26
|
+
'X-Frame-Options': 'DENY',
|
|
27
|
+
'Referrer-Policy': 'no-referrer',
|
|
28
|
+
'X-Content-Type-Options': 'nosniff',
|
|
29
|
+
'Cache-Control': 'no-store',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Escape a string for safe insertion into HTML text content. */
|
|
33
|
+
export function escapeHtml(value) {
|
|
34
|
+
return value
|
|
35
|
+
.replace(/&/g, '&')
|
|
36
|
+
.replace(/</g, '<')
|
|
37
|
+
.replace(/>/g, '>');
|
|
38
|
+
}
|
|
39
|
+
/** Escape a string for safe insertion into a double-quoted HTML attribute. */
|
|
40
|
+
export function escapeAttr(value) {
|
|
41
|
+
return escapeHtml(value).replace(/"/g, '"');
|
|
42
|
+
}
|
|
43
|
+
export function renderCheckoutHostPage(options) {
|
|
44
|
+
const title = escapeHtml(options.pageTitle ?? 'Checkout');
|
|
45
|
+
const slug = escapeAttr(options.slug);
|
|
46
|
+
const apiRoot = escapeAttr(options.apiRoot);
|
|
47
|
+
const nonceAttr = ` nonce="${escapeAttr(options.cspNonce)}"`;
|
|
48
|
+
const elementSrc = options.elementSrc ? escapeAttr(options.elementSrc) : '';
|
|
49
|
+
if (elementSrc) {
|
|
50
|
+
return `<!doctype html>
|
|
51
|
+
<html lang="en">
|
|
52
|
+
<head>
|
|
53
|
+
<meta charset="utf-8" />
|
|
54
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
55
|
+
<meta name="robots" content="noindex" />
|
|
56
|
+
<title>${title}</title>
|
|
57
|
+
<style${nonceAttr}>${PAGE_CSS}</style>
|
|
58
|
+
<script type="module" src="${elementSrc}"${nonceAttr}></script>
|
|
59
|
+
</head>
|
|
60
|
+
<body>
|
|
61
|
+
<main class="mj-co">
|
|
62
|
+
<mj-orders-checkout slug="${slug}" api-root="${apiRoot}" csp-nonce="${escapeAttr(options.cspNonce)}"></mj-orders-checkout>
|
|
63
|
+
</main>
|
|
64
|
+
</body>
|
|
65
|
+
</html>`;
|
|
66
|
+
}
|
|
67
|
+
return `<!doctype html>
|
|
68
|
+
<html lang="en">
|
|
69
|
+
<head>
|
|
70
|
+
<meta charset="utf-8" />
|
|
71
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
72
|
+
<meta name="robots" content="noindex" />
|
|
73
|
+
<title>${title}</title>
|
|
74
|
+
<style${nonceAttr}>${PAGE_CSS}</style>
|
|
75
|
+
</head>
|
|
76
|
+
<body>
|
|
77
|
+
<main class="mj-co" id="mj-co" data-slug="${slug}" data-api-root="${apiRoot}">
|
|
78
|
+
<p class="mj-co__status" role="status">Loading checkout…</p>
|
|
79
|
+
</main>
|
|
80
|
+
<script${nonceAttr}>${BOOT_SCRIPT}</script>
|
|
81
|
+
</body>
|
|
82
|
+
</html>`;
|
|
83
|
+
}
|
|
84
|
+
export function renderCheckoutHostErrorPage(options) {
|
|
85
|
+
const title = escapeHtml(options.pageTitle ?? 'Checkout unavailable');
|
|
86
|
+
const message = escapeHtml(options.message);
|
|
87
|
+
const nonceAttr = ` nonce="${escapeAttr(options.cspNonce)}"`;
|
|
88
|
+
return `<!doctype html>
|
|
89
|
+
<html lang="en">
|
|
90
|
+
<head>
|
|
91
|
+
<meta charset="utf-8" />
|
|
92
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
93
|
+
<meta name="robots" content="noindex" />
|
|
94
|
+
<title>${title}</title>
|
|
95
|
+
<style${nonceAttr}>${PAGE_CSS}</style>
|
|
96
|
+
</head>
|
|
97
|
+
<body>
|
|
98
|
+
<main class="mj-co">
|
|
99
|
+
<p class="mj-co__error" role="alert">${message}</p>
|
|
100
|
+
</main>
|
|
101
|
+
</body>
|
|
102
|
+
</html>`;
|
|
103
|
+
}
|
|
104
|
+
const PAGE_CSS = `
|
|
105
|
+
:root { color-scheme: light dark; }
|
|
106
|
+
* { box-sizing: border-box; }
|
|
107
|
+
html, body { margin: 0; padding: 0; min-height: 100%; }
|
|
108
|
+
body {
|
|
109
|
+
background: var(--mj-bg, #f8fafc);
|
|
110
|
+
color: var(--mj-text, #0f172a);
|
|
111
|
+
font-family: var(--mj-font-body, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
|
|
112
|
+
-webkit-text-size-adjust: 100%;
|
|
113
|
+
min-height: 100vh;
|
|
114
|
+
}
|
|
115
|
+
.mj-co {
|
|
116
|
+
max-width: 36rem;
|
|
117
|
+
margin: 0 auto;
|
|
118
|
+
padding: clamp(1.5rem, 5vw, 3rem) clamp(1rem, 4vw, 2rem);
|
|
119
|
+
}
|
|
120
|
+
.mj-co__status, .mj-co__error, .mj-co__ok {
|
|
121
|
+
font-size: 1.0625rem;
|
|
122
|
+
line-height: 1.5;
|
|
123
|
+
}
|
|
124
|
+
.mj-co__error { color: var(--mj-error, #b3261e); }
|
|
125
|
+
.mj-co__ok { color: var(--mj-ok, #0f7b3b); }
|
|
126
|
+
.mj-co h1 { font-size: 1.5rem; margin: 0 0 0.5rem; }
|
|
127
|
+
.mj-co p { margin: 0 0 1rem; }
|
|
128
|
+
.mj-co label { display: block; font-weight: 600; margin: 0.75rem 0 0.25rem; }
|
|
129
|
+
.mj-co input, .mj-co select, .mj-co textarea {
|
|
130
|
+
width: 100%;
|
|
131
|
+
padding: 0.5rem 0.65rem;
|
|
132
|
+
border: 1px solid var(--mj-border, #c9cdd4);
|
|
133
|
+
border-radius: 6px;
|
|
134
|
+
font: inherit;
|
|
135
|
+
background: var(--mj-surface, #fff);
|
|
136
|
+
color: inherit;
|
|
137
|
+
}
|
|
138
|
+
.mj-co button {
|
|
139
|
+
margin-top: 1.25rem;
|
|
140
|
+
padding: 0.65rem 1.1rem;
|
|
141
|
+
border: 0;
|
|
142
|
+
border-radius: 6px;
|
|
143
|
+
background: var(--mj-primary, #2563eb);
|
|
144
|
+
color: #fff;
|
|
145
|
+
font: inherit;
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
cursor: pointer;
|
|
148
|
+
}
|
|
149
|
+
.mj-co button[disabled] { opacity: 0.6; cursor: not-allowed; }
|
|
150
|
+
.mj-co__total { font-weight: 600; margin-top: 1rem; }
|
|
151
|
+
#mj-co-card { margin-top: 1rem; }
|
|
152
|
+
`;
|
|
153
|
+
/**
|
|
154
|
+
* Static boot script. Per-request values are read from `#mj-co` data-* attributes
|
|
155
|
+
* at runtime — never spliced into this string.
|
|
156
|
+
*/
|
|
157
|
+
const BOOT_SCRIPT = `
|
|
158
|
+
(function () {
|
|
159
|
+
var host = document.getElementById('mj-co');
|
|
160
|
+
var slug = host.getAttribute('data-slug') || '';
|
|
161
|
+
var apiRoot = (host.getAttribute('data-api-root') || '/checkout').replace(/\\/+$/, '');
|
|
162
|
+
|
|
163
|
+
function showError(msg) {
|
|
164
|
+
host.innerHTML = '';
|
|
165
|
+
var p = document.createElement('p');
|
|
166
|
+
p.className = 'mj-co__error';
|
|
167
|
+
p.setAttribute('role', 'alert');
|
|
168
|
+
p.textContent = msg;
|
|
169
|
+
host.appendChild(p);
|
|
170
|
+
}
|
|
171
|
+
function setStatus(msg) {
|
|
172
|
+
var el = host.querySelector('.mj-co__status');
|
|
173
|
+
if (el) el.textContent = msg;
|
|
174
|
+
}
|
|
175
|
+
function clientKey() {
|
|
176
|
+
var storageKey = 'mj-checkout-key:' + slug;
|
|
177
|
+
try {
|
|
178
|
+
var existing = sessionStorage.getItem(storageKey);
|
|
179
|
+
if (existing) return existing;
|
|
180
|
+
} catch (e) {}
|
|
181
|
+
var key;
|
|
182
|
+
if (window.crypto && crypto.randomUUID) {
|
|
183
|
+
key = crypto.randomUUID();
|
|
184
|
+
} else if (window.crypto && crypto.getRandomValues) {
|
|
185
|
+
var b = new Uint8Array(16);
|
|
186
|
+
crypto.getRandomValues(b);
|
|
187
|
+
b[6] = (b[6] & 0x0f) | 0x40;
|
|
188
|
+
b[8] = (b[8] & 0x3f) | 0x80;
|
|
189
|
+
var hex = [];
|
|
190
|
+
for (var i = 0; i < 16; i++) hex.push(('0' + b[i].toString(16)).slice(-2));
|
|
191
|
+
key = hex.slice(0, 4).join('') + '-' + hex.slice(4, 6).join('') + '-' + hex.slice(6, 8).join('') + '-' + hex.slice(8, 10).join('') + '-' + hex.slice(10, 16).join('');
|
|
192
|
+
} else {
|
|
193
|
+
showError('Checkout requires a secure random source. Open this page over HTTPS.');
|
|
194
|
+
return '';
|
|
195
|
+
}
|
|
196
|
+
try { sessionStorage.setItem(storageKey, key); } catch (e) {}
|
|
197
|
+
return key;
|
|
198
|
+
}
|
|
199
|
+
function post(path, body) {
|
|
200
|
+
return fetch(apiRoot + path, {
|
|
201
|
+
method: 'POST',
|
|
202
|
+
headers: { 'Content-Type': 'application/json' },
|
|
203
|
+
credentials: 'same-origin',
|
|
204
|
+
body: JSON.stringify(body)
|
|
205
|
+
}).then(function (res) {
|
|
206
|
+
return res.json().then(function (json) {
|
|
207
|
+
json._httpStatus = res.status;
|
|
208
|
+
return json;
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function fieldValue(el) {
|
|
213
|
+
if (el.type === 'checkbox') return el.checked;
|
|
214
|
+
if (el.type === 'number') return el.value === '' ? '' : Number(el.value);
|
|
215
|
+
return el.value;
|
|
216
|
+
}
|
|
217
|
+
function loadStripe(pk) {
|
|
218
|
+
return new Promise(function (resolve, reject) {
|
|
219
|
+
// Reuse the same Stripe instance that created the Card Element. A second
|
|
220
|
+
// Stripe(pk) object cannot confirm that element (Stripe.js throws).
|
|
221
|
+
if (stripe) { resolve(stripe); return; }
|
|
222
|
+
function inst() {
|
|
223
|
+
stripe = window.Stripe(pk);
|
|
224
|
+
resolve(stripe);
|
|
225
|
+
}
|
|
226
|
+
if (window.Stripe) { inst(); return; }
|
|
227
|
+
var s = document.createElement('script');
|
|
228
|
+
s.src = 'https://js.stripe.com/v3/';
|
|
229
|
+
s.onload = function () {
|
|
230
|
+
if (!window.Stripe) { reject(new Error('Stripe.js did not load')); return; }
|
|
231
|
+
inst();
|
|
232
|
+
};
|
|
233
|
+
s.onerror = function () { reject(new Error('Could not load Stripe.js')); };
|
|
234
|
+
document.head.appendChild(s);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!slug) {
|
|
239
|
+
showError('This checkout link is missing its reference. Please check the link and try again.');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
var sessionKey = clientKey();
|
|
244
|
+
if (!sessionKey) return;
|
|
245
|
+
var sessionId = '';
|
|
246
|
+
var config = {};
|
|
247
|
+
var stripe = null;
|
|
248
|
+
var cardElement = null;
|
|
249
|
+
|
|
250
|
+
post('/initialize', { slug: slug, clientSessionKey: sessionKey }).then(function (init) {
|
|
251
|
+
if (!init || !init.Success) {
|
|
252
|
+
showError((init && init.ErrorMessage) || 'This checkout is not available.');
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
sessionId = init.SessionID;
|
|
256
|
+
config = init.Configuration || {};
|
|
257
|
+
var productId = config.productId;
|
|
258
|
+
if (!productId) {
|
|
259
|
+
showError('This checkout is not configured with a product.');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
host.innerHTML = '';
|
|
264
|
+
var h1 = document.createElement('h1');
|
|
265
|
+
h1.textContent = config.title || init.WidgetName || 'Checkout';
|
|
266
|
+
host.appendChild(h1);
|
|
267
|
+
if (config.description) {
|
|
268
|
+
var desc = document.createElement('p');
|
|
269
|
+
desc.textContent = config.description;
|
|
270
|
+
host.appendChild(desc);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
var form = document.createElement('form');
|
|
274
|
+
form.setAttribute('novalidate', 'novalidate');
|
|
275
|
+
|
|
276
|
+
function addField(name, label, type, required, placeholder, options) {
|
|
277
|
+
var lab = document.createElement('label');
|
|
278
|
+
lab.setAttribute('for', 'mj-co-' + name);
|
|
279
|
+
lab.textContent = label + (required ? ' *' : '');
|
|
280
|
+
form.appendChild(lab);
|
|
281
|
+
var input;
|
|
282
|
+
if (type === 'select' && options && options.length) {
|
|
283
|
+
input = document.createElement('select');
|
|
284
|
+
var blank = document.createElement('option');
|
|
285
|
+
blank.value = '';
|
|
286
|
+
blank.textContent = '';
|
|
287
|
+
input.appendChild(blank);
|
|
288
|
+
options.forEach(function (opt) {
|
|
289
|
+
var o = document.createElement('option');
|
|
290
|
+
if (typeof opt === 'object' && opt) {
|
|
291
|
+
o.value = String(opt.value);
|
|
292
|
+
o.textContent = String(opt.label);
|
|
293
|
+
} else {
|
|
294
|
+
o.value = String(opt);
|
|
295
|
+
o.textContent = String(opt);
|
|
296
|
+
}
|
|
297
|
+
input.appendChild(o);
|
|
298
|
+
});
|
|
299
|
+
} else if (type === 'textarea') {
|
|
300
|
+
input = document.createElement('textarea');
|
|
301
|
+
input.rows = 3;
|
|
302
|
+
} else if (type === 'boolean') {
|
|
303
|
+
input = document.createElement('input');
|
|
304
|
+
input.type = 'checkbox';
|
|
305
|
+
} else {
|
|
306
|
+
input = document.createElement('input');
|
|
307
|
+
input.type = type === 'number' ? 'number' : (type === 'date' ? 'date' : 'text');
|
|
308
|
+
}
|
|
309
|
+
input.id = 'mj-co-' + name;
|
|
310
|
+
input.name = name;
|
|
311
|
+
if (placeholder && input.placeholder !== undefined) input.placeholder = placeholder;
|
|
312
|
+
if (required && input.type !== 'checkbox') input.required = true;
|
|
313
|
+
form.appendChild(input);
|
|
314
|
+
return input;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
addField('email', 'Email', 'text', true, 'you@example.com');
|
|
318
|
+
var qtyInput = null;
|
|
319
|
+
if (config.allowQuantity !== false) {
|
|
320
|
+
qtyInput = addField('quantity', 'Quantity', 'number', true, '1');
|
|
321
|
+
qtyInput.min = '1';
|
|
322
|
+
qtyInput.value = '1';
|
|
323
|
+
if (config.maxQuantity) qtyInput.max = String(config.maxQuantity);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
var extFields = Array.isArray(config.extensionFields) ? config.extensionFields : [];
|
|
327
|
+
extFields.forEach(function (f) {
|
|
328
|
+
if (!f || !f.name || f.name === 'email') return;
|
|
329
|
+
addField(f.name, f.label || f.name, f.type || 'text', !!f.required, f.placeholder || '', f.options);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
var cardMount = document.createElement('div');
|
|
333
|
+
cardMount.id = 'mj-co-card';
|
|
334
|
+
form.appendChild(cardMount);
|
|
335
|
+
|
|
336
|
+
var totalEl = document.createElement('p');
|
|
337
|
+
totalEl.className = 'mj-co__total';
|
|
338
|
+
form.appendChild(totalEl);
|
|
339
|
+
|
|
340
|
+
var status = document.createElement('p');
|
|
341
|
+
status.className = 'mj-co__status';
|
|
342
|
+
status.setAttribute('role', 'status');
|
|
343
|
+
form.appendChild(status);
|
|
344
|
+
|
|
345
|
+
var submit = document.createElement('button');
|
|
346
|
+
submit.type = 'submit';
|
|
347
|
+
submit.textContent = 'Continue';
|
|
348
|
+
form.appendChild(submit);
|
|
349
|
+
host.appendChild(form);
|
|
350
|
+
|
|
351
|
+
form.addEventListener('submit', function (ev) {
|
|
352
|
+
ev.preventDefault();
|
|
353
|
+
submit.disabled = true;
|
|
354
|
+
status.textContent = 'Pricing…';
|
|
355
|
+
var emailEl = form.elements.namedItem('email');
|
|
356
|
+
var email = emailEl && 'value' in emailEl ? String(emailEl.value || '').trim() : '';
|
|
357
|
+
var qty = qtyInput ? Math.max(1, parseInt(qtyInput.value, 10) || 1) : 1;
|
|
358
|
+
var extensionFields = {};
|
|
359
|
+
extFields.forEach(function (f) {
|
|
360
|
+
if (!f || !f.name) return;
|
|
361
|
+
var el = form.elements.namedItem(f.name);
|
|
362
|
+
if (el && 'type' in el) extensionFields[f.name] = fieldValue(el);
|
|
363
|
+
});
|
|
364
|
+
var line = { ProductID: productId, Quantity: qty };
|
|
365
|
+
if (Object.keys(extensionFields).length) {
|
|
366
|
+
line.ExtensionData = { EntityName: config.extensionEntityName, Fields: extensionFields };
|
|
367
|
+
}
|
|
368
|
+
post('/draft', {
|
|
369
|
+
sessionId: sessionId,
|
|
370
|
+
clientSessionKey: sessionKey,
|
|
371
|
+
email: email,
|
|
372
|
+
lines: [line]
|
|
373
|
+
}).then(function (draft) {
|
|
374
|
+
if (!draft || !draft.Success) {
|
|
375
|
+
throw new Error((draft && draft.ErrorMessage) || 'Could not price this checkout.');
|
|
376
|
+
}
|
|
377
|
+
var total = typeof draft.TotalGross === 'number' ? draft.TotalGross : 0;
|
|
378
|
+
totalEl.textContent = 'Total: ' + total.toFixed(2);
|
|
379
|
+
if (!draft.RequiresPayment) {
|
|
380
|
+
status.textContent = 'Completing…';
|
|
381
|
+
return post('/complete', { sessionId: sessionId, clientSessionKey: sessionKey });
|
|
382
|
+
}
|
|
383
|
+
status.textContent = 'Opening payment…';
|
|
384
|
+
return post('/payment-intent', { sessionId: sessionId, clientSessionKey: sessionKey }).then(function (intent) {
|
|
385
|
+
if (!intent || !intent.Success || !intent.ClientSecret) {
|
|
386
|
+
throw new Error((intent && intent.ErrorMessage) || 'Could not start payment.');
|
|
387
|
+
}
|
|
388
|
+
var pk = config.stripePublishableKey;
|
|
389
|
+
if (!pk) {
|
|
390
|
+
throw new Error('This checkout requires card payment. Configure stripePublishableKey on the widget, or embed the checkout widget on a site that already loads Stripe.');
|
|
391
|
+
}
|
|
392
|
+
return loadStripe(pk).then(function (stripeInst) {
|
|
393
|
+
stripe = stripeInst;
|
|
394
|
+
if (!cardElement) {
|
|
395
|
+
var elements = stripe.elements();
|
|
396
|
+
cardElement = elements.create('card');
|
|
397
|
+
cardElement.mount('#mj-co-card');
|
|
398
|
+
status.textContent = 'Enter card details and submit again to pay.';
|
|
399
|
+
submit.textContent = 'Pay';
|
|
400
|
+
submit.disabled = false;
|
|
401
|
+
form.dataset.payReady = '1';
|
|
402
|
+
return { _waitForCard: true };
|
|
403
|
+
}
|
|
404
|
+
status.textContent = 'Confirming payment…';
|
|
405
|
+
return stripe.confirmCardPayment(intent.ClientSecret, {
|
|
406
|
+
payment_method: { card: cardElement, billing_details: { email: email } }
|
|
407
|
+
}).then(function (result) {
|
|
408
|
+
if (result.error) throw new Error(result.error.message || 'Payment failed.');
|
|
409
|
+
status.textContent = 'Completing…';
|
|
410
|
+
return post('/complete', { sessionId: sessionId, clientSessionKey: sessionKey });
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}).then(function (done) {
|
|
415
|
+
if (!done || done._waitForCard) return;
|
|
416
|
+
if (!done.Success) {
|
|
417
|
+
throw new Error(done.ErrorMessage || 'Could not complete checkout.');
|
|
418
|
+
}
|
|
419
|
+
host.innerHTML = '';
|
|
420
|
+
var ok = document.createElement('p');
|
|
421
|
+
ok.className = 'mj-co__ok';
|
|
422
|
+
ok.setAttribute('role', 'status');
|
|
423
|
+
ok.textContent = config.successMessage || ('Thank you. Order ' + (done.OrderNumber || '') + ' is confirmed.');
|
|
424
|
+
host.appendChild(ok);
|
|
425
|
+
if (config.redirectUrl) {
|
|
426
|
+
window.location.href = config.redirectUrl;
|
|
427
|
+
}
|
|
428
|
+
}).catch(function (err) {
|
|
429
|
+
status.textContent = '';
|
|
430
|
+
var existing = form.querySelector('.mj-co__error');
|
|
431
|
+
if (existing) existing.remove();
|
|
432
|
+
var p = document.createElement('p');
|
|
433
|
+
p.className = 'mj-co__error';
|
|
434
|
+
p.setAttribute('role', 'alert');
|
|
435
|
+
p.textContent = err && err.message ? err.message : 'Checkout failed.';
|
|
436
|
+
form.insertBefore(p, submit);
|
|
437
|
+
submit.disabled = false;
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
}).catch(function () {
|
|
441
|
+
showError('Checkout is temporarily unavailable. Please try again.');
|
|
442
|
+
});
|
|
443
|
+
})();
|
|
444
|
+
`;
|
|
445
|
+
//# sourceMappingURL=checkout-host-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkout-host-page.js","sourceRoot":"","sources":["../src/checkout-host-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6BH,+EAA+E;AAC/E,MAAM,UAAU,2BAA2B,CAAC,KAAa;IACrD,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG;QACR,oBAAoB;QACpB,iBAAiB;QACjB,oBAAoB;QACpB,wBAAwB;QACxB,qBAAqB,CAAC,gCAAgC;QACtD,oBAAoB,CAAC,UAAU;QAC/B,2CAA2C;QAC3C,6JAA6J;QAC7J,oEAAoE;KACvE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,OAAO;QACH,yBAAyB,EAAE,GAAG;QAC9B,iBAAiB,EAAE,MAAM;QACzB,iBAAiB,EAAE,aAAa;QAChC,wBAAwB,EAAE,SAAS;QACnC,eAAe,EAAE,UAAU;KAC9B,CAAC;AACN,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,UAAU,CAAC,KAAa;IACpC,OAAO,KAAK;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,UAAU,CAAC,KAAa;IACpC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACnE,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,IAAI,UAAU,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,WAAW,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IAC7D,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,IAAI,UAAU,EAAE,CAAC;QACb,OAAO;;;;;;WAMJ,KAAK;UACN,SAAS,IAAI,QAAQ;+BACA,UAAU,IAAI,SAAS;;;;gCAItB,IAAI,eAAe,OAAO,gBAAgB,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC;;;QAG9F,CAAC;IACL,CAAC;IACD,OAAO;;;;;;WAMA,KAAK;UACN,SAAS,IAAI,QAAQ;;;8CAGe,IAAI,oBAAoB,OAAO;;;WAGlE,SAAS,IAAI,WAAW;;QAE3B,CAAC;AACT,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,OAAiC;IACzE,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,IAAI,sBAAsB,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,WAAW,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IAC7D,OAAO;;;;;;WAMA,KAAK;UACN,SAAS,IAAI,QAAQ;;;;2CAIY,OAAO;;;QAG1C,CAAC;AACT,CAAC;AAED,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDhB,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+RnB,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,cAAc,SAAS,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `Orders: Generate Invoice` — hand it any order, get back the document you send the
|
|
3
|
+
* customer.
|
|
4
|
+
*
|
|
5
|
+
* WHY AN ACTION AND NOT A REMOTE OPERATION. The other order verbs — confirm, capture, fulfil — are
|
|
6
|
+
* operations because they are the API a browser calls to change something. This one changes
|
|
7
|
+
* nothing, and its callers are different in kind: a workflow that emails a bill on confirmation, an
|
|
8
|
+
* agent asked to "send Contoso their invoice", a scheduled job producing a month's PDFs, a person
|
|
9
|
+
* clicking Print. Actions are the surface all four already speak, and they are discoverable in
|
|
10
|
+
* metadata rather than only from code.
|
|
11
|
+
*
|
|
12
|
+
* WHAT IT COMPOSES. This class does no arithmetic, no formatting and no markup. It resolves inputs,
|
|
13
|
+
* calls the reader, the display layer and the template engine in that order, and hands back what
|
|
14
|
+
* they produced:
|
|
15
|
+
*
|
|
16
|
+
* `InvoiceBuilder` → the document, per selling company, derived from the order
|
|
17
|
+
* `InvoiceDisplay` → the strings, so the template can print without computing
|
|
18
|
+
* MJ Templates → the HTML, editable in the database with no deploy
|
|
19
|
+
*
|
|
20
|
+
* IT RETURNS THE DATA ALONGSIDE THE HTML. A caller that wants a PDF renders the HTML; a caller
|
|
21
|
+
* doing a reconciliation check wants the numbers, and making it scrape them back out of markup
|
|
22
|
+
* would be a guarantee that the check and the document eventually disagree.
|
|
23
|
+
*
|
|
24
|
+
* IT NEVER WRITES. There is no invoice record — the confirmed order IS the receivable — so
|
|
25
|
+
* rendering the same order twice produces two identical documents. See the header of
|
|
26
|
+
* `InvoiceBehavior` for why storing one would be a mistake, and why statements are the opposite
|
|
27
|
+
* case.
|
|
28
|
+
*
|
|
29
|
+
* @module @mj-biz-apps/orders-actions
|
|
30
|
+
*/
|
|
31
|
+
import { BaseAction } from '@memberjunction/actions';
|
|
32
|
+
import type { ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
|
|
33
|
+
export { DEFAULT_INVOICE_TEMPLATE } from '../services/invoice-renderer.js';
|
|
34
|
+
export type { RenderedInvoice } from '../services/invoice-renderer.js';
|
|
35
|
+
/**
|
|
36
|
+
* Render an order as an invoice, quote or credit memo.
|
|
37
|
+
*
|
|
38
|
+
* Inputs: `OrderID` (required), `CompanyID`, `AsOfDate`, `TemplateName`, `Locale`, `CurrencyCode`,
|
|
39
|
+
* `Format`, `ShowDiagnostics`. Outputs: `Invoices`, `HTML`, `DocumentCount`, `Notes`.
|
|
40
|
+
*/
|
|
41
|
+
export declare class GenerateInvoiceAction extends BaseAction {
|
|
42
|
+
/**
|
|
43
|
+
* AN ACTION MUST NOT THROW AT ITS CALLER. The layers below guard their inputs by throwing —
|
|
44
|
+
* `RequireUUID` is what keeps an id out of a SQL string — and a workflow, an agent or a print
|
|
45
|
+
* button calling this gets an exception rather than a result it can branch on. So the whole body
|
|
46
|
+
* runs behind this, and every failure comes back as a Success=false with a code.
|
|
47
|
+
*/
|
|
48
|
+
protected InternalRunAction(params: RunActionParams): Promise<ActionResultSimple>;
|
|
49
|
+
private render;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Registers {@link GenerateInvoiceAction}.
|
|
53
|
+
*
|
|
54
|
+
* WITHOUT THIS ANCHOR the class is tree-shaken out of the bundle, the `@RegisterClass` decorator
|
|
55
|
+
* never runs, and `ActionEngine` falls back to no implementation — which surfaces as an action that
|
|
56
|
+
* exists in metadata and does nothing.
|
|
57
|
+
*/
|
|
58
|
+
export declare function LoadGenerateInvoiceAction(): void;
|
|
59
|
+
//# sourceMappingURL=generate-invoice.action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-invoice.action.d.ts","sourceRoot":"","sources":["../../src/custom/generate-invoice.action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,EAAe,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAWrG,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,YAAY,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAmCvE;;;;;GAKG;AACH,qBACa,qBAAsB,SAAQ,UAAU;IACjD;;;;;OAKG;cACa,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;YAYzE,MAAM;CAwGvB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|