Scrub Square card and customer references on anonymization (GDPR)
anonymize_user, delete_guest_user, and AnonymizeStaleGuestAccounts now NULL square_card_id and square_customer_id on user_saved_cards (7-year retained_until soft-delete kept for financial records). Make square_card_id nullable in the schema. GDPR export refunds join fixed to include gift-card-purchase refunds. Add scrub assertions to the GDPR and stale-guest test suites.
This commit is contained in:
@@ -936,14 +936,18 @@ BEGIN
|
||||
-- Scrub social login identities (immutable_id is PII from OAuth providers)
|
||||
DELETE FROM user_social_logins WHERE user_id = target_id;
|
||||
|
||||
-- Soft-delete all saved cards and clear PCI data
|
||||
-- Soft-delete all saved cards, clear PCI data, and scrub Square references
|
||||
-- (square_card_id / square_customer_id are external-system identifiers and
|
||||
-- MUST be NULLed for GDPR right-to-erasure — a scrubbed card has no Square id).
|
||||
UPDATE user_saved_cards
|
||||
SET deleted_at = NOW(),
|
||||
retained_until = NOW() + INTERVAL '7 years',
|
||||
last_4 = 'XXXX',
|
||||
fingerprint = NULL,
|
||||
exp_month = 1,
|
||||
exp_year = 2000
|
||||
exp_year = 2000,
|
||||
square_card_id = NULL,
|
||||
square_customer_id = NULL
|
||||
WHERE user_id = target_id;
|
||||
|
||||
-- Expire all pending verification codes
|
||||
@@ -998,6 +1002,9 @@ BEGIN
|
||||
fingerprint = NULL,
|
||||
exp_month = 1,
|
||||
exp_year = 2000,
|
||||
-- Scrub Square references (external-system identifiers) before unlinking
|
||||
square_card_id = NULL,
|
||||
square_customer_id = NULL,
|
||||
user_id = NULL
|
||||
WHERE user_id = target_id;
|
||||
|
||||
@@ -1116,8 +1123,10 @@ BEGIN
|
||||
)
|
||||
ORDER BY p.created_at DESC), '[]'::json)
|
||||
FROM payments p
|
||||
JOIN bookings b ON p.booking_id = b.id
|
||||
LEFT JOIN bookings b ON p.booking_id = b.id
|
||||
LEFT JOIN gift_cards gc ON p.gift_card_id = gc.id
|
||||
WHERE b.user_id = target_user_id
|
||||
OR gc.created_by = target_user_id
|
||||
),
|
||||
'patch_tests', (
|
||||
SELECT COALESCE(json_agg(
|
||||
@@ -1208,8 +1217,10 @@ BEGIN
|
||||
) ORDER BY r.created_at DESC), '[]'::json)
|
||||
FROM refunds r
|
||||
JOIN payments p ON r.payment_id = p.id
|
||||
JOIN bookings b ON p.booking_id = b.id
|
||||
LEFT JOIN bookings b ON p.booking_id = b.id
|
||||
LEFT JOIN gift_cards gc ON p.gift_card_id = gc.id
|
||||
WHERE b.user_id = target_user_id
|
||||
OR gc.created_by = target_user_id
|
||||
),
|
||||
'social_logins', (
|
||||
SELECT COALESCE(json_agg(json_build_object(
|
||||
@@ -1980,7 +1991,10 @@ $$ LANGUAGE plpgsql;
|
||||
CREATE TABLE user_saved_cards (
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_user_saved_card_id(),
|
||||
user_id CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
square_card_id TEXT NOT NULL,
|
||||
-- NULL once a card is scrubbed by anonymization (anonymize_user,
|
||||
-- delete_guest_user, AnonymizeStaleGuestAccounts). A scrubbed card has no
|
||||
-- Square id — the reference must be removable for GDPR right-to-erasure.
|
||||
square_card_id TEXT,
|
||||
-- Square customer profile id (P14): populated lazily the first time the
|
||||
-- user SAVES a card, then reused for every subsequent card save. NULL for
|
||||
-- rows created before provisioning was introduced. One-off (non-save)
|
||||
|
||||
Reference in New Issue
Block a user