Перейти к содержанию

apply_payment

Сигнатура: apply_payment("p_tg_user_id" bigint, "p_charge_id" "text", "p_provider" "text", "p_currency" "text", "p_amount" integer, "p_product_kind" "text", "p_product_code" "text", "p_credits" integer DEFAULT NULL::integer, "p_sub_days" integer DEFAULT NULL::integer) RETURNS "jsonb"
Язык: sql
Security: DEFINER

Тело функции

declare
  v_user_id uuid;
  v_balance integer;
  v_already boolean := false;
  v_credits_to_add integer := coalesce(p_credits, 0);
  v_days integer := coalesce(p_sub_days, 30);
  v_now timestamptz := now();
  v_until timestamptz;
begin
  -- находим/создаём пользователя по tg_user_id
  select id into v_user_id
  from public.users
  where tg_user_id = p_tg_user_id
  limit 1;

  if v_user_id is null then
    insert into public.users (tg_user_id)
    values (p_tg_user_id)
    returning id into v_user_id;
  end if;

  -- идемпотентность: платёж уже записан?
  select exists(select 1 from public.payments where charge_id = p_charge_id)
    into v_already;

  if v_already then
    -- вернуть актуальное состояние без повторных действий
    select coalesce(w.balance, 0) into v_balance
    from public.wallets w
    where w.user_id = v_user_id;

    select subscription_until into v_until from public.users where id = v_user_id;

    return jsonb_build_object(
      'ok', true,
      'already_processed', true,
      'user_id', v_user_id,
      'balance', coalesce(v_balance, 0),
      'subscription_tier', (select subscription_tier from public.users where id=v_user_id),
      'subscription_until', v_until
    );
  end if;

  -- если подписка — по умолчанию добавим 270 кредитов (жёстко, как просили)
  if p_product_kind = 'subscription' then
    if v_credits_to_add is null or v_credits_to_add = 0 then
      v_credits_to_add := 270;
    end if;

    -- продление/установка подписки
    update public.users
    set subscription_tier  = p_product_code,
        subscription_until = greatest(coalesce(subscription_until, v_now), v_now) + (v_days || ' days')::interval
    where id = v_user_id;

  elsif p_product_kind = 'credits' then
    -- для пакетов кредитов: если p_credits не пришёл — можно прошить логику мэппинга по product_code
    if v_credits_to_add is null or v_credits_to_add = 0 then
      -- пример простого мэппинга (при необходимости поменяйте под ваши payload'ы)
      if p_product_code = 'pack100' then v_credits_to_add := 100;
      elsif p_product_code = 'pack300' then v_credits_to_add := 300;
      elsif p_product_code = 'pack1000' then v_credits_to_add := 1000;
      else v_credits_to_add := 0;
      end if;
    end if;
  end if;

  -- апдейт кошелька (balance)
  insert into public.wallets (user_id, balance)
  values (v_user_id, coalesce(v_credits_to_add,0))
  on conflict (user_id)
  do update set balance = public.wallets.balance + coalesce(excluded.balance,0)
  returning balance into v_balance;

  -- записываем платёж
  insert into public.payments (
    user_id, provider, charge_id, currency, amount,
    product_kind, product_code, credits_added
  ) values (
    v_user_id, p_provider, p_charge_id, p_currency, p_amount,
    p_product_kind, p_product_code, coalesce(v_credits_to_add, 0)
  );

  select subscription_until into v_until from public.users where id = v_user_id;

  return jsonb_build_object(
    'ok', true,
    'already_processed', false,
    'user_id', v_user_id,
    'balance', coalesce(v_balance,0),
    'credits_added', coalesce(v_credits_to_add,0),
    'subscription_tier', (select subscription_tier from public.users where id=v_user_id),
    'subscription_until', v_until
  );
end;