Skip to content

Commit 55ab273

Browse files
authored
Merge pull request #440 from kernvalley/patch/checkout
Update checkout process & version bump
2 parents b714f7b + 1394f53 commit 55ab273

File tree

8 files changed

+59
-74
lines changed

8 files changed

+59
-74
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [v2.3.1] - 2023-01-21
11+
12+
### Added
13+
- Create actual WFD Mayors page with events
14+
15+
### Changed
16+
- Updated Store to be (basically) fully functional
17+
18+
### Fixed
19+
- Checkout no longer creates duplicate orders
20+
1021
## [v2.3.0] - 2023-01-09
1122

1223
### Changed

_config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ author: Chris Zuber
22
title: Whiskey Flat Days
33
url: 'https://whiskeyflatdays.com'
44
markdown: kramdown
5-
version: 2.3.0
5+
version: 2.3.1
66
timezone: America/Los_Angeles
77
lang: en
88
dir: ltr

_data/app.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name: Whiskey Flat Days
33
shortName: WFD App
44
id: WhiskeyFlatDays
5-
version: 2.3.0-2023-01-16T17:18
5+
version: 2.3.1
66
start_url: /?utm_source=homescreen&utm_medium=pwa
77
display: standalone
88
description: "Map, events, store, and news app for Whiskey Flat Days"

api/paymentRequest.js

Lines changed: 11 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { HTTPError } = require('./http-error.js');
55
const { calculateShipping, calculateCardFee, calculateTaxes, getTotal } = require('./stripe-utils.js');
66
const { currency } = require('./stripe-consts.js');
77

8-
async function getDisplayItems(cart, { signal } = {}) {
8+
async function createDisplayItems(cart, { signal } = {}) {
99
const { getProducts } = require('./store.js');
1010
const products = await getProducts(cart.map(({ id }) => id, { signal }));
1111

@@ -35,56 +35,8 @@ async function getDisplayItems(cart, { signal } = {}) {
3535
}
3636
});
3737
}
38-
async function getPaymentRequest(displayItems) {
39-
if (! Array.isArray(displayItems) || displayItems.length === 0) {
40-
throw new TypeError('`displayItems` invalid');
41-
} else {
42-
const req = {
43-
details: {
44-
displayItems,
45-
modifiers: {
46-
additionalDisplayItems: [{
47-
label: 'Taxes',
48-
amount: {
49-
value: await calculateTaxes({ details: { displayItems }}),
50-
currency,
51-
}
52-
}, {
53-
label: 'Shipping',
54-
amount: {
55-
value: await calculateShipping({ displayItems }),
56-
currency,
57-
},
58-
}]
59-
}
60-
},
61-
options: {
62-
requestShipping: true,
63-
}
64-
};
65-
66-
req.details.modifiers.additionalDisplayItems.push({
67-
label: 'Processing Fee',
68-
amount: {
69-
value: await calculateCardFee(req),
70-
currency,
71-
}
72-
});
73-
74-
req.details.total = {
75-
label: 'Total',
76-
amount: {
77-
value: getTotal(req),
78-
currency,
79-
}
80-
};
81-
82-
return req;
83-
}
84-
}
85-
async function createPaymentRequest(query) {
86-
const { createDisplayItems } = require('./store.js');
87-
const displayItems = await createDisplayItems(query);
38+
async function createPaymentRequest(items, { signal } = {}) {
39+
const displayItems = await createDisplayItems(items, { signal });
8840

8941
if (! Array.isArray(displayItems) || displayItems.length === 0) {
9042
throw new TypeError('`displayItems` invalid');
@@ -96,7 +48,7 @@ async function createPaymentRequest(query) {
9648
additionalDisplayItems: [{
9749
label: 'Taxes',
9850
amount: {
99-
value: await calculateTaxes({ displayItems }),
51+
value: await calculateTaxes({ details: { displayItems }}),
10052
currency,
10153
}
10254
}, {
@@ -137,15 +89,15 @@ exports.handler = async function(event) {
13789
try {
13890
switch(event.httpMethod) {
13991
case 'GET':
140-
if (typeof event.queryStringParameters.query !== 'string') {
141-
throw new HTTPError('Missing required query param', { status: 400 });
92+
if (typeof event.queryStringParameters.id !== 'string') {
93+
throw new HTTPError('Missing required id param', { status: 400 });
14294
} else {
143-
const req = await createPaymentRequest(event.queryStringParameters.query);
144-
95+
const { getOrder } = require('./stripe-utils.js');
96+
const { paymentRequest, paymentIntent } = await getOrder(event.queryStringParameters.id);
14597
return {
14698
statusCode: 200,
14799
headers,
148-
body: JSON.stringify(req),
100+
body: JSON.stringify({ paymentRequest, paymentIntent }),
149101
};
150102
}
151103
case 'POST':
@@ -157,8 +109,7 @@ exports.handler = async function(event) {
157109
if (! Array.isArray(items) || items.length === 0) {
158110
throw new HTTPError('Invalid request body', { status: 400 });
159111
} else {
160-
const displayItems = await getDisplayItems(items);
161-
const paymentRequest = await getPaymentRequest(displayItems);
112+
const paymentRequest = await createPaymentRequest(items);
162113
const { createPaymentIntent, createOrder } = require('./stripe-utils.js');
163114
const paymentIntent = await createPaymentIntent(paymentRequest.details.total.amount.value);
164115
paymentRequest.details.id = paymentIntent.id;
@@ -176,6 +127,7 @@ exports.handler = async function(event) {
176127
}
177128
} catch(err) {
178129
console.error(err);
130+
179131
if (err instanceof HTTPError) {
180132
return err.send({ Options: methods.join(',')});
181133
} else {

api/stripe-utils.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,9 @@ async function getOrder(id) {
167167
if (doc.exists) {
168168
const Stripe = require('stripe');
169169
const stripe = Stripe(process.env.STRIPE_SECRET);
170-
const order = doc.data();
171-
const paymentIntent = await stripe.paymentIntents.retrieve(order.paymentRequest.details.id);
172-
return { paymentIntent, order };
170+
const { paymentRequest } = doc.data();
171+
const paymentIntent = await stripe.paymentIntents.retrieve(paymentRequest.details.id);
172+
return { paymentIntent, paymentRequest };
173173
} else {
174174
throw new Error(`Invalid order "${id}"`);
175175
}

js/store.js

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,22 @@ async function showProductDetails(id, { signal } = {}) {
265265
await promise;
266266
}
267267

268-
async function getPaymentRequest({ signal } = {}) {
269-
const cart = new Cart();
268+
async function createPaymentRequest(cart = new Cart(), { signal } = {}) {
270269
const url = new URL('/api/paymentRequest', document.baseURI);
271270
const body = await cart.getAll({ signal });
272271
return await postJSON(url, { body, signal });
273272
}
274273

274+
async function getPaymentRequest({ signal } = {}) {
275+
const params = new URLSearchParams(location.search);
276+
277+
if (params.has('token')) {
278+
const url = new URL('/api/paymentRequest', document.baseURI);
279+
url.searchParams.set('id', params.get('token'));
280+
return await getJSON(url, { signal });
281+
}
282+
}
283+
275284
async function reviewCart(cart, { signal } = {}) {
276285
const [products, items] = await Promise.all([
277286
getProducts({ signal }),
@@ -391,13 +400,26 @@ async function reviewCart(cart, { signal } = {}) {
391400
events: { click: ({ target }) => target.closest('dialog').close() },
392401
text: 'Back to Shopping',
393402
}),
394-
create('a', {
395-
href: '/store/checkout',
396-
role: 'button',
403+
create('button', {
404+
type: 'button',
397405
classList: items.length === 0
398406
? ['btn', 'btn-primary', 'checkout-btn', 'disabled']
399407
: ['btn', 'btn-primary', 'checkout-btn'],
400408
text: 'Continue to Checkout',
409+
events: {
410+
click: async ({ currentTarget }) => {
411+
try {
412+
currentTarget.disabled = true;
413+
const { paymentIntent } = await createPaymentRequest();
414+
const url = new URL('/store/checkout', document.baseURI);
415+
url.searchParams.set('token', paymentIntent);
416+
location.href = url.href;
417+
} catch(err) {
418+
currentTarget.disabled = false;
419+
console.error(err);
420+
}
421+
}
422+
}
401423
})
402424
]
403425
}),
@@ -450,12 +472,12 @@ if (location.pathname.startsWith('/store/checkout')) {
450472
break;
451473
}
452474
}
453-
} else {
475+
} else if (location.search.includes('token=')) {
454476
Promise.all([
455477
getPaymentRequest(),
456478
getStripeKey(),
457479
]).then(async ([{ paymentRequest, paymentIntent }, key]) => {
458-
const form = new HTMLStripePaymentFormElement(key, paymentIntent, {
480+
const form = new HTMLStripePaymentFormElement(key, paymentIntent.client_secret, {
459481
...paymentRequest,
460482
config: {
461483
returnURL: new URL(location.pathname, location.origin).href,

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "whiskey-flat-days",
3-
"version": "2.3.0",
3+
"version": "2.3.1",
44
"private": true,
55
"description": "Map, events, store, and news app for Whiskey Flat Days",
66
"config": {

0 commit comments

Comments
 (0)