feat: add customer cart with expiry, shipping addresses with USPS validation, and multi-item PayPal checkout
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { pool } from '../db';
|
||||
import { requireCustomer } from '../middleware/customerAuth';
|
||||
import { validateAddress, uspsConfigured } from '../usps';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', requireCustomer, async (req: Request, res: Response) => {
|
||||
const { rows } = await pool.query(
|
||||
`SELECT * FROM shipping_addresses WHERE customer_id = $1 ORDER BY is_default DESC, created_at DESC`,
|
||||
[req.customerId]
|
||||
);
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
router.post('/', requireCustomer, async (req: Request, res: Response) => {
|
||||
const { fullName, addressLine1, addressLine2, city, state, postalCode, country, isDefault } = req.body;
|
||||
if (!fullName || !addressLine1 || !city || !state || !postalCode) {
|
||||
return res.status(400).json({ error: 'fullName, addressLine1, city, state, and postalCode are required' });
|
||||
}
|
||||
|
||||
let uspsResult = { validated: false, deliverable: null as boolean | null, standardized: null as Record<string, unknown> | null, reason: undefined as string | undefined };
|
||||
if ((country || 'US') === 'US') {
|
||||
uspsResult = await validateAddress({ addressLine1, addressLine2, city, state, postalCode });
|
||||
}
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
if (isDefault) {
|
||||
await client.query(`UPDATE shipping_addresses SET is_default = false WHERE customer_id = $1`, [req.customerId]);
|
||||
}
|
||||
const { rows } = await client.query(
|
||||
`INSERT INTO shipping_addresses
|
||||
(customer_id, full_name, address_line1, address_line2, city, state, postal_code, country, is_default, usps_validated, usps_standardized)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *`,
|
||||
[
|
||||
req.customerId, fullName, addressLine1, addressLine2 || null, city, state, postalCode,
|
||||
country || 'US', !!isDefault, uspsResult.deliverable === true, uspsResult.standardized
|
||||
]
|
||||
);
|
||||
await client.query('COMMIT');
|
||||
res.status(201).json({ address: rows[0], uspsCheck: uspsResult, uspsConfigured: uspsConfigured() });
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK');
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'internal error' });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/:id', requireCustomer, async (req: Request, res: Response) => {
|
||||
const { fullName, addressLine1, addressLine2, city, state, postalCode, country, isDefault } = req.body;
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
if (isDefault) {
|
||||
await client.query(`UPDATE shipping_addresses SET is_default = false WHERE customer_id = $1`, [req.customerId]);
|
||||
}
|
||||
const { rows } = await client.query(
|
||||
`UPDATE shipping_addresses
|
||||
SET full_name=$1, address_line1=$2, address_line2=$3, city=$4, state=$5, postal_code=$6, country=$7, is_default=$8,
|
||||
usps_validated = false, usps_standardized = NULL
|
||||
WHERE id=$9 AND customer_id=$10 RETURNING *`,
|
||||
[fullName, addressLine1, addressLine2 || null, city, state, postalCode, country || 'US', !!isDefault, req.params.id, req.customerId]
|
||||
);
|
||||
if (!rows.length) { await client.query('ROLLBACK'); return res.status(404).json({ error: 'not found' }); }
|
||||
await client.query('COMMIT');
|
||||
res.json(rows[0]);
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK');
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'internal error' });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/:id', requireCustomer, async (req: Request, res: Response) => {
|
||||
await pool.query(`DELETE FROM shipping_addresses WHERE id = $1 AND customer_id = $2`, [req.params.id, req.customerId]);
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
router.post('/:id/set-default', requireCustomer, async (req: Request, res: Response) => {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
await client.query(`UPDATE shipping_addresses SET is_default = false WHERE customer_id = $1`, [req.customerId]);
|
||||
const { rows } = await client.query(
|
||||
`UPDATE shipping_addresses SET is_default = true WHERE id = $1 AND customer_id = $2 RETURNING *`,
|
||||
[req.params.id, req.customerId]
|
||||
);
|
||||
await client.query('COMMIT');
|
||||
res.json(rows[0]);
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK');
|
||||
res.status(500).json({ error: 'internal error' });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user