wiki-plugin-shoppe 0.0.22 → 0.0.23
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/CLAUDE.md +53 -9
- package/client/shoppe.js +40 -12
- package/package.json +1 -1
- package/server/scripts/shoppe-sign.js +369 -0
- package/server/server.js +767 -41
- package/server/templates/appointment-booking.html +23 -2
- package/server/templates/ebook-download.html +1 -0
- package/server/templates/generic-address-stripe.html +23 -2
- package/server/templates/generic-recover-stripe.html +23 -2
- package/server/templates/subscription-subscribe.html +23 -2
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
|
7
7
|
<meta name="twitter:title" content="{{title}}">
|
|
8
8
|
<meta name="description" content="{{description}}">
|
|
9
|
+
<meta name="keywords" content="{{keywords}}">
|
|
9
10
|
<meta name="og:title" content="{{title}}">
|
|
10
11
|
<meta name="og:description" content="{{description}}">
|
|
11
12
|
<title>Book: {{title}}</title>
|
|
@@ -189,6 +190,22 @@
|
|
|
189
190
|
const AMOUNT = {{amount}};
|
|
190
191
|
const TIMEZONE = '{{timezone}}';
|
|
191
192
|
|
|
193
|
+
// Parse ?payees=pubKey:amount:percent:addieUrl|... from the page URL.
|
|
194
|
+
function parseUrlPayees() {
|
|
195
|
+
const raw = new URLSearchParams(window.location.search).get('payees');
|
|
196
|
+
if (!raw) return null;
|
|
197
|
+
return raw.split('|').map(t => {
|
|
198
|
+
const [pubKey, amount, percent, addieUrl] = t.split(':');
|
|
199
|
+
const p = {};
|
|
200
|
+
if (pubKey) p.pubKey = pubKey;
|
|
201
|
+
if (amount) p.amount = Number(amount);
|
|
202
|
+
if (percent) p.percent = Number(percent);
|
|
203
|
+
if (addieUrl) p.addieUrl = addieUrl;
|
|
204
|
+
return p;
|
|
205
|
+
}).filter(p => p.pubKey);
|
|
206
|
+
}
|
|
207
|
+
const URL_PAYEES = parseUrlPayees();
|
|
208
|
+
|
|
192
209
|
let availableDates = []; // [{ date, dayLabel, slots }]
|
|
193
210
|
let selectedDate = null;
|
|
194
211
|
let selectedSlot = null;
|
|
@@ -328,7 +345,8 @@
|
|
|
328
345
|
const resp = await fetch(`${SHOPPE_URL}/purchase/intent`, {
|
|
329
346
|
method: 'POST',
|
|
330
347
|
headers: { 'Content-Type': 'application/json' },
|
|
331
|
-
body: JSON.stringify({ recoveryKey, productId: PRODUCT_ID, title: TITLE, slotDatetime: selectedSlot
|
|
348
|
+
body: JSON.stringify({ recoveryKey, productId: PRODUCT_ID, title: TITLE, slotDatetime: selectedSlot,
|
|
349
|
+
...(URL_PAYEES && { payees: URL_PAYEES }) })
|
|
332
350
|
});
|
|
333
351
|
const data = await resp.json();
|
|
334
352
|
|
|
@@ -361,6 +379,7 @@
|
|
|
361
379
|
paySection.classList.add('visible');
|
|
362
380
|
document.getElementById('selected-slot-payment').textContent = `📅 ${formatSlotDisplay(selectedSlot)}`;
|
|
363
381
|
|
|
382
|
+
window.clientSecret = data.clientSecret;
|
|
364
383
|
stripe = Stripe(data.publishableKey);
|
|
365
384
|
elements = stripe.elements({ clientSecret: data.clientSecret });
|
|
366
385
|
elements.create('payment').mount('#payment-element');
|
|
@@ -406,6 +425,7 @@
|
|
|
406
425
|
email: document.getElementById('contact-email').value.trim(),
|
|
407
426
|
phone: document.getElementById('contact-phone').value.trim()
|
|
408
427
|
};
|
|
428
|
+
const paymentIntentId = window.clientSecret ? window.clientSecret.split('_secret_')[0] : undefined;
|
|
409
429
|
|
|
410
430
|
await fetch(`${SHOPPE_URL}/purchase/complete`, {
|
|
411
431
|
method: 'POST',
|
|
@@ -415,7 +435,8 @@
|
|
|
415
435
|
productId: PRODUCT_ID,
|
|
416
436
|
title: TITLE,
|
|
417
437
|
slotDatetime: selectedSlot,
|
|
418
|
-
contactInfo
|
|
438
|
+
contactInfo,
|
|
439
|
+
paymentIntentId
|
|
419
440
|
})
|
|
420
441
|
});
|
|
421
442
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<!-- Web preview meta tags -->
|
|
8
8
|
<meta name="twitter:title" content="{{title}}">
|
|
9
9
|
<meta name="description" content="{{description}}">
|
|
10
|
+
<meta name="keywords" content="{{keywords}}">
|
|
10
11
|
<meta name="twitter:description" content="{{description}}">
|
|
11
12
|
<meta name="twitter:image" content="{{image}}">
|
|
12
13
|
<meta name="og:title" content="{{title}}">
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
<!--these are the tags that create the web preview card. You can use og:, or twitter: tags or both-->
|
|
9
9
|
<meta name="twitter:title" content="{{title}}">
|
|
10
10
|
<meta name="description" content="{{description}}">
|
|
11
|
+
<meta name="keywords" content="{{keywords}}">
|
|
11
12
|
<meta name="twitter:description" content="{{description}}">
|
|
12
13
|
<meta name="twitter:image" content={{image}}>
|
|
13
14
|
|
|
@@ -471,6 +472,22 @@
|
|
|
471
472
|
</script>
|
|
472
473
|
|
|
473
474
|
<script type="text/javascript">
|
|
475
|
+
// Parse ?payees=pubKey:amount:percent:addieUrl|... from the page URL.
|
|
476
|
+
function parseUrlPayees() {
|
|
477
|
+
const raw = new URLSearchParams(window.location.search).get('payees');
|
|
478
|
+
if (!raw) return null;
|
|
479
|
+
return raw.split('|').map(t => {
|
|
480
|
+
const [pubKey, amount, percent, addieUrl] = t.split(':');
|
|
481
|
+
const p = {};
|
|
482
|
+
if (pubKey) p.pubKey = pubKey;
|
|
483
|
+
if (amount) p.amount = Number(amount);
|
|
484
|
+
if (percent) p.percent = Number(percent);
|
|
485
|
+
if (addieUrl) p.addieUrl = addieUrl;
|
|
486
|
+
return p;
|
|
487
|
+
}).filter(p => p.pubKey);
|
|
488
|
+
}
|
|
489
|
+
const URL_PAYEES = parseUrlPayees();
|
|
490
|
+
|
|
474
491
|
let stripe;
|
|
475
492
|
let elements;
|
|
476
493
|
let orderRef;
|
|
@@ -489,7 +506,8 @@
|
|
|
489
506
|
headers: {'Content-Type': 'application/json'},
|
|
490
507
|
body: JSON.stringify({
|
|
491
508
|
productId: '{{productId}}',
|
|
492
|
-
title: '{{title}}'
|
|
509
|
+
title: '{{title}}',
|
|
510
|
+
...(URL_PAYEES && { payees: URL_PAYEES })
|
|
493
511
|
})
|
|
494
512
|
});
|
|
495
513
|
|
|
@@ -497,6 +515,7 @@
|
|
|
497
515
|
if (data.error) { showError(data.error); return; }
|
|
498
516
|
|
|
499
517
|
orderRef = data.orderRef;
|
|
518
|
+
window.clientSecret = data.clientSecret;
|
|
500
519
|
|
|
501
520
|
stripe = Stripe(data.publishableKey);
|
|
502
521
|
elements = stripe.elements({ clientSecret: data.clientSecret });
|
|
@@ -522,6 +541,7 @@
|
|
|
522
541
|
if (error) { showError(error.message); return; }
|
|
523
542
|
|
|
524
543
|
// Payment succeeded — now record the order server-side with the shipping address
|
|
544
|
+
const paymentIntentId = window.clientSecret ? window.clientSecret.split('_secret_')[0] : undefined;
|
|
525
545
|
await fetch('{{shoppeUrl}}/purchase/complete', {
|
|
526
546
|
method: 'POST',
|
|
527
547
|
headers: {'Content-Type': 'application/json'},
|
|
@@ -530,7 +550,8 @@
|
|
|
530
550
|
productId: '{{productId}}',
|
|
531
551
|
title: '{{title}}',
|
|
532
552
|
amount: {{amount}},
|
|
533
|
-
address: window.pendingAddress
|
|
553
|
+
address: window.pendingAddress,
|
|
554
|
+
paymentIntentId
|
|
534
555
|
})
|
|
535
556
|
});
|
|
536
557
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
<!--these are the tags that create the web preview card. You can use og:, or twitter: tags or both-->
|
|
9
9
|
<meta name="twitter:title" content="{{title}}">
|
|
10
10
|
<meta name="description" content="{{description}}">
|
|
11
|
+
<meta name="keywords" content="{{keywords}}">
|
|
11
12
|
<meta name="twitter:description" content="{{description}}">
|
|
12
13
|
<meta name="twitter:image" content={{image}}>
|
|
13
14
|
|
|
@@ -564,6 +565,23 @@
|
|
|
564
565
|
</script>
|
|
565
566
|
|
|
566
567
|
<script>
|
|
568
|
+
// Parse ?payees=pubKey:amount:percent:addieUrl|... from the page URL.
|
|
569
|
+
// Returns an array of payee objects, or null if the param is absent.
|
|
570
|
+
function parseUrlPayees() {
|
|
571
|
+
const raw = new URLSearchParams(window.location.search).get('payees');
|
|
572
|
+
if (!raw) return null;
|
|
573
|
+
return raw.split('|').map(t => {
|
|
574
|
+
const [pubKey, amount, percent, addieUrl] = t.split(':');
|
|
575
|
+
const p = {};
|
|
576
|
+
if (pubKey) p.pubKey = pubKey;
|
|
577
|
+
if (amount) p.amount = Number(amount);
|
|
578
|
+
if (percent) p.percent = Number(percent);
|
|
579
|
+
if (addieUrl) p.addieUrl = addieUrl;
|
|
580
|
+
return p;
|
|
581
|
+
}).filter(p => p.pubKey);
|
|
582
|
+
}
|
|
583
|
+
const URL_PAYEES = parseUrlPayees();
|
|
584
|
+
|
|
567
585
|
const formConfig = {
|
|
568
586
|
"Recovery": {type: "text", required: true},
|
|
569
587
|
"Explanation": {type: "text-block", content: "Put whatever you want here, but make sure you remember it. This is how you'll be able to download this again."}
|
|
@@ -580,7 +598,8 @@
|
|
|
580
598
|
const resp = await fetch('/plugin/shoppe/{{tenantUuid}}/purchase/intent', {
|
|
581
599
|
method: 'POST',
|
|
582
600
|
headers: { 'Content-Type': 'application/json' },
|
|
583
|
-
body: JSON.stringify({ recoveryKey, productId: '{{productId}}', title: '{{title}}'
|
|
601
|
+
body: JSON.stringify({ recoveryKey, productId: '{{productId}}', title: '{{title}}',
|
|
602
|
+
...(URL_PAYEES && { payees: URL_PAYEES }) })
|
|
584
603
|
});
|
|
585
604
|
const json = await resp.json();
|
|
586
605
|
|
|
@@ -595,6 +614,7 @@
|
|
|
595
614
|
}
|
|
596
615
|
|
|
597
616
|
// Show payment element
|
|
617
|
+
window.clientSecret = json.clientSecret;
|
|
598
618
|
document.getElementById('payment-section').style.display = 'block';
|
|
599
619
|
stripe = Stripe(json.publishableKey);
|
|
600
620
|
elements = stripe.elements({ clientSecret: json.clientSecret });
|
|
@@ -633,10 +653,11 @@
|
|
|
633
653
|
if (error) return showError(error.message);
|
|
634
654
|
|
|
635
655
|
const order = { title: '{{title}}', productId: '{{productId}}', formData: window.formData };
|
|
656
|
+
const paymentIntentId = window.clientSecret ? window.clientSecret.split('_secret_')[0] : undefined;
|
|
636
657
|
const resp = await fetch('/plugin/shoppe/{{tenantUuid}}/purchase/complete', {
|
|
637
658
|
method: 'POST',
|
|
638
659
|
headers: { 'Content-Type': 'application/json' },
|
|
639
|
-
body: JSON.stringify({ recoveryKey: window.formData.Recovery, productId: '{{productId}}', order })
|
|
660
|
+
body: JSON.stringify({ recoveryKey: window.formData.Recovery, productId: '{{productId}}', order, paymentIntentId })
|
|
640
661
|
});
|
|
641
662
|
const json = await resp.json();
|
|
642
663
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
|
7
7
|
<meta name="twitter:title" content="Infuse: {{title}}">
|
|
8
8
|
<meta name="description" content="{{description}}">
|
|
9
|
+
<meta name="keywords" content="{{keywords}}">
|
|
9
10
|
<meta name="og:title" content="Infuse: {{title}}">
|
|
10
11
|
<meta name="og:description" content="{{description}}">
|
|
11
12
|
<title>Infuse: {{title}}</title>
|
|
@@ -150,6 +151,22 @@
|
|
|
150
151
|
const AMOUNT = {{amount}};
|
|
151
152
|
const RENEWAL_DAYS = {{renewalDays}};
|
|
152
153
|
|
|
154
|
+
// Parse ?payees=pubKey:amount:percent:addieUrl|... from the page URL.
|
|
155
|
+
function parseUrlPayees() {
|
|
156
|
+
const raw = new URLSearchParams(window.location.search).get('payees');
|
|
157
|
+
if (!raw) return null;
|
|
158
|
+
return raw.split('|').map(t => {
|
|
159
|
+
const [pubKey, amount, percent, addieUrl] = t.split(':');
|
|
160
|
+
const p = {};
|
|
161
|
+
if (pubKey) p.pubKey = pubKey;
|
|
162
|
+
if (amount) p.amount = Number(amount);
|
|
163
|
+
if (percent) p.percent = Number(percent);
|
|
164
|
+
if (addieUrl) p.addieUrl = addieUrl;
|
|
165
|
+
return p;
|
|
166
|
+
}).filter(p => p.pubKey);
|
|
167
|
+
}
|
|
168
|
+
const URL_PAYEES = parseUrlPayees();
|
|
169
|
+
|
|
153
170
|
let stripe = null;
|
|
154
171
|
let elements = null;
|
|
155
172
|
|
|
@@ -169,7 +186,8 @@
|
|
|
169
186
|
const resp = await fetch(`${SHOPPE_URL}/purchase/intent`, {
|
|
170
187
|
method: 'POST',
|
|
171
188
|
headers: { 'Content-Type': 'application/json' },
|
|
172
|
-
body: JSON.stringify({ recoveryKey, productId: PRODUCT_ID, title: TITLE
|
|
189
|
+
body: JSON.stringify({ recoveryKey, productId: PRODUCT_ID, title: TITLE,
|
|
190
|
+
...(URL_PAYEES && { payees: URL_PAYEES }) })
|
|
173
191
|
});
|
|
174
192
|
const data = await resp.json();
|
|
175
193
|
|
|
@@ -187,6 +205,7 @@
|
|
|
187
205
|
document.getElementById('subscribe-form').style.display = 'none';
|
|
188
206
|
document.getElementById('payment-section').style.display = 'block';
|
|
189
207
|
|
|
208
|
+
window.clientSecret = data.clientSecret;
|
|
190
209
|
stripe = Stripe(data.publishableKey);
|
|
191
210
|
elements = stripe.elements({ clientSecret: data.clientSecret });
|
|
192
211
|
elements.create('payment').mount('#payment-element');
|
|
@@ -220,6 +239,7 @@
|
|
|
220
239
|
}
|
|
221
240
|
|
|
222
241
|
const recoveryKey = document.getElementById('recovery-key').value.trim();
|
|
242
|
+
const paymentIntentId = window.clientSecret ? window.clientSecret.split('_secret_')[0] : undefined;
|
|
223
243
|
const complete = await fetch(`${SHOPPE_URL}/purchase/complete`, {
|
|
224
244
|
method: 'POST',
|
|
225
245
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -229,7 +249,8 @@
|
|
|
229
249
|
title: TITLE,
|
|
230
250
|
amount: AMOUNT,
|
|
231
251
|
type: 'subscription',
|
|
232
|
-
renewalDays: RENEWAL_DAYS
|
|
252
|
+
renewalDays: RENEWAL_DAYS,
|
|
253
|
+
paymentIntentId
|
|
233
254
|
})
|
|
234
255
|
});
|
|
235
256
|
await complete.json();
|