feat: Square payment integration, booking flow redesign, and timezone/weekday fixes
- Add Square payment integration (mock + handlers + UI): terminal/online payments, refunds, tips, saved cards, webhooks. Build-tagged dev/prod clients. - Redesign booking flow: Step 4 conditional (deposit only), Step 5 confirmation screen with booking ID, auto-submit on transition. - Redesign schedule modal: 2x3 button grid with Pay Deposit/Pay Early logic. - Add deposit warning banner at Step 1 for users with outstanding deposits. - Fix weekday conversion bug: Go 0=Sunday vs DB 0=Monday mismatch in 6 locations. - Fix timezone bug: UTC vs London time in closing hours validation. - Fix frontend error parsing: plain text backend errors now displayed correctly. - Fix crypto.randomUUID fallback for environments without Web Crypto. - Add 7 new regression tests: closing hours, advance check, active booking limit, weekday conversion, UTC/London, deposit snapshot, exceptional hours. - Fix 3 flaky tests: dynamic dates instead of fixed, no-show timing.
This commit is contained in:
@@ -55,6 +55,21 @@ CREATE OR REPLACE FUNCTION generate_user_id() RETURNS CHAR(12) AS $$ SELECT gene
|
||||
CREATE OR REPLACE FUNCTION generate_service_id() RETURNS CHAR(12) AS $$ SELECT generate_short_id('services'); $$ LANGUAGE sql;
|
||||
CREATE OR REPLACE FUNCTION generate_booking_id() RETURNS CHAR(12) AS $$ SELECT generate_short_id('bookings'); $$ LANGUAGE sql;
|
||||
CREATE OR REPLACE FUNCTION generate_payment_id() RETURNS CHAR(12) AS $$ SELECT generate_short_id('payments'); $$ LANGUAGE sql;
|
||||
CREATE OR REPLACE FUNCTION generate_user_saved_card_id() RETURNS CHAR(12) AS $$
|
||||
SELECT generate_short_id('user_saved_cards');
|
||||
$$ LANGUAGE sql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION generate_refund_id() RETURNS CHAR(12) AS $$
|
||||
SELECT generate_short_id('refunds');
|
||||
$$ LANGUAGE sql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION generate_affiliate_payout_id() RETURNS CHAR(12) AS $$
|
||||
SELECT generate_short_id('affiliate_payouts');
|
||||
$$ LANGUAGE sql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION generate_square_deposit_id() RETURNS CHAR(12) AS $$
|
||||
SELECT generate_short_id('square_deposits');
|
||||
$$ LANGUAGE sql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION generate_verification_code() RETURNS CHAR(12) AS $$ SELECT substr(encode(gen_random_bytes(6), 'hex'), 1, 12); $$ LANGUAGE sql;
|
||||
|
||||
@@ -384,6 +399,11 @@ CREATE TABLE payments (
|
||||
vat_rate NUMERIC(5,2),
|
||||
vat_amount NUMERIC(10,2),
|
||||
net_amount NUMERIC(10,2),
|
||||
user_saved_card_id CHAR(12),
|
||||
square_payment_id TEXT,
|
||||
square_deposit_id CHAR(12),
|
||||
idempotency_key VARCHAR(64) UNIQUE,
|
||||
fees NUMERIC(10,2) DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by CHAR(12)
|
||||
@@ -392,6 +412,8 @@ CREATE TABLE payments (
|
||||
CREATE INDEX idx_payments_bookingid ON payments(booking_id);
|
||||
CREATE INDEX idx_payments_status ON payments(status);
|
||||
CREATE INDEX idx_payments_createdat ON payments(created_at);
|
||||
CREATE INDEX idx_payments_booking_id_status ON payments(booking_id, status);
|
||||
CREATE INDEX idx_payments_created_at_status ON payments(created_at, status);
|
||||
|
||||
-- =======================================
|
||||
-- LOYALTY REDEMPTIONS TABLE
|
||||
@@ -546,10 +568,8 @@ CREATE TABLE user_notification_preferences (
|
||||
|
||||
CREATE INDEX idx_user_notification_preferences_user_id ON user_notification_preferences(user_id);
|
||||
|
||||
CREATE INDEX idx_payments_booking_id_status ON payments(booking_id, status);
|
||||
CREATE INDEX idx_bookings_start_time_status ON bookings(start_time, status);
|
||||
CREATE INDEX idx_users_created_at ON users(created_at);
|
||||
CREATE INDEX idx_payments_created_at_status ON payments(created_at, status);
|
||||
|
||||
create table images (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
@@ -1293,6 +1313,78 @@ BEGIN
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- =======================================
|
||||
-- USER SAVED CARDS TABLE (Square Integration)
|
||||
-- =======================================
|
||||
|
||||
CREATE TABLE user_saved_cards (
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_user_saved_card_id(),
|
||||
user_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
square_card_id TEXT NOT NULL UNIQUE,
|
||||
brand TEXT NOT NULL,
|
||||
last_4 TEXT NOT NULL,
|
||||
exp_month INT NOT NULL,
|
||||
exp_year INT NOT NULL,
|
||||
fingerprint TEXT,
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
deleted_by CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
retained_until TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_user_saved_cards_user ON user_saved_cards(user_id);
|
||||
CREATE INDEX idx_user_saved_cards_fingerprint ON user_saved_cards(fingerprint);
|
||||
CREATE INDEX idx_user_saved_cards_active ON user_saved_cards(user_id, deleted_at) WHERE deleted_at IS NULL;
|
||||
|
||||
-- =======================================
|
||||
-- REFUNDS TABLE
|
||||
-- =======================================
|
||||
|
||||
CREATE TABLE refunds (
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_refund_id(),
|
||||
payment_id CHAR(12) NOT NULL REFERENCES payments(id) ON DELETE CASCADE,
|
||||
booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE CASCADE,
|
||||
amount NUMERIC(10,2) NOT NULL CHECK (amount > 0),
|
||||
square_refund_id TEXT,
|
||||
status payment_status NOT NULL DEFAULT 'pending',
|
||||
reason TEXT NOT NULL,
|
||||
created_by CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_refunds_payment ON refunds(payment_id);
|
||||
CREATE INDEX idx_refunds_booking ON refunds(booking_id);
|
||||
|
||||
-- =======================================
|
||||
-- AFFILIATE PAYOUTS TABLE
|
||||
-- =======================================
|
||||
|
||||
CREATE TABLE affiliate_payouts (
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_affiliate_payout_id(),
|
||||
affiliate_id CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
amount NUMERIC(10,2) CHECK (amount >= 0),
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_affiliate_payouts_affiliate ON affiliate_payouts(affiliate_id);
|
||||
|
||||
-- =======================================
|
||||
-- SQUARE DEPOSITS TABLE (Bank Reconciliation)
|
||||
-- =======================================
|
||||
|
||||
CREATE TABLE square_deposits (
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_square_deposit_id(),
|
||||
square_deposit_id TEXT,
|
||||
amount NUMERIC(10,2) NOT NULL,
|
||||
fees_deducted NUMERIC(10,2),
|
||||
deposited_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_square_deposits_deposited ON square_deposits(deposited_at);
|
||||
|
||||
-- =======================================
|
||||
-- FUNCTION USAGE SUMMARY
|
||||
-- =======================================
|
||||
|
||||
Reference in New Issue
Block a user