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

apply_telegram_payment

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

Тело функции

declare
  v_user_id     uuid;
  v_payment_id  uuid;
  v_balance     numeric;
  v_plan        text;
  v_plan_until  timestamptz;
begin
  -- 0) ensure user exists (или создаём скелет)
  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, tg_username, plan, plan_until, lang)
    values (p_tg_user_id, null, 'free', null, coalesce(p_lang, 'ru'))
    returning id into v_user_id;
  else
    if p_lang is not null then
      update public.users set lang = p_lang where id = v_user_id;
    end if;
  end if;

  -- 1) идемпотентность по charge_id
  select id into v_payment_id
  from public.payments
  where charge_id = p_charge_id
  limit 1;

  if v_payment_id is not null then
    select balance into v_balance from public.wallets where user_id = v_user_id;
    select plan, plan_until into v_plan, v_plan_until from public.users where id = v_user_id;

    return jsonb_build_object(
      'ok', true,
      'idempotent', true,
      'user_id', v_user_id,
      'payment_id', v_payment_id,
      'plan', v_plan,
      'plan_until', v_plan_until,
      'balance', coalesce(v_balance, 0)
    );
  end if;

  -- 2) пишем платеж
  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(p_credits_added, 0)
  )
  returning id into v_payment_id;

  -- 3) начисляем кредиты (если > 0): tx + апдейт баланса
  if coalesce(p_credits_added, 0) > 0 then
    insert into public.wallet_tx (user_id, kind, amount, model, meta)
    values (
      v_user_id,
      'credit_add',
      (p_credits_added)::numeric,
      null,
      jsonb_build_object(
        'provider', p_provider,
        'charge_id', p_charge_id,
        'product_kind', p_product_kind,
        'product_code', p_product_code
      )
    );

    insert into public.wallets (user_id, balance)
    values (v_user_id, (p_credits_added)::numeric)
    on conflict (user_id) do update
      set balance = public.wallets.balance + excluded.balance,
          updated_at = now();
  end if;

  -- 4) обновляем план (если передан)
  if p_plan is not null and coalesce(p_plan_days, 0) > 0 then
    update public.users
    set plan = p_plan,
        plan_until = now() + make_interval(days => p_plan_days)
    where id = v_user_id;
  end if;

  -- 5) итог
  select balance into v_balance from public.wallets where user_id = v_user_id;
  select plan, plan_until into v_plan, v_plan_until from public.users where id = v_user_id;

  return jsonb_build_object(
    'ok', true,
    'user_id', v_user_id,
    'payment_id', v_payment_id,
    'plan', v_plan,
    'plan_until', v_plan_until,
    'new_balance', coalesce(v_balance, 0)
  );
end;