d3ro-voice/server/supabase/tests/payple-cancellation-order-scope.integration.sql
Yun Chan ba9ef9741e fix: red-team round 3 hardening across desktop, mobile, core and server
Batch of red-team r3 fixes that were in the working tree before the
2026-09-28 design overhaul, committed as one unit with their tests.

- desktop main: STT timeouts and sidecar, voice recording store, sync
  (credentials, audio, knowledge reindex, push gates), runtime
  provisioner, update policy, AltGr keybindings, voice-command policy,
  dictionary file codec/limits, meeting transcript condensing and a
  local recording ledger so interrupted-session recovery only closes
  meetings this device recorded (a phone's live meeting is left alone).
- mobile: login CSRF via implicit token callbacks rejected, account
  deletion/retention, durable queue retention, knowledge realtime
  without unfiltered DELETE, meeting re-record failure paths, cloud STT
  client, preferences store/resync.
- core: text chunking splits long unbroken transcripts to fit, template
  field policy, dictionary limits, meeting markdown inline handling.
- server: payple webhook policy and cancellation order scope, meeting
  document generation quota, team RPC null-role guard, unified LLM
  quota in-flight accounting, knowledge chunk vector index, meeting
  re-record failure paths (migrations 20260929*).
- ci: portable/runtime feed gates, update-policy schema, Forgejo file
  delete and alias planning.

Four older tests are updated to the new contracts rather than the old
behavior: token-pair auth callbacks are rejected, knowledge realtime no
longer subscribes to DELETE, long transcript lines are split, and
meeting recovery requires the local recording ledger for empty rows.
2026-09-28 20:45:52 +09:00

207 lines
7.1 KiB
PL/PgSQL

\set ON_ERROR_STOP on
-- Regression: a confirmed refund of an older Payple order (same billing key)
-- must not revoke the period funded by the current order.
-- Covers apply_payment_provider_event from
-- 20260929100011_payple_cancellation_order_scope.sql.
BEGIN;
CREATE OR REPLACE FUNCTION pg_temp.assert_true(condition boolean, message text)
RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN
IF condition IS NOT TRUE THEN
RAISE EXCEPTION 'assertion_failed: %', message;
END IF;
END;
$$;
INSERT INTO auth.users (
id, aud, role, email, encrypted_password, email_confirmed_at,
raw_app_meta_data, raw_user_meta_data, created_at, updated_at
) VALUES (
'20000000-0000-4000-8000-000000000031', 'authenticated', 'authenticated',
'payple-order-scope@example.invalid', crypt('fixture-password', gen_salt('bf')), now(),
'{"provider":"email","providers":["email"]}'::jsonb, '{}'::jsonb, now(), now()
);
DO $$
DECLARE
c_user constant uuid := '20000000-0000-4000-8000-000000000031';
c_payer constant text := 'payer-order-scope';
c_oid1 constant text := 'D3RO-20260801090000-scope01';
c_oid2 constant text := 'D3RO-20260901090000-scope02';
v_result jsonb;
v_period_end timestamptz;
v_cursor_before timestamptz;
v_cursor_after timestamptz;
v_sub public.subscriptions%ROWTYPE;
v_disposition text;
BEGIN
-- Month 1: checkout order oid1.
v_result := public.apply_payment_provider_event(
p_user_id => c_user,
p_provider => 'payple',
p_event_id => 'payment:' || c_oid1,
p_event_created_at => now() - interval '40 days',
p_event_type => 'payment.completed',
p_payload_digest => repeat('a', 64),
p_provider_resource_id => c_payer,
p_tier => 'pro',
p_status => 'active',
p_entitled => true,
p_current_period_start => now() - interval '40 days',
p_current_period_end => now() - interval '10 days',
p_auto_renewing => true,
p_provider_customer_id => c_payer,
p_provider_order_id => c_oid1
);
PERFORM pg_temp.assert_true((v_result->>'applied')::boolean, 'month 1 applied');
-- Month 2: renewal order oid2 on the same billing key.
v_period_end := now() + interval '20 days';
v_result := public.apply_payment_provider_event(
p_user_id => c_user,
p_provider => 'payple',
p_event_id => 'payment:' || c_oid2,
p_event_created_at => now() - interval '10 days',
p_event_type => 'payment.completed',
p_payload_digest => repeat('b', 64),
p_provider_resource_id => c_payer,
p_tier => 'pro',
p_status => 'active',
p_entitled => true,
p_current_period_start => now() - interval '10 days',
p_current_period_end => v_period_end,
p_auto_renewing => true,
p_provider_customer_id => c_payer,
p_provider_order_id => c_oid2
);
PERFORM pg_temp.assert_true((v_result->>'applied')::boolean, 'month 2 applied');
SELECT last_event_created_at INTO v_cursor_before
FROM public.payment_provider_cursors
WHERE user_id = c_user AND provider = 'payple';
-- Support refunds oid1; Payple confirms the cancellation webhook.
v_result := public.apply_payment_provider_event(
p_user_id => c_user,
p_provider => 'payple',
p_event_id => 'cancel:' || c_oid1 || ':승인취소완료',
p_event_created_at => now(),
p_event_type => 'webhook.payment_canceled',
p_payload_digest => repeat('c', 64),
p_provider_resource_id => c_payer,
p_tier => 'free',
p_status => 'canceled',
p_entitled => false,
p_current_period_start => NULL,
p_current_period_end => now(),
p_cancel_at => now(),
p_auto_renewing => false,
p_provider_customer_id => c_payer,
p_provider_order_id => c_oid1
);
PERFORM pg_temp.assert_true(
NOT (v_result->>'applied')::boolean
AND v_result->>'reason' = 'canceled_order_not_current',
'refund of an older order is ignored, got ' || v_result::text
);
SELECT * INTO v_sub FROM public.subscriptions WHERE user_id = c_user;
PERFORM pg_temp.assert_true(
v_sub.tier = 'pro'
AND v_sub.provider = 'payple'
AND v_sub.auto_renewing
AND v_sub.payple_pay_oid = c_oid2
AND v_sub.current_period_end = v_period_end
AND v_sub.provider_resource_id = c_payer,
'current paid period and renewals are preserved'
);
SELECT disposition INTO v_disposition
FROM public.payment_provider_events
WHERE provider = 'payple' AND event_id = 'cancel:' || c_oid1 || ':승인취소완료';
PERFORM pg_temp.assert_true(v_disposition = 'ignored', 'ignored event is recorded');
SELECT last_event_created_at INTO v_cursor_after
FROM public.payment_provider_cursors
WHERE user_id = c_user AND provider = 'payple';
PERFORM pg_temp.assert_true(
v_cursor_after = v_cursor_before,
'ignored refund does not advance the ordering cursor'
);
-- Refunding the current order still revokes entitlement.
v_result := public.apply_payment_provider_event(
p_user_id => c_user,
p_provider => 'payple',
p_event_id => 'cancel:' || c_oid2 || ':승인취소완료',
p_event_created_at => now(),
p_event_type => 'webhook.payment_canceled',
p_payload_digest => repeat('d', 64),
p_provider_resource_id => c_payer,
p_tier => 'free',
p_status => 'canceled',
p_entitled => false,
p_current_period_start => NULL,
p_current_period_end => now(),
p_cancel_at => now(),
p_auto_renewing => false,
p_provider_customer_id => c_payer,
p_provider_order_id => c_oid2
);
PERFORM pg_temp.assert_true(
(v_result->>'applied')::boolean,
'refund of the current order is applied, got ' || v_result::text
);
SELECT * INTO v_sub FROM public.subscriptions WHERE user_id = c_user;
PERFORM pg_temp.assert_true(
v_sub.tier = 'free' AND v_sub.provider = 'none' AND NOT v_sub.auto_renewing,
'current order refund revokes the subscription'
);
-- Revocations that name no order (scheduled expiry, manage) are unaffected.
v_result := public.apply_payment_provider_event(
p_user_id => c_user,
p_provider => 'payple',
p_event_id => 'payment:D3RO-20260910090000-scope03',
p_event_created_at => now() + interval '1 second',
p_event_type => 'payment.completed',
p_payload_digest => repeat('e', 64),
p_provider_resource_id => c_payer,
p_tier => 'pro',
p_status => 'active',
p_entitled => true,
p_current_period_start => now(),
p_current_period_end => now() + interval '1 month',
p_auto_renewing => false,
p_provider_customer_id => c_payer,
p_provider_order_id => 'D3RO-20260910090000-scope03'
);
PERFORM pg_temp.assert_true((v_result->>'applied')::boolean, 're-entitled');
v_result := public.apply_payment_provider_event(
p_user_id => c_user,
p_provider => 'payple',
p_event_id => 'scheduled-expire:order-scope',
p_event_created_at => now() + interval '2 seconds',
p_event_type => 'subscription.scheduled_expiry',
p_payload_digest => repeat('f', 64),
p_provider_resource_id => c_payer,
p_tier => 'free',
p_status => 'expired',
p_entitled => false,
p_provider_order_id => NULL
);
PERFORM pg_temp.assert_true(
(v_result->>'applied')::boolean,
'order-less revocation still applies, got ' || v_result::text
);
END;
$$;
ROLLBACK;
SELECT 'payple_cancellation_order_scope_ok' AS result;