Saque e Pague — assinatura em dinheiro
Terceira forma de pagamento do Baita: endpoints, autenticação por API key, concorrência com advisory lock e ciclo de vida.
🆕 Não documentada no PDF de 07/08 — implementada entre 14/07 e 23/07/2026 (feat/saque-pague-integration, PR #859).
O que é: forma de pagamento em espécie via rede de correspondentes bancários físicos "Saque e Pague". O usuário paga em dinheiro no balcão e o sistema do próprio parceiro (externo à Urbis) chama a API da Urbis informando CPF + telefone para liberar o acesso. Não há gateway de pagamento envolvido — não há Pagar.me nesse fluxo.
Autenticação
SaquePagueGuard: header accesskey comparado a SAQUE_PAGUE_ACCESS_KEY (env); fixa whitelabelId = BAITA_WHITELABELID no request (o parceiro não escolhe clube) e falha fechado se a env não estiver configurada.
POST /subscription/saque-pague/subscription-status
- Entrada: { cpfCnpj }.
- Se já existe assinatura ativa → 409 ConflictException (não apenas false).
- Para assinatura de cartão recorrente, consulta a Pagar.me como fonte de verdade do status antes de responder.
POST /subscription/saque-pague/subscription
- Entrada: { cpfCnpj, telephone }.
- Plano fixo no código (não env): SAQUE_PAGUE_PLAN_ID = '84e4e57e-579d-43a4-9428-12124500ea11'.
- isFullAccess = plan.title.includes('completo') define codeType (lotteryAndIncentives vs onlyLottery) e a tag de acesso — mesma convenção frágil por nome de plano.
- Usuário novo: enriquecido via Nova Vida (resiliente); senha = CPF (bcrypt); nome provisório = CPF se Nova Vida não retornar nome.
- Usuário existente com assinatura anterior: a anterior é desativada e o telefone informado sobrescreve o cadastrado.
- Vigência: nextInvoiceDate = addMonths(hoje, plan.collectionInterval) — mesma regra do cartão.
- Grava Subscription.metadata = { payment_method: 'cash', project: 'baita', origin: 'saque-pague' }, subscriptionIdInGateway: null e User.hasSubscriptionMechanics: false.
- Concede UserAccessGrant + tags de acesso/origem e dispara upsert na DE do Salesforce com pagamento: 'cash' (fire-and-forget, pós-commit).
Concorrência
Pré-checagem fora da transação + rechecagem dentro de $transaction sob pg_advisory_xact_lock (namespace 8274, chave = hash do CPF), evitando duplo cadastro em chamadas simultâneas do mesmo CPF. Não existe idempotency key de transação externa: uma tentativa de usar externalTransactionId foi implementada e depois removida por decisão de produto — divergência intencional da regra geral do CLAUDE.md §4, registrada como decisão consciente.
Ciclo de vida compartilhado com PIX
cash é tratado como "assinatura por janela de dias" para efeito de expiração no mesmo cron que expira PIX (invalidateBaitaPixUsersJob), mas usuários cash não recebem e-mail nem novo PIX de renovação. Para elegibilidade de sorteio, cash é tratado como cartão (subtrai collectionInterval em meses da data de renovação), não como PIX.
Consumidor: exclusivamente o sistema do parceiro Saque e Pague (via API key). Não é usado pelo Manager nem pelo app Baita.