tronzap-sdk 1.0.0 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47eb7ea9f4ddd00ae8d660cc3b80423ec34badd0b79282e0a0ee3467018192d4
4
- data.tar.gz: 8d8eaaedce42acb5aba146c6cd610af884c3d535002e296593bf4102a30d5777
3
+ metadata.gz: 3ffa340c958b0a66217f4c255dc615ebb13c8580730bcd276658be3e7178a991
4
+ data.tar.gz: f464a18b56042ae6e74bde0a194fb464a669891edada491cdd86c967089cc094
5
5
  SHA512:
6
- metadata.gz: a70751df5acfd943527b15de891f78fd69bd25a245df44086e2c805001c6a31805a854f93c6079f0df0c945ba9f66f82f79750570e9f8385d09c56d5857b8b72
7
- data.tar.gz: 3b279e1a68f525fdb795d9c54ec6da604b3d635884e03a869cd983ccadf26df4433b65300d2bce5d94e2e74389b16466470a972f4b46cba55cd7ab85155bc21a
6
+ metadata.gz: 1e98924e7e7b72f4b4dd8d78b62e8591047a5f0e76795244c071e02f9f324bbf6f07c834e02da72839a813317aa7764ac289cc7c413d74f1b247d16c33046af4
7
+ data.tar.gz: 9543a6086b456f1a963ccee701add632f0742eb6c39aedab71ada79f6dc96b6cd3be385edba7d38ebfdef0da841f6098ee44812d300279934e7fbdd2f9b31a69
data/CHANGELOG.md CHANGED
@@ -4,6 +4,45 @@ All notable changes to this project are documented in this file. The format is b
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [Unreleased]
8
+
9
+ ## [1.1.0] - 2026-10-09
10
+
11
+ ### Added
12
+
13
+ - Energy subscriptions: `Client#get_subscriptions`, `#start_subscription`, `#check_subscription`,
14
+ `#stop_subscription` and `#get_subscription_history`, with the requests `Requests::StartSubscription`,
15
+ `Requests::SubscriptionLookup` and `Requests::SubscriptionHistory`, the results `Responses::SubscriptionPlan`,
16
+ `Responses::Subscription` and `Responses::SubscriptionHistory`, `Models::SubscriptionParams` and
17
+ `Models::SUBSCRIPTION_STATUSES`. `get_subscriptions` returns the plans in the API's order, each with its key as
18
+ `subscription_id`.
19
+ - The example program lists subscription plans and history, checks `TRONZAP_SUBSCRIPTION_ID`, and with purchases
20
+ allowed and `TRONZAP_SUBSCRIPTION_PLAN` set starts a one-day subscription and always stops it.
21
+
22
+ ### Changed
23
+
24
+ - A hash AML check without a `direction` now sends `deposit` explicitly instead of leaving the direction to the API.
25
+ An address check still sends a direction only when one is given.
26
+ - The documentation of AML checks: for a hash check, `address` is the recipient address of the transaction, and
27
+ `direction` says which side you are on (`:deposit` if the funds were sent to your address, `:withdrawal` if you
28
+ sent them); the risk is scored for the counterparty.
29
+ - The documentation of error codes 10 and 21: `INVALID_TRON_ADDRESS` is also reported when the address already has
30
+ an active subscription, and `CANNOT_STOP_SUBSCRIPTION` covers, for example, a subscription with a transactions
31
+ limit.
32
+
33
+ ### Deprecated
34
+
35
+ - `Models::EnergyRate#min_energy` and `#max_energy`: use `#min_amount` and `#max_amount`. They now hold the same
36
+ values, read from the API's `min_amount` and `max_amount`.
37
+ - `Responses::Calculation#energy`: use `#amount`. It now holds the same value, read from the API's `amount`.
38
+ - `Responses::EnergyEstimate#energy`: use `#amount`. It now holds the same value, read from the API's `amount`.
39
+
40
+ ### Fixed
41
+
42
+ - The documentation of `get_services` prices: energy, like bandwidth, is priced per 1000 units, so a purchase costs
43
+ `price × amount / 1000`. It was described as priced per unit.
44
+ - `Models::DirectRechargeRate#price` is the price of 1000 units of energy as well, not of one unit.
45
+
7
46
  ## [1.0.0] - 2026-10-06
8
47
 
9
48
  First release of the official Ruby SDK for the [TronZap API](https://docs.tronzap.com/).
@@ -20,4 +59,6 @@ First release of the official Ruby SDK for the [TronZap API](https://docs.tronza
20
59
  - An error hierarchy under `Tronzap::Error` that separates API, HTTP, response and network failures, with the HTTP
21
60
  status, API error code, error key, request ID and raw response body.
22
61
 
62
+ [Unreleased]: https://github.com/tron-energy-market/tronzap-sdk-ruby/compare/v1.1.0...HEAD
63
+ [1.1.0]: https://github.com/tron-energy-market/tronzap-sdk-ruby/releases/tag/v1.1.0
23
64
  [1.0.0]: https://github.com/tron-energy-market/tronzap-sdk-ruby/releases/tag/v1.0.0
data/README.es.md CHANGED
@@ -57,7 +57,7 @@ begin
57
57
 
58
58
  transaction = client.create_energy_transaction(
59
59
  address: "TRecipientAddress",
60
- energy: estimate.energy,
60
+ energy: estimate.amount,
61
61
  duration: 1,
62
62
  external_id: "order-42",
63
63
  activate_address: true
@@ -185,6 +185,11 @@ bibliotecas HTTP debe traducirlos el adaptador, como en el ejemplo anterior.
185
185
  | `create_aml_check(type:, network:, address:, transaction_hash: nil, direction: nil)` | `/v1/aml-checks/new` | Iniciar una verificación AML |
186
186
  | `check_aml_status(id)` | `/v1/aml-checks/check` | Estado y resultado de una verificación AML |
187
187
  | `get_aml_history(page: 1, per_page: 10, status: nil)` | `/v1/aml-checks/history` | Historial paginado de verificaciones AML |
188
+ | `get_subscriptions` | `/v1/subscriptions` | Planes de suscripción y precios |
189
+ | `start_subscription(subscription_id:, address:, duration_days: 0, transactions_limit: 0, external_id: nil, activate_address: false)` | `/v1/subscription/start` | Suscribir una dirección a un plan |
190
+ | `check_subscription(id: nil, external_id: nil)` | `/v1/subscription/check` | Estado de una suscripción, por id o id externo |
191
+ | `stop_subscription(id: nil, external_id: nil)` | `/v1/subscription/stop` | Detener una suscripción |
192
+ | `get_subscription_history(page: 1, per_page: 10, status: nil)` | `/v1/subscriptions/history` | Historial paginado de suscripciones |
188
193
 
189
194
  Los métodos con parámetros aceptan argumentos de palabra clave o un objeto de
190
195
  solicitud de `Tronzap::Requests`, así que una solicitud se puede construir, validar
@@ -197,11 +202,13 @@ client.create_energy_transaction(request)
197
202
 
198
203
  Una solicitud se valida al crearse, así que una solicitud inválida lanza
199
204
  `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.
205
+ positivos. Los valores por defecto coinciden con la API: `duration` es 1 hora y los
206
+ historiales AML y de suscripciones empiezan en la página 1 con 10 elementos. La
207
+ excepción es `start_subscription`, donde `duration_days` y `transactions_limit`
208
+ valen 0 por defecto, lo que significa sin límite.
202
209
 
203
210
  Los resultados son objetos `Data` inmutables en `Tronzap::Responses` y
204
- `Tronzap::Models`, no hashes: `transaction.status`, `estimate.energy`. Las
211
+ `Tronzap::Models`, no hashes: `transaction.status`, `estimate.amount`. Las
205
212
  colecciones están congeladas y nunca son `nil`, y los valores que la API puede
206
213
  omitir son `nil`.
207
214
 
@@ -232,10 +239,10 @@ client.create_resource_bundle_transaction(
232
239
  client.create_address_activation_transaction(address: "TRecipientAddress", external_id: "activation-1")
233
240
  ```
234
241
 
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.
242
+ En `get_services`, los precios de la energía y del ancho de banda son ambos por
243
+ 1000 unidades, así que una compra cuesta `price × amount / 1000`: 65000 de energía
244
+ con un `EnergyRate#price` de 0.03 cuestan 1.95, y 345 de ancho de banda con un
245
+ `BandwidthRate#price` de 1 cuestan 0.345.
239
246
 
240
247
  Actualmente la API informa un paquete de recursos con `service` igual a `:energy`,
241
248
  no `:resource_bundle`. Consulta `params.amounts` para saber qué recursos contiene
@@ -268,10 +275,51 @@ if result.status == :completed
268
275
  end
269
276
  ```
270
277
 
278
+ En una verificación por hash, `address` es la dirección del destinatario de la
279
+ transacción, donde se recibieron los fondos, y `direction` indica en qué lado
280
+ estás: `:deposit` si los fondos llegaron a tu dirección (`address` es tu
281
+ dirección), `:withdrawal` si los enviaste tú (`address` es la dirección del
282
+ destinatario externo). El riesgo se calcula para la contraparte: el remitente en
283
+ un deposit, el destinatario en un withdrawal. Sin `direction`, el SDK envía
284
+ `deposit`.
285
+
271
286
  `risk_score` es `nil` hasta que termina la verificación. Una verificación
272
287
  completada puede tener una puntuación de 0, que no es lo mismo que no tener
273
288
  puntuación todavía.
274
289
 
290
+ ### Suscripciones
291
+
292
+ Una suscripción mantiene una dirección abastecida de energía para cada transacción
293
+ hasta que se detiene o se agotan sus días o transacciones. Elija un plan de
294
+ `get_subscriptions` y pase su `subscription_id`, como `"unlimited_energy"`, no su
295
+ `id` numérico. Iniciar una suscripción cobra el precio inicial del plan.
296
+
297
+ ```ruby
298
+ client.get_subscriptions.each do |plan|
299
+ puts "#{plan.subscription_id} #{plan.initial_price.to_s("F")} #{plan.price.to_s("F")}"
300
+ end
301
+
302
+ subscription = client.start_subscription(
303
+ subscription_id: "unlimited_energy",
304
+ address: "TRecipientAddress",
305
+ duration_days: 30, # 0 para no limitar el tiempo
306
+ transactions_limit: 0, # 0 para no limitar
307
+ external_id: "subscription-42"
308
+ )
309
+
310
+ subscription = client.check_subscription(external_id: "subscription-42")
311
+
312
+ subscription = client.stop_subscription(id: subscription.id)
313
+
314
+ history = client.get_subscription_history(status: :active)
315
+ ```
316
+
317
+ Iniciar, consultar y detener devuelven la suscripción con sus `params`; el
318
+ historial devuelve en su lugar los contadores de uso `transactions_used`,
319
+ `energy_used` y `total_price`, y deja `params` en `nil`. Los estados de una
320
+ suscripción están en `Tronzap::Models::SUBSCRIPTION_STATUSES`. Una suscripción con
321
+ límite de transacciones no se puede detener (`CANNOT_STOP_SUBSCRIPTION`).
322
+
275
323
  ## Gestión de errores
276
324
 
277
325
  Todo fallo de una llamada a la API es un `Tronzap::Error`. Captura una subclase
@@ -343,11 +391,11 @@ como `Tronzap::HttpError`.
343
391
  | 2 | `INVALID_SERVICE_OR_PARAMS` | Servicio o parámetros inválidos |
344
392
  | 5 | `WALLET_NOT_FOUND` | Billetera interna no encontrada. Contacta con soporte. |
345
393
  | 6 | `INSUFFICIENT_FUNDS` | Fondos insuficientes |
346
- | 10 | `INVALID_TRON_ADDRESS` | Dirección TRON inválida |
394
+ | 10 | `INVALID_TRON_ADDRESS` | Dirección TRON inválida, o la dirección ya tiene una suscripción activa |
347
395
  | 11 | `INVALID_ENERGY_AMOUNT` | Cantidad de energía inválida |
348
396
  | 12 | `INVALID_DURATION` | Duración inválida |
349
397
  | 20 | `TRANSACTION_NOT_FOUND` | Transacción/suscripción no encontrada |
350
- | 21 | `CANNOT_STOP_SUBSCRIPTION` | No se puede detener la suscripción |
398
+ | 21 | `CANNOT_STOP_SUBSCRIPTION` | No se puede detener la suscripción, p. ej. tiene límite de transacciones |
351
399
  | 24 | `ADDRESS_NOT_ACTIVATED` | Dirección no activada |
352
400
  | 25 | `ADDRESS_ALREADY_ACTIVATED` | Dirección ya activada |
353
401
  | 30 | `AML_CHECK_NOT_FOUND` | Verificación AML no encontrada |
data/README.md CHANGED
@@ -57,7 +57,7 @@ begin
57
57
 
58
58
  transaction = client.create_energy_transaction(
59
59
  address: "TRecipientAddress",
60
- energy: estimate.energy,
60
+ energy: estimate.amount,
61
61
  duration: 1,
62
62
  external_id: "order-42",
63
63
  activate_address: true
@@ -185,6 +185,11 @@ libraries need to be translated by the adapter, as above.
185
185
  | `create_aml_check(type:, network:, address:, transaction_hash: nil, direction: nil)` | `/v1/aml-checks/new` | Start an AML screening |
186
186
  | `check_aml_status(id)` | `/v1/aml-checks/check` | Status and result of an AML check |
187
187
  | `get_aml_history(page: 1, per_page: 10, status: nil)` | `/v1/aml-checks/history` | Paginated AML check history |
188
+ | `get_subscriptions` | `/v1/subscriptions` | Subscription plans and prices |
189
+ | `start_subscription(subscription_id:, address:, duration_days: 0, transactions_limit: 0, external_id: nil, activate_address: false)` | `/v1/subscription/start` | Subscribe an address to a plan |
190
+ | `check_subscription(id: nil, external_id: nil)` | `/v1/subscription/check` | Status of a subscription, by id or external id |
191
+ | `stop_subscription(id: nil, external_id: nil)` | `/v1/subscription/stop` | Stop a subscription |
192
+ | `get_subscription_history(page: 1, per_page: 10, status: nil)` | `/v1/subscriptions/history` | Paginated subscription history |
188
193
 
189
194
  Methods with parameters take either keyword arguments or a request object from
190
195
  `Tronzap::Requests`, so a request can be built, validated and passed around before
@@ -197,11 +202,12 @@ client.create_energy_transaction(request)
197
202
 
198
203
  A request is validated when it is created, so an invalid one raises
199
204
  `ArgumentError` and never reaches the API. Amounts must be positive `Integer`s.
200
- Defaults match the API: `duration` is 1 hour, and AML history starts at page 1
201
- with 10 items.
205
+ Defaults match the API: `duration` is 1 hour, and AML and subscription history
206
+ start at page 1 with 10 items. The exception is `start_subscription`, where
207
+ `duration_days` and `transactions_limit` default to 0, which means no limit.
202
208
 
203
209
  Results are immutable `Data` objects in `Tronzap::Responses` and
204
- `Tronzap::Models`, not hashes: `transaction.status`, `estimate.energy`. Collections
210
+ `Tronzap::Models`, not hashes: `transaction.status`, `estimate.amount`. Collections
205
211
  are frozen and never `nil`, and values the API may omit are `nil`.
206
212
 
207
213
  ### Buying resources
@@ -231,9 +237,9 @@ client.create_resource_bundle_transaction(
231
237
  client.create_address_activation_transaction(address: "TRecipientAddress", external_id: "activation-1")
232
238
  ```
233
239
 
234
- Energy prices are per unit, bandwidth prices are per 1000 units: in
235
- `get_services`, `EnergyRate#price` × 65000 is the cost of 65000 energy, while 345
236
- bandwidth at a `BandwidthRate#price` of 1 costs 0.345.
240
+ Energy and bandwidth prices in `get_services` are both per 1000 units, so a
241
+ purchase costs `price × amount / 1000`: 65000 energy at an `EnergyRate#price` of
242
+ 0.03 costs 1.95, and 345 bandwidth at a `BandwidthRate#price` of 1 costs 0.345.
237
243
 
238
244
  The API currently reports a resource bundle with `service` equal to `:energy`, not
239
245
  `:resource_bundle`. Read `params.amounts` to see which resources a transaction
@@ -266,9 +272,49 @@ if result.status == :completed
266
272
  end
267
273
  ```
268
274
 
275
+ For a hash check, `address` is the recipient address of the transaction, where
276
+ the funds were received, and `direction` says which side you are on: `:deposit`
277
+ if the funds were sent to your address (`address` is your address), `:withdrawal`
278
+ if you sent them (`address` is the external recipient's address). The risk is
279
+ scored for the counterparty: the sender of a deposit, the recipient of a
280
+ withdrawal. Without a `direction`, the SDK sends `deposit`.
281
+
269
282
  `risk_score` is `nil` until screening finishes. A completed check can have a score
270
283
  of 0, which is not the same as having no score yet.
271
284
 
285
+ ### Subscriptions
286
+
287
+ A subscription keeps an address supplied with energy for every transaction until
288
+ it is stopped or runs out of days or transactions. Pick a plan from
289
+ `get_subscriptions` and pass its `subscription_id`, such as `"unlimited_energy"`,
290
+ not its numeric `id`. Starting a subscription charges the plan's initial price.
291
+
292
+ ```ruby
293
+ client.get_subscriptions.each do |plan|
294
+ puts "#{plan.subscription_id} #{plan.initial_price.to_s("F")} #{plan.price.to_s("F")}"
295
+ end
296
+
297
+ subscription = client.start_subscription(
298
+ subscription_id: "unlimited_energy",
299
+ address: "TRecipientAddress",
300
+ duration_days: 30, # 0 for no time limit
301
+ transactions_limit: 0, # 0 for no limit
302
+ external_id: "subscription-42"
303
+ )
304
+
305
+ subscription = client.check_subscription(external_id: "subscription-42")
306
+
307
+ subscription = client.stop_subscription(id: subscription.id)
308
+
309
+ history = client.get_subscription_history(status: :active)
310
+ ```
311
+
312
+ Start, check and stop return the subscription with its `params`; the history
313
+ returns the usage counters `transactions_used`, `energy_used` and `total_price`
314
+ instead, and leaves `params` `nil`. A subscription moves through the statuses in
315
+ `Tronzap::Models::SUBSCRIPTION_STATUSES`. A subscription with a transactions limit
316
+ cannot be stopped (`CANNOT_STOP_SUBSCRIPTION`).
317
+
272
318
  ## Error handling
273
319
 
274
320
  Every failure of an API call is a `Tronzap::Error`. Rescue a subclass to handle
@@ -340,11 +386,11 @@ a non-zero code is always reported as `Tronzap::ApiError`, never as
340
386
  | 2 | `INVALID_SERVICE_OR_PARAMS` | Invalid service or parameters |
341
387
  | 5 | `WALLET_NOT_FOUND` | Internal wallet not found. Contact support. |
342
388
  | 6 | `INSUFFICIENT_FUNDS` | Insufficient funds |
343
- | 10 | `INVALID_TRON_ADDRESS` | Invalid TRON address |
389
+ | 10 | `INVALID_TRON_ADDRESS` | Invalid TRON address, or the address already has an active subscription |
344
390
  | 11 | `INVALID_ENERGY_AMOUNT` | Invalid energy amount |
345
391
  | 12 | `INVALID_DURATION` | Invalid duration |
346
392
  | 20 | `TRANSACTION_NOT_FOUND` | Transaction/subscription not found |
347
- | 21 | `CANNOT_STOP_SUBSCRIPTION` | Cannot stop subscription |
393
+ | 21 | `CANNOT_STOP_SUBSCRIPTION` | Cannot stop subscription, e.g. it has a transactions limit |
348
394
  | 24 | `ADDRESS_NOT_ACTIVATED` | Address not activated |
349
395
  | 25 | `ADDRESS_ALREADY_ACTIVATED` | Address already activated |
350
396
  | 30 | `AML_CHECK_NOT_FOUND` | AML check not found |
data/README.pt-br.md CHANGED
@@ -57,7 +57,7 @@ begin
57
57
 
58
58
  transaction = client.create_energy_transaction(
59
59
  address: "TRecipientAddress",
60
- energy: estimate.energy,
60
+ energy: estimate.amount,
61
61
  duration: 1,
62
62
  external_id: "order-42",
63
63
  activate_address: true
@@ -184,6 +184,11 @@ outras bibliotecas HTTP precisam ser traduzidos pelo adaptador, como acima.
184
184
  | `create_aml_check(type:, network:, address:, transaction_hash: nil, direction: nil)` | `/v1/aml-checks/new` | Iniciar uma verificação AML |
185
185
  | `check_aml_status(id)` | `/v1/aml-checks/check` | Status e resultado de uma verificação AML |
186
186
  | `get_aml_history(page: 1, per_page: 10, status: nil)` | `/v1/aml-checks/history` | Histórico paginado de verificações AML |
187
+ | `get_subscriptions` | `/v1/subscriptions` | Planos de assinatura e preços |
188
+ | `start_subscription(subscription_id:, address:, duration_days: 0, transactions_limit: 0, external_id: nil, activate_address: false)` | `/v1/subscription/start` | Assinar um plano para um endereço |
189
+ | `check_subscription(id: nil, external_id: nil)` | `/v1/subscription/check` | Status de uma assinatura, por id ou id externo |
190
+ | `stop_subscription(id: nil, external_id: nil)` | `/v1/subscription/stop` | Parar uma assinatura |
191
+ | `get_subscription_history(page: 1, per_page: 10, status: nil)` | `/v1/subscriptions/history` | Histórico paginado de assinaturas |
187
192
 
188
193
  Os métodos com parâmetros aceitam argumentos nomeados ou um objeto de requisição de
189
194
  `Tronzap::Requests`, então uma requisição pode ser montada, validada e repassada
@@ -196,11 +201,13 @@ client.create_energy_transaction(request)
196
201
 
197
202
  Uma requisição é validada ao ser criada, então uma requisição inválida lança
198
203
  `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.
204
+ positivos. Os padrões coincidem com a API: `duration` é 1 hora e os históricos AML
205
+ e de assinaturas começam na página 1 com 10 itens. A exceção é
206
+ `start_subscription`, em que `duration_days` e `transactions_limit` valem 0 por
207
+ padrão, o que significa sem limite.
201
208
 
202
209
  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
210
+ `Tronzap::Models`, não hashes: `transaction.status`, `estimate.amount`. As coleções
204
211
  são congeladas e nunca são `nil`, e os valores que a API pode omitir são `nil`.
205
212
 
206
213
  ### Comprar recursos
@@ -230,9 +237,10 @@ client.create_resource_bundle_transaction(
230
237
  client.create_address_activation_transaction(address: "TRecipientAddress", external_id: "activation-1")
231
238
  ```
232
239
 
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.
240
+ Em `get_services`, os preços da energia e da largura de banda são ambos por 1000
241
+ unidades, então uma compra custa `price × amount / 1000`: 65000 de energia com um
242
+ `EnergyRate#price` de 0.03 custam 1.95, e 345 de largura de banda com um
243
+ `BandwidthRate#price` de 1 custam 0.345.
236
244
 
237
245
  Atualmente a API informa um pacote de recursos com `service` igual a `:energy`, e
238
246
  não `:resource_bundle`. Consulte `params.amounts` para saber quais recursos uma
@@ -265,9 +273,50 @@ if result.status == :completed
265
273
  end
266
274
  ```
267
275
 
276
+ Em uma verificação por hash, `address` é o endereço do destinatário da
277
+ transação, onde os fundos foram recebidos, e `direction` indica de que lado você
278
+ está: `:deposit` se os fundos chegaram ao seu endereço (`address` é o seu
279
+ endereço), `:withdrawal` se foi você quem enviou (`address` é o endereço do
280
+ destinatário externo). O risco é calculado para a contraparte: o remetente em um
281
+ deposit, o destinatário em um withdrawal. Sem `direction`, o SDK envia
282
+ `deposit`.
283
+
268
284
  `risk_score` é `nil` até a verificação terminar. Uma verificação concluída pode
269
285
  ter pontuação 0, o que não é o mesmo que ainda não ter pontuação.
270
286
 
287
+ ### Assinaturas
288
+
289
+ Uma assinatura mantém um endereço abastecido de energia para cada transação até
290
+ ser parada ou esgotar seus dias ou transações. Escolha um plano de
291
+ `get_subscriptions` e passe o seu `subscription_id`, como `"unlimited_energy"`, não
292
+ o `id` numérico. Iniciar uma assinatura cobra o preço inicial do plano.
293
+
294
+ ```ruby
295
+ client.get_subscriptions.each do |plan|
296
+ puts "#{plan.subscription_id} #{plan.initial_price.to_s("F")} #{plan.price.to_s("F")}"
297
+ end
298
+
299
+ subscription = client.start_subscription(
300
+ subscription_id: "unlimited_energy",
301
+ address: "TRecipientAddress",
302
+ duration_days: 30, # 0 para não limitar o tempo
303
+ transactions_limit: 0, # 0 para não limitar
304
+ external_id: "subscription-42"
305
+ )
306
+
307
+ subscription = client.check_subscription(external_id: "subscription-42")
308
+
309
+ subscription = client.stop_subscription(id: subscription.id)
310
+
311
+ history = client.get_subscription_history(status: :active)
312
+ ```
313
+
314
+ Iniciar, consultar e parar retornam a assinatura com seus `params`; o histórico
315
+ retorna em vez disso os contadores de uso `transactions_used`, `energy_used` e
316
+ `total_price`, e deixa `params` como `nil`. Os status de uma assinatura estão em
317
+ `Tronzap::Models::SUBSCRIPTION_STATUSES`. Uma assinatura com limite de transações
318
+ não pode ser parada (`CANNOT_STOP_SUBSCRIPTION`).
319
+
271
320
  ## Tratamento de erros
272
321
 
273
322
  Toda falha de uma chamada à API é um `Tronzap::Error`. Capture uma subclasse para
@@ -339,11 +388,11 @@ diferente de zero é sempre informado como `Tronzap::ApiError`, nunca como
339
388
  | 2 | `INVALID_SERVICE_OR_PARAMS` | Serviço ou parâmetros inválidos |
340
389
  | 5 | `WALLET_NOT_FOUND` | Carteira interna não encontrada. Contate o suporte. |
341
390
  | 6 | `INSUFFICIENT_FUNDS` | Saldo insuficiente |
342
- | 10 | `INVALID_TRON_ADDRESS` | Endereço TRON inválido |
391
+ | 10 | `INVALID_TRON_ADDRESS` | Endereço TRON inválido, ou o endereço já tem uma assinatura ativa |
343
392
  | 11 | `INVALID_ENERGY_AMOUNT` | Quantidade de energia inválida |
344
393
  | 12 | `INVALID_DURATION` | Duração inválida |
345
394
  | 20 | `TRANSACTION_NOT_FOUND` | Transação/assinatura não encontrada |
346
- | 21 | `CANNOT_STOP_SUBSCRIPTION` | Não é possível interromper a assinatura |
395
+ | 21 | `CANNOT_STOP_SUBSCRIPTION` | Não é possível interromper a assinatura, p. ex. ela tem limite de transações |
347
396
  | 24 | `ADDRESS_NOT_ACTIVATED` | Endereço não ativado |
348
397
  | 25 | `ADDRESS_ALREADY_ACTIVATED` | Endereço já ativado |
349
398
  | 30 | `AML_CHECK_NOT_FOUND` | Verificação AML não encontrada |
data/README.ru.md CHANGED
@@ -57,7 +57,7 @@ begin
57
57
 
58
58
  transaction = client.create_energy_transaction(
59
59
  address: "TRecipientAddress",
60
- energy: estimate.energy,
60
+ energy: estimate.amount,
61
61
  duration: 1,
62
62
  external_id: "order-42",
63
63
  activate_address: true
@@ -185,6 +185,11 @@ HTTP-библиотек адаптер должен преобразовать
185
185
  | `create_aml_check(type:, network:, address:, transaction_hash: nil, direction: nil)` | `/v1/aml-checks/new` | Запустить AML-проверку |
186
186
  | `check_aml_status(id)` | `/v1/aml-checks/check` | Статус и результат AML-проверки |
187
187
  | `get_aml_history(page: 1, per_page: 10, status: nil)` | `/v1/aml-checks/history` | История AML-проверок с пагинацией |
188
+ | `get_subscriptions` | `/v1/subscriptions` | Планы подписок и цены |
189
+ | `start_subscription(subscription_id:, address:, duration_days: 0, transactions_limit: 0, external_id: nil, activate_address: false)` | `/v1/subscription/start` | Подписать адрес на план |
190
+ | `check_subscription(id: nil, external_id: nil)` | `/v1/subscription/check` | Статус подписки по id или внешнему id |
191
+ | `stop_subscription(id: nil, external_id: nil)` | `/v1/subscription/stop` | Остановить подписку |
192
+ | `get_subscription_history(page: 1, per_page: 10, status: nil)` | `/v1/subscriptions/history` | История подписок с пагинацией |
188
193
 
189
194
  Методы с параметрами принимают либо именованные аргументы, либо объект запроса из
190
195
  `Tronzap::Requests`, поэтому запрос можно создать, проверить и передать дальше до
@@ -198,10 +203,12 @@ client.create_energy_transaction(request)
198
203
  Запрос проверяется при создании, поэтому невалидный запрос вызывает
199
204
  `ArgumentError` и никогда не доходит до API. Количества должны быть
200
205
  положительными `Integer`. Значения по умолчанию совпадают с API: `duration` —
201
- 1 час, история AML начинается со страницы 1 по 10 элементов.
206
+ 1 час, история AML и подписок начинается со страницы 1 по 10 элементов.
207
+ Исключение — `start_subscription`: `duration_days` и `transactions_limit` по
208
+ умолчанию равны 0, что означает отсутствие ограничения.
202
209
 
203
210
  Результаты — неизменяемые объекты `Data` в `Tronzap::Responses` и
204
- `Tronzap::Models`, а не хеши: `transaction.status`, `estimate.energy`. Коллекции
211
+ `Tronzap::Models`, а не хеши: `transaction.status`, `estimate.amount`. Коллекции
205
212
  заморожены и никогда не бывают `nil`, а значения, которые API может не прислать,
206
213
  равны `nil`.
207
214
 
@@ -232,9 +239,9 @@ client.create_resource_bundle_transaction(
232
239
  client.create_address_activation_transaction(address: "TRecipientAddress", external_id: "activation-1")
233
240
  ```
234
241
 
235
- Цена энергии указана за единицу, цена bandwidth — за 1000 единиц: в
236
- `get_services` `EnergyRate#price` × 65000 — это стоимость 65000 энергии, а 345
237
- bandwidth при `BandwidthRate#price`, равном 1, стоят 0.345.
242
+ В `get_services` цены энергии и bandwidth указаны за 1000 единиц, поэтому покупка
243
+ стоит `price × amount / 1000`: 65000 энергии при `EnergyRate#price`, равном 0.03,
244
+ стоят 1.95, а 345 bandwidth при `BandwidthRate#price`, равном 1, стоят 0.345.
238
245
 
239
246
  Сейчас API возвращает пакет ресурсов с `service`, равным `:energy`, а не
240
247
  `:resource_bundle`. Состав покупки смотрите в `params.amounts`.
@@ -266,9 +273,50 @@ if result.status == :completed
266
273
  end
267
274
  ```
268
275
 
276
+ Для проверки по hash `address` — это адрес получателя средств в транзакции, а
277
+ `direction` указывает, на какой стороне транзакции вы: `:deposit`, если средства
278
+ пришли на ваш адрес (`address` — ваш адрес), `:withdrawal`, если их отправили вы
279
+ (`address` — адрес внешнего получателя). Риск оценивается для контрагента: для
280
+ deposit — для отправителя, для withdrawal — для получателя. Без `direction` SDK
281
+ отправляет `deposit`.
282
+
269
283
  `risk_score` равен `nil`, пока проверка не завершится. У завершённой проверки score
270
284
  может быть равен 0, и это не то же самое, что отсутствие результата.
271
285
 
286
+ ### Подписки
287
+
288
+ Подписка обеспечивает адрес энергией для каждой транзакции, пока её не
289
+ остановят или не закончатся её дни или транзакции. Выберите план из
290
+ `get_subscriptions` и передайте его `subscription_id`, например
291
+ `"unlimited_energy"`, а не числовой `id`. Запуск подписки списывает начальную
292
+ цену плана.
293
+
294
+ ```ruby
295
+ client.get_subscriptions.each do |plan|
296
+ puts "#{plan.subscription_id} #{plan.initial_price.to_s("F")} #{plan.price.to_s("F")}"
297
+ end
298
+
299
+ subscription = client.start_subscription(
300
+ subscription_id: "unlimited_energy",
301
+ address: "TRecipientAddress",
302
+ duration_days: 30, # 0 — без ограничения по времени
303
+ transactions_limit: 0, # 0 — без ограничения
304
+ external_id: "subscription-42"
305
+ )
306
+
307
+ subscription = client.check_subscription(external_id: "subscription-42")
308
+
309
+ subscription = client.stop_subscription(id: subscription.id)
310
+
311
+ history = client.get_subscription_history(status: :active)
312
+ ```
313
+
314
+ Запуск, проверка и остановка возвращают подписку с её `params`, а история
315
+ вместо них — счётчики использования `transactions_used`, `energy_used` и
316
+ `total_price`, и `params` в ней равен `nil`. Статусы подписки перечислены в
317
+ `Tronzap::Models::SUBSCRIPTION_STATUSES`. Подписку с лимитом транзакций
318
+ остановить нельзя (`CANNOT_STOP_SUBSCRIPTION`).
319
+
272
320
  ## Обработка ошибок
273
321
 
274
322
  Любой сбой вызова API — исключение `Tronzap::Error`. Перехватывайте подкласс, чтобы
@@ -339,11 +387,11 @@ end
339
387
  | 2 | `INVALID_SERVICE_OR_PARAMS` | Неверный сервис или параметры |
340
388
  | 5 | `WALLET_NOT_FOUND` | Внутренний кошелёк не найден. Обратитесь в поддержку. |
341
389
  | 6 | `INSUFFICIENT_FUNDS` | Недостаточно средств |
342
- | 10 | `INVALID_TRON_ADDRESS` | Неверный адрес TRON |
390
+ | 10 | `INVALID_TRON_ADDRESS` | Неверный адрес TRON, или у адреса уже есть активная подписка |
343
391
  | 11 | `INVALID_ENERGY_AMOUNT` | Неверное количество энергии |
344
392
  | 12 | `INVALID_DURATION` | Неверная длительность |
345
393
  | 20 | `TRANSACTION_NOT_FOUND` | Транзакция/подписка не найдена |
346
- | 21 | `CANNOT_STOP_SUBSCRIPTION` | Невозможно остановить подписку |
394
+ | 21 | `CANNOT_STOP_SUBSCRIPTION` | Невозможно остановить подписку, например, у неё есть лимит транзакций |
347
395
  | 24 | `ADDRESS_NOT_ACTIVATED` | Адрес не активирован |
348
396
  | 25 | `ADDRESS_ALREADY_ACTIVATED` | Адрес уже активирован |
349
397
  | 30 | `AML_CHECK_NOT_FOUND` | AML-проверка не найдена |
@@ -4,8 +4,8 @@ require "digest"
4
4
  require "json"
5
5
 
6
6
  module Tronzap
7
- # Client for the {https://docs.tronzap.com/ TronZap API}: buy TRON energy and bandwidth, activate addresses and
8
- # run AML checks.
7
+ # Client for the {https://docs.tronzap.com/ TronZap API}: buy TRON energy and bandwidth, activate addresses, run
8
+ # AML checks and manage energy subscriptions.
9
9
  #
10
10
  # A client is immutable and safe to share between threads. Create one per set of credentials.
11
11
  #
@@ -157,7 +157,7 @@ module Tronzap
157
157
  post("/v1/aml-checks", {}) { Responses::AmlService.list_from_api(_1) }
158
158
  end
159
159
 
160
- # Starts an AML screening of an address or a transaction.
160
+ # Starts an AML screening of an address or a transaction. A hash check without a direction sends +deposit+.
161
161
  #
162
162
  # @overload create_aml_check(request)
163
163
  # @param request [Requests::AmlCheck] see {Requests::AmlCheck.for_address} and {Requests::AmlCheck.for_hash}
@@ -191,6 +191,65 @@ module Tronzap
191
191
  post("/v1/aml-checks/history", body) { Responses::AmlHistory.from_api(_1) }
192
192
  end
193
193
 
194
+ # Subscription plans on sale, in the order the API lists them.
195
+ #
196
+ # @return [Array<Responses::SubscriptionPlan>]
197
+ # @raise [Error]
198
+ def get_subscriptions
199
+ post("/v1/subscriptions", {}) { Responses::SubscriptionPlan.list_from_api(_1) }
200
+ end
201
+
202
+ # Subscribes an address to a plan from {#get_subscriptions}. Starting a subscription charges the plan's initial
203
+ # price.
204
+ #
205
+ # @overload start_subscription(request)
206
+ # @param request [Requests::StartSubscription]
207
+ # @overload start_subscription(subscription_id:, address:, duration_days: 0, transactions_limit: 0,
208
+ # external_id: nil, activate_address: false)
209
+ # @return [Responses::Subscription]
210
+ # @raise [ArgumentError, Error]
211
+ def start_subscription(request = nil, **params)
212
+ body = build(Requests::StartSubscription, request, params).body
213
+ post("/v1/subscription/start", body) { Responses::Subscription.from_api(_1) }
214
+ end
215
+
216
+ # The current state of a subscription, by its TronZap ID or by your external ID.
217
+ #
218
+ # @overload check_subscription(request)
219
+ # @param request [Requests::SubscriptionLookup]
220
+ # @overload check_subscription(id: nil, external_id: nil)
221
+ # @return [Responses::Subscription]
222
+ # @raise [ArgumentError, Error]
223
+ def check_subscription(request = nil, **params)
224
+ body = build(Requests::SubscriptionLookup, request, params).body
225
+ post("/v1/subscription/check", body) { Responses::Subscription.from_api(_1) }
226
+ end
227
+
228
+ # Stops a subscription, by its TronZap ID or by your external ID. A subscription with a transactions limit
229
+ # cannot be stopped and fails with {ErrorCode::CANNOT_STOP_SUBSCRIPTION}.
230
+ #
231
+ # @overload stop_subscription(request)
232
+ # @param request [Requests::SubscriptionLookup]
233
+ # @overload stop_subscription(id: nil, external_id: nil)
234
+ # @return [Responses::Subscription]
235
+ # @raise [ArgumentError, Error]
236
+ def stop_subscription(request = nil, **params)
237
+ body = build(Requests::SubscriptionLookup, request, params).body
238
+ post("/v1/subscription/stop", body) { Responses::Subscription.from_api(_1) }
239
+ end
240
+
241
+ # One page of your subscriptions, newest first.
242
+ #
243
+ # @overload get_subscription_history(request)
244
+ # @param request [Requests::SubscriptionHistory]
245
+ # @overload get_subscription_history(page: 1, per_page: 10, status: nil)
246
+ # @return [Responses::SubscriptionHistory]
247
+ # @raise [ArgumentError, Error]
248
+ def get_subscription_history(request = nil, **params)
249
+ body = build(Requests::SubscriptionHistory, request, params).body
250
+ post("/v1/subscriptions/history", body) { Responses::SubscriptionHistory.from_api(_1) }
251
+ end
252
+
194
253
  # @return [String] a description that leaves out the credentials
195
254
  def inspect
196
255
  "#<#{self.class.name} base_url=#{config.base_url.inspect}>"
@@ -53,6 +53,10 @@ module Tronzap
53
53
  number.to_i
54
54
  end
55
55
 
56
+ def optional_integer(value, name)
57
+ value.nil? ? nil : integer(value, name)
58
+ end
59
+
56
60
  def decimal(value, name)
57
61
  optional_decimal(value, name) || BigDecimal(0)
58
62
  end
@@ -123,7 +123,8 @@ module Tronzap
123
123
  WALLET_NOT_FOUND = 5
124
124
  # Insufficient funds: top up the account or request a smaller amount.
125
125
  INSUFFICIENT_FUNDS = 6
126
- # Invalid TRON address: it should be a valid 34-character TRON address.
126
+ # Invalid TRON address: it should be a valid 34-character TRON address. Also reported when the address already
127
+ # has an active subscription.
127
128
  INVALID_TRON_ADDRESS = 10
128
129
  # Invalid energy amount.
129
130
  INVALID_ENERGY_AMOUNT = 11
@@ -132,7 +133,7 @@ module Tronzap
132
133
  # Transaction or subscription not found: check the transaction ID or external ID. The API reports this code
133
134
  # under the key +subscription_not_found+.
134
135
  TRANSACTION_NOT_FOUND = 20
135
- # The subscription cannot be stopped right now.
136
+ # Cannot stop subscription, for example because it has a transactions limit.
136
137
  CANNOT_STOP_SUBSCRIPTION = 21
137
138
  # Address not activated: activate it first with an address activation transaction.
138
139
  ADDRESS_NOT_ACTIVATED = 24