Solves the issue of not being able to look up an order after clearing the session cart

main
Ashelyn Dawn 5 years ago
parent a834eb441b
commit a6ed5bc430

@ -5,10 +5,15 @@ const stripe = require('stripe')(process.env.STRIPE_PRIVATE_KEY);
const validate = require('./middleware/validators')
router.get('/', async (req, res) => {
const orders = await db.order.findAllForSession(req.session.uuid)
res.json(orders)
})
router.use(require('./middleware/ensureCart'))
router.put('/', async (req, res) => {
const order = await db.order.create(req.cart.uuid);
const order = await db.order.create(req.cart.uuid, req.sessionObj.uuid);
res.json(order)
})
@ -66,17 +71,23 @@ router.post('/current/checkout/stripe', async (req, res) => {
const itemPriceWithDiscount = item_total_price - (coupon_effective_discount || 0)
const shippingPrice = (free_shipping ? 0 : shipping_price)
const line_items = [{
const line_items = []
if(itemPriceWithDiscount > 0)
line_items.push({
name: `Cart Total (${numberOfItems} item${numberOfItems > 1 ? 's' : ''})`,
amount: itemPriceWithDiscount,
currency: 'usd',
quantity: 1
}, {
})
if(shippingPrice > 0)
line_items.push({
name: 'Shipping',
amount: shippingPrice,
currency: 'usd',
quantity: 1
}]
})
if(tax_price > 0)
line_items.push({
@ -86,12 +97,16 @@ router.post('/current/checkout/stripe', async (req, res) => {
quantity: 1
})
// TODO: We need to handle this case, mark it paid or something?
if(line_items.length < 1)
res.json(null)
const session = await stripe.checkout.sessions.create({
client_reference_id: currentTransaction.uuid,
customer_email: req.user ? req.user.email : undefined,
payment_method_types: ['card'],
line_items,
success_url: `${process.env.EXTERNAL_URL}/store/checkout/complete?session_id={CHECKOUT_SESSION_ID}`,
success_url: `${process.env.EXTERNAL_URL}/store/checkout/verify?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.EXTERNAL_URL}/cancel`,
});
@ -118,5 +133,7 @@ router.post('/current/checkout/verify', parseJSON, async (req, res) => {
await db.session.clearCart(req.sessionObj.uuid)
}
// TODO: Save stockchange record for purchase
res.json({status: payment.status, order})
})

@ -9,7 +9,8 @@ module.exports = [{
'date_uploaded'
],
associations: [
{name: 'uploader', mapId: 'userMap', columnPrefix: 'user_'}
// TODO: Uploader should not be included in non-admin API responses
{name: 'uploader', mapId: 'userMap', columnPrefix: 'uploader_'}
]
},{
mapId: 'itemMap',

@ -26,7 +26,8 @@ module.exports = [{
],
associations: [
{name: 'cart', mapId: 'cartMap', columnPrefix: 'cart_'},
{name: 'coupon', mapId: 'couponMap', columnPrefix: 'coupon_'}
{name: 'coupon', mapId: 'couponMap', columnPrefix: 'coupon_'},
{name: 'payment', mapId: 'paymentMap', columnPrefix: 'payment_'}
]
},{
mapId: 'addressMap',
@ -56,4 +57,21 @@ module.exports = [{
'per_sock_discount_cents',
'number_of_socks_free'
]
},{
mapId: 'paymentMap',
idProperty: 'uuid',
properties: [
'type',
'time',
'value_cents'
],
associations: [
{name: 'stripe', mapId: 'paymentStripeMap', columnPrefix: 'stripe_'}
]
},{
mapId: 'paymentStripeMap',
idProperty: 'payment_intent_id',
properties: [
'reciept_email'
]
}]

@ -9,10 +9,10 @@ const easypost = new (require('@easypost/api'))(process.env.EASYPOST_API_KEY);
const order = module.exports = {}
order.create = async function(cart_uuid){
order.create = async function(cart_uuid, session_uuid){
const query = {
text: 'select * from sos.create_order($1)',
values: [cart_uuid]
text: 'select * from sos.create_order($1, $2)',
values: [cart_uuid, session_uuid]
}
debug(query);
@ -33,6 +33,18 @@ order.findForCart = async function(cart_uuid) {
return joinjs.map(rows, mappings, 'orderMap', 'order_')[0];
}
order.findAllForSession = async function(session_uuid) {
const query = {
text: 'select * from sos.find_orders_for_session($1)',
values: [session_uuid]
}
debug(query)
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'orderMap', 'order_');
}
order.addAddress = async function (transaction, address){
// Get parcel size
const parcel = {

@ -171,6 +171,7 @@ create table sos."coupon" (
create table sos."transaction" (
transaction_uuid uuid primary key default uuid_generate_v4(),
transaction_order_uuid uuid references sos."order" (order_uuid),
transaction_session_uuid uuid not null references sos."session" (session_uuid),
transaction_cart_uuid uuid references sos."cart" (cart_uuid),
transaction_coupon_uuid uuid references sos."coupon" (coupon_uuid),
transaction_start_time timestamptz not null default now(),

@ -5,7 +5,7 @@ create or replace view sos.v_item as
"image".image_featured,
"image".image_mime_type,
"image".image_date_uploaded,
"user".user_email,
"user".user_email as uploader_email,
coalesce(num_added - num_removed, 0) as item_number_in_stock
from sos."item"
left join sos."image" on item.item_uuid = image.image_item_uuid

@ -402,13 +402,14 @@ begin
return query select * from sos.v_item where item_uuid = _item_uuid;
end; $function$;
create or replace function sos.create_order(_cart_uuid uuid)
create or replace function sos.create_order(_cart_uuid uuid, _session_uuid uuid)
returns setof sos.v_order
language plpgsql
as $function$
declare
_completed_transactions integer;
_order_uuid uuid;
_existing_transaction uuid;
_cart_price integer;
begin
-- Check for completed transactions
@ -422,11 +423,18 @@ begin
end if;
-- Check for existing transaction for this cart?
select transaction_order_uuid into _order_uuid
select transaction_order_uuid, transaction_uuid into _order_uuid, _existing_transaction
from sos."transaction"
where transaction_cart_uuid = _cart_uuid
and transaction_payment_state = 'started';
-- Update existing transaction to current session
if _existing_transaction is not null then
update sos."transaction" set
transaction_session_uuid = _session_uuid
where transaction_uuid = _existing_transaction;
end if;
-- If no existing order, create an order and a transaction
if _order_uuid is null then
-- Create order
@ -442,11 +450,13 @@ begin
insert into sos."transaction" (
transaction_order_uuid,
transaction_cart_uuid,
transaction_item_total_price
transaction_item_total_price,
transaction_session_uuid
) values (
_order_uuid,
_cart_uuid,
_cart_price
_cart_price,
_session_uuid
);
end if;
@ -495,6 +505,22 @@ begin
return query select * from sos.v_order where order_uuid = _order_uuid;
end; $function$;
create or replace function sos.find_orders_for_session(_session_uuid uuid)
returns setof sos.v_order
language plpgsql
as $function$
begin
return query
select "order".* from sos."transaction"
left join sos.v_order "order" on "order".order_uuid = "transaction".transaction_order_uuid
where "transaction".transaction_session_uuid = _session_uuid
and (
"transaction".transaction_payment_state = 'started'
or
"transaction".transaction_payment_state = 'completed'
);
end; $function$;
create or replace function sos.create_address(_name text, _company text, _street1 text, _street2 text, _city text, _state text, _zip text, _country text, _phone text, _easypost_id text)
returns setof sos."address"
language plpgsql

@ -1,16 +1,37 @@
import {useEffect} from 'react'
import Router from 'next/router'
import {DateTime} from 'luxon'
CheckoutComplete.getInitialProps = async function({ctx: {query: {session_id}, axios}}){
const {data: {status, order}} = await axios.post('/api/orders/current/checkout/verify', {session_id})
return {status, order}
const {data: orders} = await axios.get('/api/orders')
const mostRecentOrder = orders.sort(sortOrders)[0]
return {order: mostRecentOrder}
}
export default function CheckoutComplete({status, order}){
export default function CheckoutComplete({order}){
return (
<pre>
{JSON.stringify(order, null, 2)}
</pre>
)
}
function parsePaymentTime({payment}){
if(typeof payment.time === 'string')
payment.time = DateTime.fromISO(payment.time)
return payment.time
}
function sortTransactions(a,b){
const timeA = parsePaymentTime(a)
const timeB = parsePaymentTime(b)
return timeB.diff(timeA).as('seconds')
}
function sortOrders(a,b){
const timePaidA = parsePaymentTime(a.transactions.sort(sortTransactions)[0])
const timePaidB = parsePaymentTime(b.transactions.sort(sortTransactions)[0])
return timePaidB.diff(timePaidA).as('seconds')
}

@ -0,0 +1,39 @@
import {useState, useEffect} from 'react'
import axios from 'axios'
import Router from 'next/router'
CheckoutComplete.getInitialProps = async function({ctx: {query: {session_id}}}){
return {session_id}
}
export default function CheckoutComplete({session_id}){
const [loading, setLoading] = useState(true)
useEffect(()=>{
(async ()=>{
const {data: {status}} = await axios.post('/api/orders/current/checkout/verify', {session_id})
if(status === "succeeded")
Router.push('/store/checkout/complete')
else
setLoading(false)
})()
}, [])
if(loading)
return (
<>
<h2>Checkout</h2>
<p style={{textAlign: 'center'}}>Verifying your payment . . .</p>
</>
)
return (
<>
<h2>Checkout</h2>
<p style={{textAlign: 'center'}}>There was a problem with your payment.</p>
</>
)
}
Loading…
Cancel
Save