Skip to content

Commit a762568

Browse files
committed
feat: implement wallet 4 ArenaBR
1 parent 5334dbc commit a762568

2 files changed

Lines changed: 163 additions & 0 deletions

File tree

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# frozen_string_literal: true
2+
3+
require 'net/http'
4+
5+
module Api
6+
module V1
7+
# WalletController
8+
#
9+
# Transparent proxy between ArenaBR frontend and the ProPay service.
10+
# All requests are forwarded with the caller's Authorization header so
11+
# ProPay can validate the same JWT (shared INTERNAL_JWT_SECRET is NOT
12+
# used here — the user/player Bearer token is passed through as-is).
13+
#
14+
# Authentication is enforced by BaseController before any action runs.
15+
#
16+
# @example Get wallet balance
17+
# GET /api/v1/wallet
18+
# Authorization: Bearer <user_token>
19+
#
20+
# @example Submit a deposit
21+
# POST /api/v1/wallet/deposit
22+
# Authorization: Bearer <user_token>
23+
# Idempotency-Key: <uuid>
24+
# Body: { "amount": 5000 }
25+
class WalletController < BaseController
26+
# Returns the current user's wallet (balance, currency, status).
27+
#
28+
# @return [JSON] Proxied response from ProPay
29+
def show
30+
proxy_to_propay(:get, '/v1/wallet')
31+
end
32+
33+
# Returns a paginated list of wallet transactions.
34+
#
35+
# @return [JSON] Proxied response from ProPay
36+
def transactions
37+
proxy_to_propay(:get, '/v1/wallet/transactions')
38+
end
39+
40+
# Initiates a deposit request (PIX or other method).
41+
#
42+
# @return [JSON] Proxied response from ProPay
43+
def deposit
44+
proxy_to_propay(
45+
:post,
46+
'/v1/wallet/deposit',
47+
body: request.raw_post,
48+
idempotency_key: request.headers['Idempotency-Key']
49+
)
50+
end
51+
52+
# Returns the status of a specific charge by txid.
53+
#
54+
# @param txid [String] The transaction ID (URL param)
55+
# @return [JSON] Proxied response from ProPay
56+
def charge_status
57+
proxy_to_propay(:get, "/v1/charges/#{params[:txid]}")
58+
end
59+
60+
# Creates a payout request.
61+
#
62+
# @return [JSON] Proxied response from ProPay
63+
def create_payout
64+
proxy_to_propay(
65+
:post,
66+
'/v1/wallet/payouts',
67+
body: request.raw_post,
68+
idempotency_key: request.headers['Idempotency-Key']
69+
)
70+
end
71+
72+
# Returns the status of a specific payout.
73+
#
74+
# @param id [String] The payout ID (URL param)
75+
# @return [JSON] Proxied response from ProPay
76+
def payout_status
77+
proxy_to_propay(:get, "/v1/wallet/payouts/#{params[:id]}")
78+
end
79+
80+
private
81+
82+
# Forwards the request to ProPay and renders the response verbatim.
83+
#
84+
# @param method [Symbol] HTTP method (:get or :post)
85+
# @param path [String] ProPay endpoint path
86+
# @param body [String, nil] Raw request body (JSON string)
87+
# @param idempotency_key [String, nil] Value for Idempotency-Key header
88+
# @return [void]
89+
def proxy_to_propay(method, path, body: nil, idempotency_key: nil)
90+
propay_url = ENV.fetch('PROPAY_URL', 'http://propay:5555')
91+
uri = URI("#{propay_url}#{path}")
92+
93+
http = build_http_client(uri)
94+
http_request = build_http_request(method, uri, body, idempotency_key)
95+
96+
response = http.request(http_request)
97+
render json: JSON.parse(response.body), status: response.code.to_i
98+
rescue Net::OpenTimeout, Net::ReadTimeout
99+
render json: { error: { message: 'ProPay timeout' } }, status: :gateway_timeout
100+
rescue StandardError => e
101+
Rails.logger.error("[WALLET] ProPay proxy error for #{path}: #{e.message}")
102+
render json: { error: { message: e.message } }, status: :bad_gateway
103+
end
104+
105+
# Builds a configured Net::HTTP instance.
106+
#
107+
# @param uri [URI] Target URI
108+
# @return [Net::HTTP]
109+
def build_http_client(uri)
110+
http = Net::HTTP.new(uri.host, uri.port)
111+
http.open_timeout = 5
112+
http.read_timeout = 10
113+
http
114+
end
115+
116+
# Builds the HTTP request object with all required headers.
117+
#
118+
# @param method [Symbol] :get or :post
119+
# @param uri [URI] Target URI
120+
# @param body [String, nil] Raw JSON body
121+
# @param idempotency_key [String, nil] Idempotency-Key header value
122+
# @return [Net::HTTPRequest]
123+
def build_http_request(method, uri, body, idempotency_key)
124+
req_class = http_method_class(method)
125+
http_request = req_class.new(uri.request_uri, build_headers(idempotency_key))
126+
http_request.body = body if body.present?
127+
http_request
128+
end
129+
130+
# Maps a symbol to a Net::HTTP request class.
131+
#
132+
# @param method [Symbol] :get or :post
133+
# @return [Class]
134+
def http_method_class(method)
135+
{ get: Net::HTTP::Get, post: Net::HTTP::Post }.fetch(method)
136+
end
137+
138+
# Builds the forwarded headers hash.
139+
#
140+
# @param idempotency_key [String, nil]
141+
# @return [Hash]
142+
def build_headers(idempotency_key)
143+
headers = {
144+
'Content-Type' => 'application/json',
145+
'Authorization' => request.headers['Authorization'],
146+
'User-Agent' => 'prostaff-api/1.0'
147+
}
148+
headers['Idempotency-Key'] = idempotency_key if idempotency_key.present?
149+
headers
150+
end
151+
end
152+
end
153+
end

config/routes.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,16 @@
483483
get 'champion-analytics', to: '/ai_intelligence/controllers/champion_analytics#index'
484484
end
485485

486+
# Wallet Module — proxy to ProPay service
487+
scope '/wallet', as: 'wallet' do
488+
get '/', to: 'wallet#show', as: 'root'
489+
get 'transactions', to: 'wallet#transactions', as: 'transactions'
490+
post 'deposit', to: 'wallet#deposit', as: 'deposit'
491+
post 'payouts', to: 'wallet#create_payout', as: 'payouts'
492+
get 'payouts/:id', to: 'wallet#payout_status', as: 'payout_status'
493+
end
494+
get 'wallet/charges/:txid', to: 'wallet#charge_status', as: 'wallet_charge_status'
495+
486496
# Tournaments Module — ArenaBR double elimination
487497
resources :tournaments, controller: '/tournaments/controllers/tournaments',
488498
only: %i[index show create update] do

0 commit comments

Comments
 (0)