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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 47eb7ea9f4ddd00ae8d660cc3b80423ec34badd0b79282e0a0ee3467018192d4
4
+ data.tar.gz: 8d8eaaedce42acb5aba146c6cd610af884c3d535002e296593bf4102a30d5777
5
+ SHA512:
6
+ metadata.gz: a70751df5acfd943527b15de891f78fd69bd25a245df44086e2c805001c6a31805a854f93c6079f0df0c945ba9f66f82f79750570e9f8385d09c56d5857b8b72
7
+ data.tar.gz: 3b279e1a68f525fdb795d9c54ec6da604b3d635884e03a869cd983ccadf26df4433b65300d2bce5d94e2e74389b16466470a972f4b46cba55cd7ab85155bc21a
data/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.0.0] - 2026-10-06
8
+
9
+ First release of the official Ruby SDK for the [TronZap API](https://docs.tronzap.com/).
10
+
11
+ ### Added
12
+
13
+ - `Tronzap::Client` with every TronZap API operation: services and prices, account balance, address info, energy
14
+ estimates and price calculation, energy, bandwidth, resource bundle and address activation purchases,
15
+ transaction status, direct recharge info, and AML services, checks and history.
16
+ - Configuration through keyword arguments or a block; every client owns a frozen configuration.
17
+ - Immutable request and response value objects built on `Data`, with money as `BigDecimal` and timestamps that keep
18
+ the raw text next to the parsed `Time`.
19
+ - A pluggable HTTP adapter; the default uses `Net::HTTP` and always verifies TLS certificates.
20
+ - An error hierarchy under `Tronzap::Error` that separates API, HTTP, response and network failures, with the HTTP
21
+ status, API error code, error key, request ID and raw response body.
22
+
23
+ [1.0.0]: https://github.com/tron-energy-market/tronzap-sdk-ruby/releases/tag/v1.0.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TronZap.com
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.es.md ADDED
@@ -0,0 +1,396 @@
1
+ # Alquiler de Energía Tron vía 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 en Ruby para la API de TronZap.
11
+ Este SDK permite integrar fácilmente los servicios de TronZap para alquilar energía TRON.
12
+
13
+ TronZap.com permite [comprar energía TRON](https://tronzap.com/), reduciendo significativamente las comisiones en transferencias de USDT (TRC20).
14
+
15
+ 👉 [Regístrate para obtener una clave API](https://tronzap.com) para comenzar a usar la API de TronZap e integrarla a través del SDK.
16
+
17
+ - Sitio web: https://tronzap.com/
18
+ - Referencia de la API: https://docs.tronzap.com/
19
+ - RubyGems: https://rubygems.org/gems/tronzap-sdk
20
+ - Código fuente: https://github.com/tron-energy-market/tronzap-sdk-ruby
21
+
22
+ ## Instalación
23
+
24
+ Añade la gema a tu Gemfile:
25
+
26
+ ```ruby
27
+ gem "tronzap-sdk"
28
+ ```
29
+
30
+ o instálala directamente:
31
+
32
+ ```bash
33
+ gem install tronzap-sdk
34
+ ```
35
+
36
+ ## Requisitos
37
+
38
+ - Ruby 3.3 o superior
39
+ - Una sola dependencia en tiempo de ejecución, `bigdecimal`. El SDK no depende de Rails.
40
+
41
+ ## Inicio rápido
42
+
43
+ ```ruby
44
+ require "tronzap"
45
+
46
+ client = Tronzap::Client.new(
47
+ api_token: "su_api_token",
48
+ api_secret: "su_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 cuánta energía necesita una transferencia de USDT y compra exactamente esa cantidad.
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"` carga el SDK. Con Bundler, `gem "tronzap-sdk"` en el Gemfile también lo carga.
72
+
73
+ Un recorrido ejecutable por todas las operaciones está en
74
+ [`examples/basic_usage.rb`](examples/basic_usage.rb):
75
+
76
+ ```bash
77
+ export TRONZAP_API_TOKEN=su_api_token
78
+ export TRONZAP_API_SECRET=su_api_secret
79
+ export TRONZAP_BASE_URL=api.tronzap.com # opcional
80
+ ruby -Ilib examples/basic_usage.rb
81
+ ```
82
+
83
+ Por defecto solo lee y no gasta nada. Con `TRONZAP_ALLOW_PURCHASES=1` también
84
+ ejecuta los endpoints que crean transacciones y verificaciones AML, que debitan el
85
+ saldo de la cuenta. Consulta el comentario al inicio del archivo para ver las
86
+ demás variables opcionales.
87
+
88
+ ## Configuración
89
+
90
+ El cliente recibe las dos credenciales de tu panel: el token de API se envía como
91
+ bearer token y el secreto de API firma el cuerpo de cada solicitud y nunca se envía.
92
+ Todo lo demás es opcional. Pasa la configuración como argumentos de palabra clave,
93
+ en un bloque o de ambas formas; el bloque se ejecuta al final:
94
+
95
+ ```ruby
96
+ client = Tronzap::Client.new(
97
+ api_token: api_token,
98
+ api_secret: api_secret,
99
+ base_url: "api.tronzap.com", # por defecto Tronzap::Configuration::DEFAULT_BASE_URL
100
+ timeout: 10, # segundos; por defecto 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` acepta un dominio o una URL completa: si falta el esquema se usa
112
+ `https` y se elimina la barra final, así que `"api.tronzap.com"`,
113
+ `"api.tronzap.com/"` y `"https://api.tronzap.com"` son equivalentes. Indica un
114
+ esquema explícito para evitarlo, por ejemplo `"http://localhost:8080"` con un mock
115
+ local.
116
+
117
+ `timeout` se aplica a la apertura de la conexión y a cada lectura y escritura, no
118
+ a la solicitud en su conjunto.
119
+
120
+ El SDK no mantiene estado global. Cada cliente valida su configuración al crearse
121
+ y la congela, así que un cliente es inmutable y seguro para compartir entre hilos.
122
+ Crea uno por cada juego de credenciales. `inspect` nunca muestra las
123
+ credenciales.
124
+
125
+ ### Tu propio adaptador HTTP
126
+
127
+ El adaptador por defecto usa `Net::HTTP` de la biblioteca estándar, abre una nueva
128
+ conexión para cada solicitud, siempre verifica los certificados TLS y respeta la
129
+ variable de entorno `https_proxy`. Para confiar en una autoridad de certificación
130
+ privada, pasa su archivo PEM:
131
+
132
+ ```ruby
133
+ adapter = Tronzap::HttpAdapter::NetHttp.new(ca_file: "/etc/ssl/corporate-ca.pem")
134
+ client = Tronzap::Client.new(api_token: api_token, api_secret: api_secret, adapter: adapter)
135
+ ```
136
+
137
+ Cualquier objeto con un método `call` puede sustituirlo. Recibe un
138
+ `Tronzap::HttpAdapter::Request` (`http_method`, `url`, `headers`, `body`,
139
+ `timeout`) y devuelve un `Tronzap::HttpAdapter::Response` (`status`, `headers`,
140
+ `body`). Envía el cuerpo sin modificarlo: se firma byte a byte.
141
+
142
+ ```ruby
143
+ class FaradayAdapter
144
+ def initialize(connection)
145
+ @connection = connection
146
+ end
147
+
148
+ def call(request)
149
+ response = @connection.post(request.url, request.body, request.headers) do |req|
150
+ req.options.timeout = request.timeout
151
+ end
152
+ Tronzap::HttpAdapter::Response.new(status: response.status, headers: response.headers.to_h,
153
+ body: response.body.to_s)
154
+ rescue Faraday::TimeoutError => e
155
+ raise Tronzap::TimeoutError, e.message
156
+ rescue Faraday::SSLError => e
157
+ raise Tronzap::SslError, e.message
158
+ rescue Faraday::ConnectionFailed => e
159
+ raise Tronzap::ConnectionError, e.message
160
+ end
161
+ end
162
+ ```
163
+
164
+ Los errores de red estándar de Ruby que lance un adaptador (`Timeout::Error`,
165
+ `OpenSSL::SSL::SSLError`, `SocketError`, `SystemCallError`, `IOError`) se informan
166
+ automáticamente como subclases de `Tronzap::NetworkError`. Los errores de otras
167
+ bibliotecas HTTP debe traducirlos el adaptador, como en el ejemplo anterior.
168
+
169
+ ## Métodos disponibles
170
+
171
+ | Método | Endpoint | Descripción |
172
+ |---|---|---|
173
+ | `get_services` | `/v1/services` | Servicios disponibles y precios |
174
+ | `get_balance` | `/v1/balance` | Saldo actual de la cuenta |
175
+ | `get_address_info(address)` | `/v1/address-info` | Recursos (energía, ancho de banda) y saldos (TRX, USDT) de una dirección |
176
+ | `estimate_energy(from_address:, to_address:, contract_address: nil)` | `/v1/estimate-energy` | Energía que necesita una transferencia y su coste |
177
+ | `calculate(address:, energy:, duration: 1)` | `/v1/calculate` | Precio de una compra sin crear la transacción |
178
+ | `create_energy_transaction(address:, energy:, duration: 1, external_id: nil, activate_address: false)` | `/v1/transaction/new` | Comprar energía |
179
+ | `create_bandwidth_transaction(address:, bandwidth:, external_id: nil)` | `/v1/transaction/new` | Comprar ancho de banda |
180
+ | `create_resource_bundle_transaction(address:, energy:, bandwidth:, duration: 1, external_id: nil, activate_address: false)` | `/v1/transaction/new` | Comprar energía y ancho de banda en una sola transacción |
181
+ | `create_address_activation_transaction(address:, external_id: nil)` | `/v1/transaction/new` | Activar una dirección TRON |
182
+ | `check_transaction(id: nil, external_id: nil)` | `/v1/transaction/check` | Estado de una transacción, por id o id externo |
183
+ | `get_direct_recharge_info` | `/v1/direct-recharge-info` | Dirección y tarifas de recarga directa |
184
+ | `get_aml_services` | `/v1/aml-checks` | Servicios AML y precios |
185
+ | `create_aml_check(type:, network:, address:, transaction_hash: nil, direction: nil)` | `/v1/aml-checks/new` | Iniciar una verificación AML |
186
+ | `check_aml_status(id)` | `/v1/aml-checks/check` | Estado y resultado de una verificación AML |
187
+ | `get_aml_history(page: 1, per_page: 10, status: nil)` | `/v1/aml-checks/history` | Historial paginado de verificaciones AML |
188
+
189
+ Los métodos con parámetros aceptan argumentos de palabra clave o un objeto de
190
+ solicitud de `Tronzap::Requests`, así que una solicitud se puede construir, validar
191
+ y pasar de un sitio a otro antes de enviarla:
192
+
193
+ ```ruby
194
+ request = Tronzap::Requests::EnergyTransaction.new(address: "TRecipientAddress", energy: 65000)
195
+ client.create_energy_transaction(request)
196
+ ```
197
+
198
+ Una solicitud se valida al crearse, así que una solicitud inválida lanza
199
+ `ArgumentError` y nunca llega a la API. Las cantidades deben ser `Integer`
200
+ positivos. Los valores por defecto coinciden con la API: `duration` es 1 hora y el
201
+ historial AML empieza en la página 1 con 10 elementos.
202
+
203
+ Los resultados son objetos `Data` inmutables en `Tronzap::Responses` y
204
+ `Tronzap::Models`, no hashes: `transaction.status`, `estimate.energy`. Las
205
+ colecciones están congeladas y nunca son `nil`, y los valores que la API puede
206
+ omitir son `nil`.
207
+
208
+ ### Comprar recursos
209
+
210
+ ```ruby
211
+ # Energía, con activación opcional de la dirección en la misma llamada.
212
+ client.create_energy_transaction(
213
+ address: "TRecipientAddress",
214
+ energy: 65000,
215
+ duration: 1, # horas; consulta get_services para las duraciones disponibles
216
+ external_id: "order-42",
217
+ activate_address: true
218
+ )
219
+
220
+ # Ancho de banda.
221
+ client.create_bandwidth_transaction(address: "TRecipientAddress", bandwidth: 345, external_id: "bandwidth-1")
222
+
223
+ # Energía y ancho de banda juntos en una sola transacción.
224
+ client.create_resource_bundle_transaction(
225
+ address: "TRecipientAddress",
226
+ energy: 65000,
227
+ bandwidth: 345,
228
+ external_id: "bundle-1"
229
+ )
230
+
231
+ # Solo la activación.
232
+ client.create_address_activation_transaction(address: "TRecipientAddress", external_id: "activation-1")
233
+ ```
234
+
235
+ El precio de la energía es por unidad y el del ancho de banda por 1000 unidades:
236
+ en `get_services`, `EnergyRate#price` × 65000 es el coste de 65000 de energía,
237
+ mientras que 345 de ancho de banda con un `BandwidthRate#price` de 1 cuestan
238
+ 0.345.
239
+
240
+ Actualmente la API informa un paquete de recursos con `service` igual a `:energy`,
241
+ no `:resource_bundle`. Consulta `params.amounts` para saber qué recursos contiene
242
+ una transacción.
243
+
244
+ ### Seguir una transacción
245
+
246
+ Una transacción pasa por `:new` → `:pending` → `:success` o `:failed`:
247
+
248
+ ```ruby
249
+ transaction = nil
250
+ loop do
251
+ sleep 2
252
+ transaction = client.check_transaction(external_id: "order-42")
253
+ break unless %i[new pending].include?(transaction.status)
254
+ end
255
+
256
+ puts "finished as #{transaction.status}, hash #{transaction.transaction_hash || "none"}"
257
+ ```
258
+
259
+ ### Verificación AML
260
+
261
+ ```ruby
262
+ check = client.create_aml_check(Tronzap::Requests::AmlCheck.for_address("TRX", "TAddressToScreen"))
263
+ # o Tronzap::Requests::AmlCheck.for_hash("BTC", "bc1RecipientAddress", "TX_HASH", direction: :withdrawal)
264
+
265
+ result = client.check_aml_status(check.id)
266
+ if result.status == :completed
267
+ puts "#{result.risk_level} #{result.risk_score.to_s("F")} #{result.risk_factors.size} factor(s)"
268
+ end
269
+ ```
270
+
271
+ `risk_score` es `nil` hasta que termina la verificación. Una verificación
272
+ completada puede tener una puntuación de 0, que no es lo mismo que no tener
273
+ puntuación todavía.
274
+
275
+ ## Gestión de errores
276
+
277
+ Todo fallo de una llamada a la API es un `Tronzap::Error`. Captura una subclase
278
+ para tratar un tipo concreto de fallo:
279
+
280
+ ```
281
+ Tronzap::Error
282
+ ├── Tronzap::ApiError — la API respondió con un código distinto de cero
283
+ ├── Tronzap::HttpError — respuesta no 2xx sin payload de la API
284
+ │ ├── Tronzap::RateLimitError — HTTP 429
285
+ │ ├── Tronzap::UnauthorizedError — HTTP 401 o 403
286
+ │ └── Tronzap::ServerError — HTTP 5xx
287
+ ├── Tronzap::InvalidResponseError — respuesta 2xx que el SDK no pudo leer
288
+ └── Tronzap::NetworkError — no llegó ninguna respuesta
289
+ ├── Tronzap::ConnectionError — fallo de DNS, conexión rechazada
290
+ ├── Tronzap::TimeoutError — la solicitud superó su timeout
291
+ └── Tronzap::SslError — fallo del handshake TLS o del certificado
292
+ ```
293
+
294
+ `ApiError`, `HttpError` e `InvalidResponseError` incluyen el estado HTTP
295
+ (`status`) y el cuerpo de la respuesta sin procesar (`response_body`). `ApiError`
296
+ incluye además el código de error de la API (`code`), la clave del error
297
+ (`error_key`) y el ID de la solicitud (`request_id`). `RateLimitError#retry_after`
298
+ contiene el retraso de `Retry-After` en segundos cuando la API lo envía.
299
+
300
+ Los argumentos inválidos no son fallos de la API: lanzan `ArgumentError` antes de
301
+ enviar nada.
302
+
303
+ ```ruby
304
+ begin
305
+ client.create_energy_transaction(address: "TRecipientAddress", energy: 65000)
306
+ rescue Tronzap::ApiError => e
307
+ # Fallo a nivel de aplicación: el código indica exactamente qué salió mal.
308
+ case e.code
309
+ when Tronzap::ErrorCode::INVALID_TRON_ADDRESS
310
+ # La clave puede precisarlo, p. ej. "invalid_tron_address.from_address"
311
+ warn "bad address: #{e.error_key}"
312
+ when Tronzap::ErrorCode::INSUFFICIENT_FUNDS
313
+ warn "top up the account"
314
+ when Tronzap::ErrorCode::ADDRESS_NOT_ACTIVATED
315
+ warn "activate the address first"
316
+ else
317
+ warn "api error #{e.code}: #{e.message} (request #{e.request_id || "-"})"
318
+ end
319
+ rescue Tronzap::RateLimitError => e
320
+ # Espera y reintenta, tras e.retry_after segundos si la API lo envió.
321
+ rescue Tronzap::UnauthorizedError
322
+ # Token o firma incorrectos.
323
+ rescue Tronzap::TimeoutError, Tronzap::ServerError
324
+ # Transitorio; se puede reintentar.
325
+ rescue Tronzap::NetworkError
326
+ # Inalcanzable.
327
+ end
328
+ ```
329
+
330
+ `request_id` es el identificador que la API asigna a cada solicitud. Indícalo al
331
+ contactar con soporte.
332
+
333
+ Un error de la API tiene prioridad sobre el estado HTTP: la API informa de
334
+ algunos fallos con estado 2xx y de otros con 4xx o 5xx, así que un payload legible
335
+ con un código distinto de cero siempre se informa como `Tronzap::ApiError`, nunca
336
+ como `Tronzap::HttpError`.
337
+
338
+ ### Códigos de error de la API
339
+
340
+ | Código | Constante | Descripción |
341
+ |------|----------|-------------|
342
+ | 1 | `AUTH_ERROR` | Error de autenticación: token de API o firma inválidos |
343
+ | 2 | `INVALID_SERVICE_OR_PARAMS` | Servicio o parámetros inválidos |
344
+ | 5 | `WALLET_NOT_FOUND` | Billetera interna no encontrada. Contacta con soporte. |
345
+ | 6 | `INSUFFICIENT_FUNDS` | Fondos insuficientes |
346
+ | 10 | `INVALID_TRON_ADDRESS` | Dirección TRON inválida |
347
+ | 11 | `INVALID_ENERGY_AMOUNT` | Cantidad de energía inválida |
348
+ | 12 | `INVALID_DURATION` | Duración inválida |
349
+ | 20 | `TRANSACTION_NOT_FOUND` | Transacción/suscripción no encontrada |
350
+ | 21 | `CANNOT_STOP_SUBSCRIPTION` | No se puede detener la suscripción |
351
+ | 24 | `ADDRESS_NOT_ACTIVATED` | Dirección no activada |
352
+ | 25 | `ADDRESS_ALREADY_ACTIVATED` | Dirección ya activada |
353
+ | 30 | `AML_CHECK_NOT_FOUND` | Verificación AML no encontrada |
354
+ | 35 | `SERVICE_NOT_AVAILABLE` | Servicio no disponible |
355
+ | 50 | `INVALID_BANDWIDTH_AMOUNT` | Cantidad de ancho de banda inválida |
356
+ | 500 | `INTERNAL_SERVER_ERROR` | Error interno del servidor: contacta con soporte |
357
+
358
+ Las constantes están en `Tronzap::ErrorCode`. Un código que esta versión del SDK
359
+ no conoce sigue disponible como número en `ApiError#code`.
360
+
361
+ ## Campos decimales y de fecha
362
+
363
+ Los importes y precios son `BigDecimal`, así que conservan el valor exacto que
364
+ envió la API. La API codifica el dinero como número JSON en algunas respuestas y
365
+ como cadena JSON en otras; ambas formas se leen igual. Usa `to_s("F")` para
366
+ imprimir uno sin notación exponencial.
367
+
368
+ Las fechas son objetos `Tronzap::Models::Timestamp`: `value` es el `Time`
369
+ interpretado y `raw` es el texto tal como lo envió la API. Se aceptan los distintos
370
+ formatos que usa la API, y las horas sin desplazamiento se leen como UTC. Una fecha
371
+ no reconocida deja `value` como `nil` en lugar de hacer fallar toda la respuesta.
372
+
373
+ Los campos similares a enumeraciones son símbolos, como `:energy` o `:completed`. Un valor
374
+ que la API pueda añadir en el futuro, como un nuevo estado de transacción, se
375
+ informa como `:unknown` en lugar de fallar. Los valores conocidos se enumeran en
376
+ `Tronzap::Models`.
377
+
378
+ ## Pruebas
379
+
380
+ ```bash
381
+ bundle install
382
+ bundle exec rspec
383
+ bundle exec rubocop
384
+ ```
385
+
386
+ Las pruebas se ejecutan contra un servidor HTTP local: el cuerpo exacto de la
387
+ solicitud y la firma de cada endpoint, errores de la API y HTTP, JSON malformado,
388
+ timeouts, fallos de red y de TLS, y uso concurrente.
389
+
390
+ ## Licencia
391
+
392
+ Licencia MIT (MIT). Consulta el [archivo de licencia](LICENSE) para más información.
393
+
394
+ ## Soporte
395
+
396
+ Para soporte, contacta con [support@tronzap.com](mailto:support@tronzap.com).