██████╗ ██████╗ ██████╗ ██████╗ █████╗ ██╗ ██╗
██╔══██╗██╔══██╗██╔═══██╗██╔══██╗██╔══██╗╚██╗ ██╔╝
██████╔╝██████╔╝██║ ██║██████╔╝███████║ ╚████╔╝
██╔═══╝ ██╔══██╗██║ ██║██╔═══╝ ██╔══██║ ╚██╔╝
██║ ██║ ██║╚██████╔╝██║ ██║ ██║ ██║
╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
**Gateway de Pagamentos - ProStaff Ecosystem**╔══════════════════════════════════════════════════════════════════════════╗
║ PROPAY - Roda / Iodine (Ruby 3.4) ║
╠══════════════════════════════════════════════════════════════════════════╣
║ Gateway de pagamentos proprietário do ecossistema ProStaff. ║
║ PIX único · PIX recorrente · Carteira interna · Prize pool · p95 < 50ms ║
╚══════════════════════════════════════════════════════════════════════════╝
KEY FEATURES:
- PIX Único - Geração de QR Code dinâmico (EMV/BR Code) via OpenPix
- PIX Recorrente - Protocolo COBR do BACEN via Efi para assinaturas automatizadas
- Carteira Interna - Saldo por usuário com débito/crédito atômico e auditoria completa
- Assinaturas SaaS - Planos Pro e Enterprise do ProStaff Analytics Hub com trial
- Prize Pool - Distribuição automática de prêmios dos campeonatos do ArenaBR
- Reembolsos - Cancelamento com crédito na carteira ou PIX de volta (CDC 7 dias)
- Saques - PIX de saída para chave cadastrada com validação BACEN
- Webhooks - Confirmação assíncrona de PIX em < 3s com HMAC-SHA256
- Idempotência -
Idempotency-Keyobrigatória +end_to_end_idUNIQUE no banco - Isolamento - Todas as queries escopadas por
customer_id(sem dados cruzados) - Rate Limiting - Rack::Attack + Redis (30 req/min por IP)
- Observabilidade - Métricas Prometheus + endpoints
/healthe/ready - Alta Performance - Iodine (event loop em C com epoll) + YJIT +
MALLOC_ARENA_MAX=2 - Banco próprio - PostgreSQL 15 isolado (
propay-db) na rede Coolify
TABLE OF CONTENTS:
- 01 · Quick Start
- 02 · Technology Stack
- 03 · Architecture
- 04 · Setup
- 05 · API Endpoints
- 06 · Providers PIX
- 07 · Background Jobs
- 08 · Webhooks
- 09 · Deployment
- 10 · Environment Variables
Docker (Recomendado):
cp .env.example .env
# Edite .env → PROPAY_OPENPIX_APP_ID, PROPAY_OPENPIX_SECRET, PROPAY_PIX_KEY, INTERNAL_JWT_SECRET
docker compose up -d
curl http://localhost:5555/v1/health
# {"status":"ok","version":"1.0.0"}Local (sem Docker):
cp .env.example .env
bundle install
bundle exec sequel -m db/migrations $DATABASE_URL
bundle exec iodine --yjit --yjit-exec-mem-size=8 -p 5555API: http://localhost:5555
Health: http://localhost:5555/v1/health
- Language: Ruby 3.4 com YJIT habilitado
- Framework: Roda 3.103 (tree routing, ~0.1ms overhead)
- HTTP Server: Iodine 0.7 (event loop em C com epoll)
- JSON: Oj 3.17 (3-5x mais rápido que stdlib)
- ORM: Sequel 5.75 + sequel_pg
- Database: PostgreSQL 15 (instância própria
propay-db) - Cache / Locks: Redis 7 + Redlock
- HTTP Client: HTTPX 1.3 (async)
- Background Jobs: Sidekiq 7
- Validação: dry-validation 1.10
- Monitoramento: Prometheus::Client
Posição no ecossistema ProStaff:
INTERNET
|
Traefik (TLS - Let's Encrypt)
|
+-- api.prostaff.gg → prostaff-api (Rails, :3000)
+-- events.prostaff.gg → prostaff-events (Phoenix, :4000)
+-- scraper.prostaff.gg → ProStaff-Scraper (FastAPI, :8000)
+-- propay.prostaff.gg → ProPay (Roda, :5555)
REDE DOCKER: coolify
|
+-- prostaff-api → PostgreSQL, Redis, Meilisearch, Sidekiq
+-- prostaff-events → Redis (compartilhado)
+-- riot-gateway → interno
+-- propay → propay-db (PostgreSQL próprio), Redis DB 1
FRONTENDS (Cloudflare Pages)
+-- ArenaBR
+-- Scrims.lol
+-- ProStaff Analytics Hub
Fluxo de pagamento (PIX único - ArenaBR):
[Frontend] → POST /v1/wallet/deposit
↓
ProPay cria Charge + OpenPix API
↓
← {qr_code, qr_code_url, txid}
↓
[Usuário paga no banco]
↓
OpenPix → POST /v1/webhooks/openpix
↓
PixWebhookJob (Sidekiq)
↓
WalletService.credit! (SELECT FOR UPDATE)
↓
propay_wallets.balance_cents += amount
propay_wallet_transactions INSERT (imutável)
Estrutura de diretórios:
propay/
├── app/
│ ├── handlers/ # Roda route handlers
│ ├── services/ # Lógica de negócio
│ ├── jobs/ # Sidekiq workers
│ ├── models/ # Sequel models
│ ├── providers/ # OpenPix / Efi
│ ├── middleware/ # Auth, idempotência, rate limit
│ └── validators/ # dry-validation schemas
├── db/migrations/
├── config/
├── config.ru
├── Gemfile
├── Dockerfile
└── docker-compose.ymlPré-requisitos:
- Ruby 3.4+
- PostgreSQL 15+
- Redis 7+
- CNPJ ativo com chave PIX (MEI basta)
- Conta OpenPix ou Efi
# 1. Dependências
bundle install
# 2. Banco
createdb propay_development
bundle exec sequel -m db/migrations $DATABASE_URL
# 3. Variáveis
cp .env.example .env
# 4. Rodar
bundle exec sidekiq -c config/sidekiq.yml & # Terminal 1
bundle exec iodine --yjit -p 5555 # Terminal 2Base URL: https://propay.prostaff.gg/v1/
Todos os endpoints exigem Authorization: Bearer <jwt> (mesmo JWT do prostaff-api).
Todas as mutações exigem Idempotency-Key: <uuid-v4>.
GET /v1/health- Status do serviçoGET /v1/ready- Readiness (DB + Redis)
POST /v1/charges- Criar cobrançaGET /v1/charges/:txid- ConsultarDELETE /v1/charges/:txid- CancelarPOST /v1/charges/:txid/refund- Reembolso CDC (janela de 7 dias)
Exemplo POST /v1/charges:
{
"amount_cents": 10000,
"description": "Inscrição Copa ArenaBR #1",
"reference_type": "tournament_registration",
"reference_id": 42,
"expires_in_seconds": 3600
}Response 201:
{
"data": {
"txid": "7978c0c97ea847e78e8849634473c1f9",
"status": "active",
"amount_cents": 10000,
"qr_code": "00020126580014br.gov.bcb.brcode...",
"qr_code_url": "https://propay.prostaff.gg/qr/7978c0c9",
"expires_at": "2026-05-05T15:00:00Z"
}
}POST /v1/subscriptions- Criar (com trial)GET /v1/subscriptions/:idGET /v1/subscriptions/by_owner/:owner_idPATCH /v1/subscriptions/:id/cancelGET /v1/subscriptions/:id/charges
GET /v1/walletGET /v1/wallet/transactionsPOST /v1/wallet/depositPOST /v1/wallet/debitPOST /v1/wallet/payoutsGET /v1/wallet/payouts/:id
GET /v1/admin/dashboard- Resumo financeiro geralGET /v1/admin/subscriptions- Todas as assinaturasGET /v1/admin/charges- Todas as cobrançasGET /v1/admin/wallets- Todas as carteirasPOST /v1/tournaments/:id/distribute_prizesGET /v1/tournaments/:id/financial_report
GET /metrics- Métricas Prometheus (proteger via Traefik em produção)
Status de cobrança:
| Status | Descrição |
|---|---|
pending |
Aguardando geração pelo provider |
active |
QR Code gerado |
paid |
Confirmado via webhook |
expired |
Expirou sem pagamento |
cancelled |
Cancelada manualmente |
refunded |
Reembolsada |
| Recurso | OpenPix (Fase 1) | Efi / Gerencianet (Fase 2) |
|---|---|---|
| PIX único | Sim | Sim |
| COBR (recorrente BACEN) | Não | Sim |
| Webhook | HMAC-SHA256 | mTLS + HMAC |
| Taxa | 0% (freemium PME) | ~0,99% |
| MEI aceito | Sim | Sim |
Abstração unificada - trocar de provider só muda PROPAY_PROVIDER.
| Job | Trigger | Responsabilidade |
|---|---|---|
PixWebhookJob |
Webhook PIX | Credita carteira / ativa assinatura |
TierSyncJob |
Assinatura muda | PATCH interno no prostaff-api |
RecurringChargeJob |
Cron diário 08:00 BRT | Gera cobranças recorrentes |
SubscriptionRetryJob |
Charge expirada | Retry D+1/D+2/D+3 |
PrizeDistributionJob |
Campeonato finalizado | Distribui prêmios |
ExpireChargesJob |
Cron 15 min | Marca cobranças expiradas |
PayoutProcessingJob |
Saque solicitado | Valida anti-fraude 24h + debita wallet + PIX de saída |
SidekiqHealthJob |
Cron 5 min | Alerta dead queue > 5, atualiza gauge Prometheus |
- Sempre responde
200 OKem < 100ms (processamento assíncrono via Sidekiq) - Validação HMAC-SHA256 obrigatória
end_to_end_idUNIQUE → idempotente
docker-compose.yml (produção):
services:
propay-db:
image: postgres:15-alpine
environment:
POSTGRES_DB: propay_production
POSTGRES_USER: propay
POSTGRES_PASSWORD: ${PROPAY_DB_PASSWORD}
volumes:
- propay_db_data:/var/lib/postgresql/data
networks:
- coolify
propay:
build: .
environment: &propay-env
DATABASE_URL: postgresql://propay:${PROPAY_DB_PASSWORD}@propay-db:5432/propay_production
REDIS_URL: redis://default:${REDIS_PASSWORD}@redis:6379/1
INTERNAL_JWT_SECRET: ${INTERNAL_JWT_SECRET}
PROPAY_OPENPIX_APP_ID: ${PROPAY_OPENPIX_APP_ID}
PROPAY_OPENPIX_SECRET: ${PROPAY_OPENPIX_SECRET}
PROPAY_PIX_KEY: ${PROPAY_PIX_KEY}
PROSTAFF_API_URL: http://api:3000
MALLOC_ARENA_MAX: 2
labels:
- "traefik.enable=true"
- "traefik.http.routers.propay.rule=Host(`propay.prostaff.gg`)"
- "traefik.http.routers.propay.tls.certresolver=letsencrypt"
- "traefik.http.services.propay.loadbalancer.server.port=5555"
networks:
- coolify
depends_on:
propay-db:
condition: service_healthy
propay-worker:
build: .
command: bundle exec sidekiq -c config/sidekiq.yml
environment: *propay-env
networks:
- coolify
volumes:
propay_db_data:
networks:
coolify:
external: true| Variável | Obrigatória | Descrição |
|---|---|---|
DATABASE_URL |
Sim | PostgreSQL do propay-db |
REDIS_URL |
Sim | Redis DB 1 |
INTERNAL_JWT_SECRET |
Sim | Mesmo do prostaff-api |
PROPAY_OPENPIX_APP_ID |
Fase 1 | OpenPix App ID |
PROPAY_OPENPIX_SECRET |
Fase 1 | Secret HMAC |
PROPAY_EFI_CLIENT_ID |
Fase 2 | Efi Client ID |
PROPAY_EFI_CLIENT_SECRET |
Fase 2 | Efi Client Secret |
PROPAY_EFI_CERTIFICATE_PATH |
Fase 2 | Caminho .p12 |
PROPAY_PIX_KEY |
Sim | Chave PIX da conta |
PROPAY_PROVIDER |
Não | openpix (default) ou efi |
PROSTAFF_API_URL |
Sim | URL interna do prostaff-api |
PROPAY_DB_PASSWORD |
Sim | Senha do banco próprio |
MALLOC_ARENA_MAX |
Não | 2 (recomendado) |
PORT |
Não | 5555 (default) |
Pré-requisito externo: CNPJ ativo (MEI é suficiente).
LICENSE:
© 2026 ProStaff.gg. All rights reserved.
Released under: GNU Affero General Public License v3.0 (AGPLv3)
Prostaff.gg isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing Riot Games properties. Riot Games, and all associated properties are trademarks or registered trademarks of Riot Games, Inc.