Files
kickkingsapi/schemas/rpc_settle_match_reward.sql
T
2026-08-14 20:55:11 +00:00

201 lines
5.3 KiB
PL/PgSQL

-- One-shot: run in Supabase Dashboard → SQL Editor **after** schemas/rpc_collect_match_entries.sql
-- Atomically settles a match: sets winner_id, pays prize + house fee only from the match escrow,
-- grants prize_cc to both participants, marks settled_at. Idempotent if already settled for same winner.
create or replace function public.settle_match_reward(p_match_id bigint, p_winner_id bigint)
returns jsonb
language plpgsql
security definer
set search_path = public
as $$
declare
v_match public.matches%rowtype;
v_system bigint := 1;
v_entry_fee bigint;
v_prize_cc bigint;
v_bet_fee_pct integer;
v_fee_each bigint;
v_house_total bigint;
v_rc_prize bigint;
v_escrow_rc numeric;
v_bet_fee_raw text;
begin
if p_match_id is null or p_match_id < 1 then
raise exception 'invalid_match';
end if;
if p_winner_id is null or p_winner_id < 1 then
raise exception 'invalid_winner';
end if;
select * into v_match
from public.matches
where id = p_match_id
for update;
if not found then
raise exception 'match_not_found';
end if;
if v_match.settled_at is not null then
if v_match.winner_id = p_winner_id then
return jsonb_build_object(
'ok', true,
'already_settled', true,
'match_id', p_match_id,
'winner_id', v_match.winner_id,
'entry_fee', coalesce(v_match.entry_fee, 0),
'rc_prize', null,
'participant_cc', coalesce(v_match.prize_cc, 100),
'settled_at', v_match.settled_at
);
end if;
raise exception 'winner_already_set';
end if;
if v_match.winner_id is not null and v_match.winner_id <> p_winner_id then
raise exception 'winner_already_set';
end if;
if v_match.entries_collected_at is null then
raise exception 'entries_not_collected';
end if;
if v_match.escrow_user_id is null then
raise exception 'escrow_missing';
end if;
if p_winner_id is distinct from v_match.user_red
and p_winner_id is distinct from v_match.user_blue then
raise exception 'winner_not_participant';
end if;
v_entry_fee := coalesce(v_match.entry_fee, 0);
v_prize_cc := coalesce(v_match.prize_cc, 100);
if v_prize_cc < 0 then
v_prize_cc := 100;
end if;
select value into v_bet_fee_raw
from public.settings
where key = 'bet_fee'
limit 1;
begin
v_bet_fee_pct := coalesce(nullif(trim(v_bet_fee_raw), '')::integer, 10);
exception when others then
v_bet_fee_pct := 10;
end;
if v_bet_fee_pct < 0 or v_bet_fee_pct > 100 then
v_bet_fee_pct := 10;
end if;
v_fee_each := ceil((v_entry_fee::numeric * v_bet_fee_pct) / 100)::bigint;
v_house_total := v_fee_each * 2;
v_rc_prize := (v_entry_fee * 2) - v_house_total;
if v_rc_prize < 0 then
raise exception 'invalid_prize';
end if;
select coalesce(rc, 0) into v_escrow_rc
from public.users
where id = v_match.escrow_user_id
for update;
if not found then
raise exception 'escrow_not_found';
end if;
if v_escrow_rc <> (v_entry_fee * 2) then
raise exception 'escrow_balance_mismatch';
end if;
if v_rc_prize > 0 then
update public.users
set rc = coalesce(rc, 0) - v_rc_prize
where id = v_match.escrow_user_id
and coalesce(rc, 0) >= v_rc_prize;
if not found then
raise exception 'insufficient_escrow';
end if;
update public.users
set rc = coalesce(rc, 0) + v_rc_prize
where id = p_winner_id;
if not found then
raise exception 'winner_not_found';
end if;
insert into public.transactions ("from", "to", amount, remarks, match_id)
values (v_match.escrow_user_id, p_winner_id, v_rc_prize, 'reward', p_match_id);
end if;
if v_house_total > 0 then
update public.users
set rc = coalesce(rc, 0) - v_house_total
where id = v_match.escrow_user_id
and coalesce(rc, 0) >= v_house_total;
if not found then
raise exception 'insufficient_escrow';
end if;
update public.users
set rc = coalesce(rc, 0) + v_house_total
where id = v_system;
if not found then
raise exception 'system_not_found';
end if;
insert into public.transactions ("from", "to", amount, remarks, match_id)
values (v_match.escrow_user_id, v_system, v_house_total, 'bet_fee', p_match_id);
end if;
-- Escrow must be empty after settle.
update public.users
set rc = 0
where id = v_match.escrow_user_id
and coalesce(rc, 0) = 0;
if not found then
raise exception 'escrow_not_zero';
end if;
if v_match.user_red is not null then
update public.users
set cc = coalesce(cc, 0) + v_prize_cc
where id = v_match.user_red;
end if;
if v_match.user_blue is not null then
update public.users
set cc = coalesce(cc, 0) + v_prize_cc
where id = v_match.user_blue;
end if;
update public.matches
set
winner_id = p_winner_id,
settled_at = now()
where id = p_match_id
and settled_at is null;
if not found then
raise exception 'settle_race';
end if;
return jsonb_build_object(
'ok', true,
'already_settled', false,
'match_id', p_match_id,
'winner_id', p_winner_id,
'entry_fee', v_entry_fee,
'rc_prize', v_rc_prize,
'house_fee', v_house_total,
'participant_cc', v_prize_cc,
'settled_at', now()
);
end;
$$;
revoke all on function public.settle_match_reward(bigint, bigint) from public;
grant execute on function public.settle_match_reward(bigint, bigint) to service_role;