tronzap-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +21 -0
  4. data/README.es.md +396 -0
  5. data/README.md +392 -0
  6. data/README.pt-br.md +392 -0
  7. data/README.ru.md +392 -0
  8. data/lib/tronzap/client.rb +239 -0
  9. data/lib/tronzap/coerce.rb +127 -0
  10. data/lib/tronzap/configuration.rb +140 -0
  11. data/lib/tronzap/errors.rb +150 -0
  12. data/lib/tronzap/http_adapter.rb +168 -0
  13. data/lib/tronzap/models/activate_address_rate.rb +11 -0
  14. data/lib/tronzap/models/address_resources.rb +13 -0
  15. data/lib/tronzap/models/aml_risk_factor.rb +28 -0
  16. data/lib/tronzap/models/bandwidth_rate.rb +28 -0
  17. data/lib/tronzap/models/direct_recharge_rate.rb +38 -0
  18. data/lib/tronzap/models/energy_rate.rb +44 -0
  19. data/lib/tronzap/models/enums.rb +26 -0
  20. data/lib/tronzap/models/resource_amounts.rb +13 -0
  21. data/lib/tronzap/models/timestamp.rb +58 -0
  22. data/lib/tronzap/models/transaction_params.rb +46 -0
  23. data/lib/tronzap/requests/address_activation.rb +28 -0
  24. data/lib/tronzap/requests/aml_check.rb +68 -0
  25. data/lib/tronzap/requests/aml_history.rb +34 -0
  26. data/lib/tronzap/requests/bandwidth_transaction.rb +33 -0
  27. data/lib/tronzap/requests/calculate.rb +32 -0
  28. data/lib/tronzap/requests/check_transaction.rb +48 -0
  29. data/lib/tronzap/requests/energy_transaction.rb +42 -0
  30. data/lib/tronzap/requests/estimate_energy.rb +38 -0
  31. data/lib/tronzap/requests/resource_bundle_transaction.rb +51 -0
  32. data/lib/tronzap/requests/validation.rb +56 -0
  33. data/lib/tronzap/response_decoder.rb +115 -0
  34. data/lib/tronzap/responses/account_balance.rb +20 -0
  35. data/lib/tronzap/responses/address_info.rb +26 -0
  36. data/lib/tronzap/responses/aml_check.rb +56 -0
  37. data/lib/tronzap/responses/aml_history.rb +28 -0
  38. data/lib/tronzap/responses/aml_service.rb +30 -0
  39. data/lib/tronzap/responses/calculation.rb +40 -0
  40. data/lib/tronzap/responses/direct_recharge_info.rb +22 -0
  41. data/lib/tronzap/responses/energy_estimate.rb +44 -0
  42. data/lib/tronzap/responses/service_rates.rb +36 -0
  43. data/lib/tronzap/responses/transaction.rb +44 -0
  44. data/lib/tronzap/version.rb +6 -0
  45. data/lib/tronzap-sdk.rb +4 -0
  46. data/lib/tronzap.rb +47 -0
  47. metadata +107 -0
data/README.pt-br.md ADDED
@@ -0,0 +1,392 @@
1
+ # Aluguel de Energia Tron via API
2
+ ## SDK Ruby por TronZap.com
3
+
4
+ [English](README.md) | [Español](README.es.md) | **[Português](README.pt-br.md)** | [Русский](README.ru.md)
5
+
6
+ [![Gem Version](https://img.shields.io/gem/v/tronzap-sdk.svg)](https://rubygems.org/gems/tronzap-sdk)
7
+ [![CI](https://github.com/tron-energy-market/tronzap-sdk-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/tron-energy-market/tronzap-sdk-ruby/actions/workflows/ci.yml)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+
10
+ SDK oficial em Ruby para a API do TronZap.
11
+ Este SDK permite integrar facilmente os serviços TronZap para aluguel de energia TRON.
12
+
13
+ TronZap.com permite [comprar energia TRON](https://tronzap.com/), reduzindo significativamente as taxas nas transferências de USDT (TRC20).
14
+
15
+ 👉 [Registre-se para obter uma chave API](https://tronzap.com) para começar a usar a API TronZap e integrá-la através do SDK.
16
+
17
+ - Site: https://tronzap.com/
18
+ - Referência da API: https://docs.tronzap.com/
19
+ - RubyGems: https://rubygems.org/gems/tronzap-sdk
20
+ - Código-fonte: https://github.com/tron-energy-market/tronzap-sdk-ruby
21
+
22
+ ## Instalação
23
+
24
+ Adicione a gem ao seu Gemfile:
25
+
26
+ ```ruby
27
+ gem "tronzap-sdk"
28
+ ```
29
+
30
+ ou instale-a diretamente:
31
+
32
+ ```bash
33
+ gem install tronzap-sdk
34
+ ```
35
+
36
+ ## Requisitos
37
+
38
+ - Ruby 3.3 ou superior
39
+ - Uma única dependência em tempo de execução, `bigdecimal`. O SDK não depende do Rails.
40
+
41
+ ## Início rápido
42
+
43
+ ```ruby
44
+ require "tronzap"
45
+
46
+ client = Tronzap::Client.new(
47
+ api_token: "seu_api_token",
48
+ api_secret: "seu_api_secret"
49
+ )
50
+
51
+ begin
52
+ balance = client.get_balance
53
+ puts "balance: #{balance.balance.to_s("F")} (deposit to #{balance.address})"
54
+
55
+ # Estima quanta energia uma transferência de USDT precisa e compra exatamente essa quantidade.
56
+ estimate = client.estimate_energy(from_address: "TSenderAddress", to_address: "TRecipientAddress")
57
+
58
+ transaction = client.create_energy_transaction(
59
+ address: "TRecipientAddress",
60
+ energy: estimate.energy,
61
+ duration: 1,
62
+ external_id: "order-42",
63
+ activate_address: true
64
+ )
65
+ puts "transaction #{transaction.id} costs #{transaction.amount.to_s("F")} and is #{transaction.status}"
66
+ rescue Tronzap::Error => e
67
+ warn "TronZap call failed: #{e.message}"
68
+ end
69
+ ```
70
+
71
+ `require "tronzap"` carrega o SDK. Com o Bundler, `gem "tronzap-sdk"` no Gemfile também o carrega.
72
+
73
+ Um passo a passo executável de todas as operações está em
74
+ [`examples/basic_usage.rb`](examples/basic_usage.rb):
75
+
76
+ ```bash
77
+ export TRONZAP_API_TOKEN=seu_api_token
78
+ export TRONZAP_API_SECRET=seu_api_secret
79
+ export TRONZAP_BASE_URL=api.tronzap.com # opcional
80
+ ruby -Ilib examples/basic_usage.rb
81
+ ```
82
+
83
+ Por padrão ele apenas lê e não gasta nada. Com `TRONZAP_ALLOW_PURCHASES=1` ele
84
+ também executa os endpoints que criam transações e verificações AML, que debitam o
85
+ saldo da conta. Veja o comentário no início do arquivo para as demais variáveis
86
+ opcionais.
87
+
88
+ ## Configuração
89
+
90
+ O cliente recebe as duas credenciais do seu painel: o token da API é enviado como
91
+ bearer token, e o segredo da API assina o corpo de cada requisição e nunca é
92
+ enviado. Todo o resto é opcional. Passe as configurações como argumentos nomeados,
93
+ em um bloco ou de ambas as formas; o bloco é executado por último:
94
+
95
+ ```ruby
96
+ client = Tronzap::Client.new(
97
+ api_token: api_token,
98
+ api_secret: api_secret,
99
+ base_url: "api.tronzap.com", # padrão: Tronzap::Configuration::DEFAULT_BASE_URL
100
+ timeout: 10, # segundos; padrão: 30
101
+ user_agent: "my-app/1.0"
102
+ )
103
+
104
+ client = Tronzap::Client.new do |config|
105
+ config.api_token = ENV.fetch("TRONZAP_API_TOKEN")
106
+ config.api_secret = ENV.fetch("TRONZAP_API_SECRET")
107
+ config.timeout = 10
108
+ end
109
+ ```
110
+
111
+ `base_url` aceita um domínio ou uma URL completa: sem esquema, usa-se `https`, e a
112
+ barra final é removida, então `"api.tronzap.com"`, `"api.tronzap.com/"` e
113
+ `"https://api.tronzap.com"` são equivalentes. Informe um esquema explícito para
114
+ evitar isso, por exemplo `"http://localhost:8080"` com um mock local.
115
+
116
+ `timeout` se aplica à abertura da conexão e a cada leitura e escrita, não à
117
+ requisição como um todo.
118
+
119
+ O SDK não mantém estado global. Cada cliente valida suas configurações ao ser
120
+ criado e as congela, então um cliente é imutável e seguro para compartilhar entre
121
+ threads. Crie um por conjunto de credenciais. `inspect` nunca exibe as
122
+ credenciais.
123
+
124
+ ### Seu próprio adaptador HTTP
125
+
126
+ O adaptador padrão usa `Net::HTTP` da biblioteca padrão, abre uma nova conexão a
127
+ cada requisição, sempre verifica os certificados TLS e respeita a variável de
128
+ ambiente `https_proxy`. Para confiar em uma autoridade certificadora privada,
129
+ passe o arquivo PEM dela:
130
+
131
+ ```ruby
132
+ adapter = Tronzap::HttpAdapter::NetHttp.new(ca_file: "/etc/ssl/corporate-ca.pem")
133
+ client = Tronzap::Client.new(api_token: api_token, api_secret: api_secret, adapter: adapter)
134
+ ```
135
+
136
+ Qualquer objeto com um método `call` pode substituí-lo. Ele recebe um
137
+ `Tronzap::HttpAdapter::Request` (`http_method`, `url`, `headers`, `body`,
138
+ `timeout`) e retorna um `Tronzap::HttpAdapter::Response` (`status`, `headers`,
139
+ `body`). Envie o corpo sem alterações: ele é assinado byte a byte.
140
+
141
+ ```ruby
142
+ class FaradayAdapter
143
+ def initialize(connection)
144
+ @connection = connection
145
+ end
146
+
147
+ def call(request)
148
+ response = @connection.post(request.url, request.body, request.headers) do |req|
149
+ req.options.timeout = request.timeout
150
+ end
151
+ Tronzap::HttpAdapter::Response.new(status: response.status, headers: response.headers.to_h,
152
+ body: response.body.to_s)
153
+ rescue Faraday::TimeoutError => e
154
+ raise Tronzap::TimeoutError, e.message
155
+ rescue Faraday::SSLError => e
156
+ raise Tronzap::SslError, e.message
157
+ rescue Faraday::ConnectionFailed => e
158
+ raise Tronzap::ConnectionError, e.message
159
+ end
160
+ end
161
+ ```
162
+
163
+ Os erros de rede padrão do Ruby lançados por um adaptador (`Timeout::Error`,
164
+ `OpenSSL::SSL::SSLError`, `SocketError`, `SystemCallError`, `IOError`) são
165
+ informados automaticamente como subclasses de `Tronzap::NetworkError`. Os erros de
166
+ outras bibliotecas HTTP precisam ser traduzidos pelo adaptador, como acima.
167
+
168
+ ## Métodos disponíveis
169
+
170
+ | Método | Endpoint | Descrição |
171
+ |---|---|---|
172
+ | `get_services` | `/v1/services` | Serviços disponíveis e preços |
173
+ | `get_balance` | `/v1/balance` | Saldo atual da conta |
174
+ | `get_address_info(address)` | `/v1/address-info` | Recursos (energia, largura de banda) e saldos (TRX, USDT) de um endereço |
175
+ | `estimate_energy(from_address:, to_address:, contract_address: nil)` | `/v1/estimate-energy` | Energia necessária para uma transferência e seu custo |
176
+ | `calculate(address:, energy:, duration: 1)` | `/v1/calculate` | Preço de uma compra sem criar a transação |
177
+ | `create_energy_transaction(address:, energy:, duration: 1, external_id: nil, activate_address: false)` | `/v1/transaction/new` | Comprar energia |
178
+ | `create_bandwidth_transaction(address:, bandwidth:, external_id: nil)` | `/v1/transaction/new` | Comprar largura de banda |
179
+ | `create_resource_bundle_transaction(address:, energy:, bandwidth:, duration: 1, external_id: nil, activate_address: false)` | `/v1/transaction/new` | Comprar energia e largura de banda em uma única transação |
180
+ | `create_address_activation_transaction(address:, external_id: nil)` | `/v1/transaction/new` | Ativar um endereço TRON |
181
+ | `check_transaction(id: nil, external_id: nil)` | `/v1/transaction/check` | Status de uma transação, por id ou id externo |
182
+ | `get_direct_recharge_info` | `/v1/direct-recharge-info` | Endereço e tarifas de recarga direta |
183
+ | `get_aml_services` | `/v1/aml-checks` | Serviços AML e preços |
184
+ | `create_aml_check(type:, network:, address:, transaction_hash: nil, direction: nil)` | `/v1/aml-checks/new` | Iniciar uma verificação AML |
185
+ | `check_aml_status(id)` | `/v1/aml-checks/check` | Status e resultado de uma verificação AML |
186
+ | `get_aml_history(page: 1, per_page: 10, status: nil)` | `/v1/aml-checks/history` | Histórico paginado de verificações AML |
187
+
188
+ Os métodos com parâmetros aceitam argumentos nomeados ou um objeto de requisição de
189
+ `Tronzap::Requests`, então uma requisição pode ser montada, validada e repassada
190
+ antes de ser enviada:
191
+
192
+ ```ruby
193
+ request = Tronzap::Requests::EnergyTransaction.new(address: "TRecipientAddress", energy: 65000)
194
+ client.create_energy_transaction(request)
195
+ ```
196
+
197
+ Uma requisição é validada ao ser criada, então uma requisição inválida lança
198
+ `ArgumentError` e nunca chega à API. As quantidades devem ser valores `Integer`
199
+ positivos. Os padrões coincidem com a API: `duration` é 1 hora e o histórico AML
200
+ começa na página 1 com 10 itens.
201
+
202
+ Os resultados são objetos `Data` imutáveis em `Tronzap::Responses` e
203
+ `Tronzap::Models`, não hashes: `transaction.status`, `estimate.energy`. As coleções
204
+ são congeladas e nunca são `nil`, e os valores que a API pode omitir são `nil`.
205
+
206
+ ### Comprar recursos
207
+
208
+ ```ruby
209
+ # Energia, com ativação opcional do endereço na mesma chamada.
210
+ client.create_energy_transaction(
211
+ address: "TRecipientAddress",
212
+ energy: 65000,
213
+ duration: 1, # horas; veja get_services para as durações disponíveis
214
+ external_id: "order-42",
215
+ activate_address: true
216
+ )
217
+
218
+ # Largura de banda.
219
+ client.create_bandwidth_transaction(address: "TRecipientAddress", bandwidth: 345, external_id: "bandwidth-1")
220
+
221
+ # Energia e largura de banda juntas em uma única transação.
222
+ client.create_resource_bundle_transaction(
223
+ address: "TRecipientAddress",
224
+ energy: 65000,
225
+ bandwidth: 345,
226
+ external_id: "bundle-1"
227
+ )
228
+
229
+ # Apenas a ativação.
230
+ client.create_address_activation_transaction(address: "TRecipientAddress", external_id: "activation-1")
231
+ ```
232
+
233
+ O preço da energia é por unidade e o da largura de banda é por 1000 unidades: em
234
+ `get_services`, `EnergyRate#price` × 65000 é o custo de 65000 de energia, enquanto
235
+ 345 de largura de banda com um `BandwidthRate#price` de 1 custam 0.345.
236
+
237
+ Atualmente a API informa um pacote de recursos com `service` igual a `:energy`, e
238
+ não `:resource_bundle`. Consulte `params.amounts` para saber quais recursos uma
239
+ transação contém.
240
+
241
+ ### Acompanhar uma transação
242
+
243
+ Uma transação passa por `:new` → `:pending` → `:success` ou `:failed`:
244
+
245
+ ```ruby
246
+ transaction = nil
247
+ loop do
248
+ sleep 2
249
+ transaction = client.check_transaction(external_id: "order-42")
250
+ break unless %i[new pending].include?(transaction.status)
251
+ end
252
+
253
+ puts "finished as #{transaction.status}, hash #{transaction.transaction_hash || "none"}"
254
+ ```
255
+
256
+ ### Verificação AML
257
+
258
+ ```ruby
259
+ check = client.create_aml_check(Tronzap::Requests::AmlCheck.for_address("TRX", "TAddressToScreen"))
260
+ # ou Tronzap::Requests::AmlCheck.for_hash("BTC", "bc1RecipientAddress", "TX_HASH", direction: :withdrawal)
261
+
262
+ result = client.check_aml_status(check.id)
263
+ if result.status == :completed
264
+ puts "#{result.risk_level} #{result.risk_score.to_s("F")} #{result.risk_factors.size} factor(s)"
265
+ end
266
+ ```
267
+
268
+ `risk_score` é `nil` até a verificação terminar. Uma verificação concluída pode
269
+ ter pontuação 0, o que não é o mesmo que ainda não ter pontuação.
270
+
271
+ ## Tratamento de erros
272
+
273
+ Toda falha de uma chamada à API é um `Tronzap::Error`. Capture uma subclasse para
274
+ tratar um tipo específico de falha:
275
+
276
+ ```
277
+ Tronzap::Error
278
+ ├── Tronzap::ApiError — a API respondeu com um código diferente de zero
279
+ ├── Tronzap::HttpError — resposta não 2xx sem payload da API
280
+ │ ├── Tronzap::RateLimitError — HTTP 429
281
+ │ ├── Tronzap::UnauthorizedError — HTTP 401 ou 403
282
+ │ └── Tronzap::ServerError — HTTP 5xx
283
+ ├── Tronzap::InvalidResponseError — resposta 2xx que o SDK não conseguiu ler
284
+ └── Tronzap::NetworkError — nenhuma resposta chegou
285
+ ├── Tronzap::ConnectionError — falha de DNS, conexão recusada
286
+ ├── Tronzap::TimeoutError — a requisição excedeu o timeout
287
+ └── Tronzap::SslError — falha no handshake TLS ou no certificado
288
+ ```
289
+
290
+ `ApiError`, `HttpError` e `InvalidResponseError` trazem o status HTTP (`status`) e
291
+ o corpo bruto da resposta (`response_body`). `ApiError` também traz o código de
292
+ erro da API (`code`), a chave do erro (`error_key`) e o ID da requisição
293
+ (`request_id`). `RateLimitError#retry_after` contém o intervalo de `Retry-After`
294
+ em segundos quando a API o envia.
295
+
296
+ Argumentos inválidos não são falhas da API: eles lançam `ArgumentError` antes de
297
+ qualquer envio.
298
+
299
+ ```ruby
300
+ begin
301
+ client.create_energy_transaction(address: "TRecipientAddress", energy: 65000)
302
+ rescue Tronzap::ApiError => e
303
+ # Falha no nível da aplicação: o código diz exatamente o que deu errado.
304
+ case e.code
305
+ when Tronzap::ErrorCode::INVALID_TRON_ADDRESS
306
+ # A chave pode detalhar, p. ex. "invalid_tron_address.from_address"
307
+ warn "bad address: #{e.error_key}"
308
+ when Tronzap::ErrorCode::INSUFFICIENT_FUNDS
309
+ warn "top up the account"
310
+ when Tronzap::ErrorCode::ADDRESS_NOT_ACTIVATED
311
+ warn "activate the address first"
312
+ else
313
+ warn "api error #{e.code}: #{e.message} (request #{e.request_id || "-"})"
314
+ end
315
+ rescue Tronzap::RateLimitError => e
316
+ # Aguarde e tente novamente, após e.retry_after segundos se a API o enviou.
317
+ rescue Tronzap::UnauthorizedError
318
+ # Token ou assinatura incorretos.
319
+ rescue Tronzap::TimeoutError, Tronzap::ServerError
320
+ # Transitório; pode tentar novamente.
321
+ rescue Tronzap::NetworkError
322
+ # Inacessível.
323
+ end
324
+ ```
325
+
326
+ `request_id` é o identificador que a API atribui a cada requisição. Informe-o ao
327
+ contatar o suporte.
328
+
329
+ Um erro da API tem prioridade sobre o status HTTP: a API informa algumas falhas
330
+ com status 2xx e outras com 4xx ou 5xx, então um payload legível com código
331
+ diferente de zero é sempre informado como `Tronzap::ApiError`, nunca como
332
+ `Tronzap::HttpError`.
333
+
334
+ ### Códigos de erro da API
335
+
336
+ | Código | Constante | Descrição |
337
+ |------|----------|-------------|
338
+ | 1 | `AUTH_ERROR` | Erro de autenticação: token da API ou assinatura inválidos |
339
+ | 2 | `INVALID_SERVICE_OR_PARAMS` | Serviço ou parâmetros inválidos |
340
+ | 5 | `WALLET_NOT_FOUND` | Carteira interna não encontrada. Contate o suporte. |
341
+ | 6 | `INSUFFICIENT_FUNDS` | Saldo insuficiente |
342
+ | 10 | `INVALID_TRON_ADDRESS` | Endereço TRON inválido |
343
+ | 11 | `INVALID_ENERGY_AMOUNT` | Quantidade de energia inválida |
344
+ | 12 | `INVALID_DURATION` | Duração inválida |
345
+ | 20 | `TRANSACTION_NOT_FOUND` | Transação/assinatura não encontrada |
346
+ | 21 | `CANNOT_STOP_SUBSCRIPTION` | Não é possível interromper a assinatura |
347
+ | 24 | `ADDRESS_NOT_ACTIVATED` | Endereço não ativado |
348
+ | 25 | `ADDRESS_ALREADY_ACTIVATED` | Endereço já ativado |
349
+ | 30 | `AML_CHECK_NOT_FOUND` | Verificação AML não encontrada |
350
+ | 35 | `SERVICE_NOT_AVAILABLE` | Serviço indisponível |
351
+ | 50 | `INVALID_BANDWIDTH_AMOUNT` | Quantidade de largura de banda inválida |
352
+ | 500 | `INTERNAL_SERVER_ERROR` | Erro interno do servidor: contate o suporte |
353
+
354
+ As constantes estão em `Tronzap::ErrorCode`. Um código que esta versão do SDK não
355
+ conhece continua disponível como número em `ApiError#code`.
356
+
357
+ ## Campos decimais e de data
358
+
359
+ Valores e preços são `BigDecimal`, então mantêm o valor exato enviado pela API. A
360
+ API codifica dinheiro como número JSON em algumas respostas e como string JSON em
361
+ outras; as duas formas são lidas da mesma maneira. Use `to_s("F")` para exibir um
362
+ valor sem notação exponencial.
363
+
364
+ Datas são objetos `Tronzap::Models::Timestamp`: `value` é o `Time` interpretado e
365
+ `raw` é o texto exatamente como a API enviou. Os vários formatos usados pela API
366
+ são aceitos, e horários sem fuso são lidos como UTC. Uma data não reconhecida
367
+ deixa `value` como `nil` em vez de fazer toda a resposta falhar.
368
+
369
+ Campos semelhantes a enums são símbolos, como `:energy` ou `:completed`. Um valor que a
370
+ API venha a adicionar no futuro, como um novo status de transação, é informado
371
+ como `:unknown` em vez de falhar. Os valores conhecidos estão listados em
372
+ `Tronzap::Models`.
373
+
374
+ ## Testes
375
+
376
+ ```bash
377
+ bundle install
378
+ bundle exec rspec
379
+ bundle exec rubocop
380
+ ```
381
+
382
+ Os testes rodam contra um servidor HTTP local: o corpo exato da requisição e a
383
+ assinatura de cada endpoint, erros da API e HTTP, JSON malformado, timeouts, falhas
384
+ de rede e de TLS, e uso concorrente.
385
+
386
+ ## Licença
387
+
388
+ Licença MIT (MIT). Veja o [arquivo de licença](LICENSE) para mais informações.
389
+
390
+ ## Suporte
391
+
392
+ Para suporte, entre em contato com [support@tronzap.com](mailto:support@tronzap.com).