abacatepay-ruby 0.1.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 +4 -4
- data/.rubocop.yml +136 -0
- data/CHANGELOG.md +171 -1
- data/README.md +491 -87
- data/Rakefile +1 -1
- data/abacatepay-ruby.gemspec +17 -10
- data/lib/abacate_pay/clients/billing_client.rb +90 -0
- data/lib/abacate_pay/clients/checkout_client.rb +91 -0
- data/lib/abacate_pay/clients/client.rb +202 -0
- data/lib/abacate_pay/clients/coupon_client.rb +57 -0
- data/lib/abacate_pay/clients/customer_client.rb +58 -0
- data/lib/abacate_pay/clients/payment_link_client.rb +71 -0
- data/lib/abacate_pay/clients/payout_client.rb +41 -0
- data/lib/abacate_pay/clients/pix_client.rb +47 -0
- data/lib/abacate_pay/clients/product_client.rb +54 -0
- data/lib/abacate_pay/clients/store_client.rb +40 -0
- data/lib/abacate_pay/clients/subscription_client.rb +90 -0
- data/lib/abacate_pay/clients/transparent_client.rb +125 -0
- data/lib/abacate_pay/clients/webhook_client.rb +82 -0
- data/lib/abacate_pay/clients.rb +24 -0
- data/lib/abacate_pay/collection.rb +98 -0
- data/lib/abacate_pay/configuration.rb +83 -0
- data/lib/{abacatepay/enums/billing → abacate_pay/enums/billings}/frequencies.rb +9 -3
- data/lib/{abacatepay/enums/billing → abacate_pay/enums/billings}/methods.rb +12 -3
- data/lib/{abacatepay/enums/billing → abacate_pay/enums/billings}/statuses.rb +3 -2
- data/lib/abacate_pay/enums/checkouts/statuses.rb +29 -0
- data/lib/abacate_pay/enums/coupons/discount_kinds.rb +26 -0
- data/lib/abacate_pay/enums/coupons/statuses.rb +27 -0
- data/lib/abacate_pay/enums/payouts/statuses.rb +29 -0
- data/lib/abacate_pay/enums/pix/key_types.rb +30 -0
- data/lib/abacate_pay/enums/products/cycles.rb +28 -0
- data/lib/abacate_pay/enums/transfers/statuses.rb +30 -0
- data/lib/abacate_pay/enums/webhooks/event_types.rb +50 -0
- data/lib/abacate_pay/enums.rb +20 -0
- data/lib/{abacatepay/resources/billing → abacate_pay/resources/billings}/metadata.rb +2 -8
- data/lib/{abacatepay/resources/billing → abacate_pay/resources/billings}/product.rb +2 -8
- data/lib/{abacatepay/resources/billing.rb → abacate_pay/resources/billings.rb} +16 -16
- data/lib/abacate_pay/resources/checkouts.rb +84 -0
- data/lib/abacate_pay/resources/coupons.rb +41 -0
- data/lib/{abacatepay/resources/customer → abacate_pay/resources/customers}/metadata.rb +2 -8
- data/lib/{abacatepay/resources/customer.rb → abacate_pay/resources/customers.rb} +5 -5
- data/lib/abacate_pay/resources/payouts.rb +40 -0
- data/lib/abacate_pay/resources/pix_transfers.rb +41 -0
- data/lib/abacate_pay/resources/products.rb +40 -0
- data/lib/{abacatepay → abacate_pay}/resources/resource.rb +33 -21
- data/lib/abacate_pay/resources/store/balance.rb +20 -0
- data/lib/abacate_pay/resources/store.rb +36 -0
- data/lib/abacate_pay/resources/subscriptions.rb +72 -0
- data/lib/abacate_pay/resources/transparents.rb +53 -0
- data/lib/abacate_pay/resources/webhook_endpoints.rb +49 -0
- data/lib/abacate_pay/resources.rb +27 -0
- data/lib/{abacatepay → abacate_pay}/version.rb +2 -2
- data/lib/abacate_pay/webhooks/event.rb +20 -0
- data/lib/abacate_pay/webhooks.rb +100 -0
- data/lib/abacate_pay.rb +112 -4
- metadata +75 -47
- data/lib/abacatepay/clients/billing_client.rb +0 -60
- data/lib/abacatepay/clients/client.rb +0 -68
- data/lib/abacatepay/clients/customer_client.rb +0 -39
- data/lib/abacatepay/clients.rb +0 -12
- data/lib/abacatepay/configuration.rb +0 -56
- data/lib/abacatepay/enums.rb +0 -12
- data/lib/abacatepay/resources.rb +0 -15
- data/sig/abacatepay/rails.rbs +0 -6
data/README.md
CHANGED
|
@@ -1,121 +1,258 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# AbacatePay Ruby SDK
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
SDK oficial da **AbacatePay** para integrar pagamentos via **PIX** de forma simples, segura e idiomática em Ruby.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
gem 'abacatepay-ruby'
|
|
9
|
-
```
|
|
7
|
+
O [`abacatepay-ruby`](https://rubygems.org/gems/abacatepay-ruby) é um **wrapper versionado de alto nível** sobre a API da AbacatePay, focado em **DX**, **verificação segura de webhooks** e **erros tipados**.
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
<img src="https://res.cloudinary.com/dkok1obj5/image/upload/v1767631413/avo_clhmaf.png" width="100%" alt="AbacatePay Open Source"/>
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
Funciona em qualquer aplicação Ruby — Rails, Sinatra, Hanami ou Ruby puro.
|
|
12
|
+
|
|
13
|
+
Referência completa da API [aqui](https://abacatepay.readme.io/reference).
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
## Requisitos
|
|
16
|
+
|
|
17
|
+
Ruby **3.2 ou superior**. Testado em 3.2, 3.3, 3.4 e 4.0.
|
|
18
|
+
|
|
19
|
+
## Instalação
|
|
20
|
+
|
|
21
|
+
</div>
|
|
18
22
|
|
|
19
23
|
```bash
|
|
20
|
-
|
|
24
|
+
bundle add abacatepay-ruby
|
|
21
25
|
```
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
<div align="center">
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
Ou adicione ao seu `Gemfile`:
|
|
30
|
+
|
|
31
|
+
</div>
|
|
26
32
|
|
|
27
33
|
```ruby
|
|
28
|
-
|
|
29
|
-
AbacatePay.configure do |config|
|
|
30
|
-
config.api_token = ENV['ABACATEPAY_TOKEN']
|
|
31
|
-
config.environment = :sandbox # or :production
|
|
32
|
-
end
|
|
34
|
+
gem 'abacatepay-ruby'
|
|
33
35
|
```
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
<div align="center">
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
## Uso básico
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
</div>
|
|
40
42
|
|
|
41
43
|
```ruby
|
|
42
|
-
|
|
44
|
+
AbacatePay.configure do |config|
|
|
45
|
+
config.api_token = ENV['ABACATEPAY_TOKEN']
|
|
46
|
+
config.timeout = 30 # opcional, segundos (default 30)
|
|
47
|
+
config.max_retries = 2 # opcional, retry em 429/5xx (default 2, 0 desliga)
|
|
48
|
+
config.logger = Rails.logger # opcional, token é redigido
|
|
49
|
+
end
|
|
43
50
|
```
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
<div align="center">
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
Nunca utilize sua API key diretamente no código.
|
|
55
|
+
**Sempre use variáveis de ambiente**.
|
|
48
56
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
Em Rails, coloque isso em `config/initializers/abacatepay.rb`.
|
|
58
|
+
|
|
59
|
+
Trocar o token em runtime tem efeito imediato — os clients são reconstruídos a cada `configure`.
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
### Criando uma cobrança
|
|
54
62
|
|
|
55
|
-
|
|
63
|
+
</div>
|
|
56
64
|
|
|
57
65
|
```ruby
|
|
58
|
-
|
|
59
|
-
AbacatePay::Resources::
|
|
60
|
-
frequency:
|
|
61
|
-
methods: [
|
|
66
|
+
checkout = AbacatePay.checkouts.create(
|
|
67
|
+
AbacatePay::Resources::Checkouts.new(
|
|
68
|
+
frequency: 'ONE_TIME',
|
|
69
|
+
methods: ['PIX'],
|
|
62
70
|
products: [
|
|
63
|
-
AbacatePay::Resources::
|
|
64
|
-
external_id: '
|
|
71
|
+
AbacatePay::Resources::Billings::Product.new(
|
|
72
|
+
external_id: 'prod_123',
|
|
65
73
|
name: 'Product A',
|
|
66
|
-
description: 'Description of product A',
|
|
67
74
|
quantity: 1,
|
|
68
|
-
price: 100
|
|
75
|
+
price: 100
|
|
69
76
|
)
|
|
70
77
|
],
|
|
71
|
-
|
|
72
|
-
return_url: 'https://www.abacatepay.com',
|
|
73
|
-
completion_url: 'https://www.abacatepay.com'
|
|
74
|
-
),
|
|
75
|
-
customer: AbacatePay::Resources::Customer.new(
|
|
76
|
-
metadata: AbacatePay::Resources::Customer::Metadata.new(
|
|
77
|
-
name: 'Abacate Lover',
|
|
78
|
-
cellphone: '01912341234',
|
|
79
|
-
email: 'lover@abacate.com',
|
|
80
|
-
tax_id: '13827826837'
|
|
81
|
-
)
|
|
82
|
-
)
|
|
78
|
+
customer: AbacatePay::Resources::Customers.new(id: 'cust_123')
|
|
83
79
|
)
|
|
84
80
|
)
|
|
85
81
|
```
|
|
86
82
|
|
|
87
|
-
|
|
83
|
+
<div align="center">
|
|
84
|
+
|
|
85
|
+
### Procure por alguns clientes
|
|
86
|
+
|
|
87
|
+
</div>
|
|
88
88
|
|
|
89
89
|
```ruby
|
|
90
|
-
AbacatePay
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
customers = AbacatePay.customers.list(limit: 25)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
<div align="center">
|
|
94
|
+
|
|
95
|
+
Todos os métodos `list` aceitam parâmetros de paginação e filtro opcionais:
|
|
96
|
+
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
AbacatePay.customers.list(limit: 10, after: 'cursor_abc')
|
|
101
|
+
AbacatePay.checkouts.list(status: 'PAID', email: 'user@example.com')
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
<div align="center">
|
|
105
|
+
|
|
106
|
+
Listas retornam no máximo 100 itens. O resultado é uma `Collection` — funciona como Array, e ainda carrega o cursor:
|
|
107
|
+
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
page = AbacatePay.customers.list
|
|
112
|
+
page.first.id # funciona como Array
|
|
113
|
+
page.has_more? # => true
|
|
114
|
+
page.next_cursor # => "cust_abc123"
|
|
115
|
+
|
|
116
|
+
# Para percorrer tudo sem lidar com cursor:
|
|
117
|
+
AbacatePay.customers.auto_paging_each { |customer| puts customer.id }
|
|
118
|
+
AbacatePay.customers.each_page { |page| puts page.size }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
<div align="center">
|
|
122
|
+
|
|
123
|
+
## Versionamento
|
|
124
|
+
|
|
125
|
+
O SDK fala **exclusivamente a v2** — `https://api.abacatepay.com/v2`. A v1 ainda existe para integrações legadas, mas usa outro dialeto (caminhos no singular como `/v1/billing/`, `/v1/customer/`) que este SDK nunca implementou. Se você precisa da v1, chame a API diretamente.
|
|
126
|
+
|
|
127
|
+
O ambiente (dev mode x produção) é definido **pela chave de API**, não por configuração: chaves de Dev mode geram transações simuladas. Por isso `config.environment` não faz nada — ela continua aceita para não quebrar initializers existentes, mas emite aviso de depreciação.
|
|
128
|
+
|
|
129
|
+
O `BillingClient` também está descontinuado, substituído pelo `CheckoutClient`. Ele emite um aviso ao ser instanciado, e seus endpoints `/billings/*` não existem na v2:
|
|
130
|
+
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
[DEPRECATION] BillingClient is deprecated. Use CheckoutClient instead.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
<div align="center">
|
|
138
|
+
|
|
139
|
+
## Tratamento de erros
|
|
140
|
+
|
|
141
|
+
Diferente do SDK de Node, **este SDK levanta exceções** — ele não retorna `{ data, error, success }`. Toda falha vira uma exceção tipada que herda de `AbacatePay::Error`, então você pode capturar tudo de uma vez ou tratar caso a caso.
|
|
142
|
+
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
begin
|
|
147
|
+
checkout = AbacatePay.checkouts.create(data)
|
|
148
|
+
rescue AbacatePay::ConfigurationError => e
|
|
149
|
+
# token ausente ou vazio
|
|
150
|
+
rescue AbacatePay::ApiError => e
|
|
151
|
+
# a API recusou a chamada, ou houve falha de rede/timeout
|
|
152
|
+
Rails.logger.error(e.message)
|
|
153
|
+
end
|
|
93
154
|
```
|
|
94
155
|
|
|
95
|
-
|
|
156
|
+
<div align="center">
|
|
157
|
+
|
|
158
|
+
| Exceção | Quando acontece |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `AbacatePay::ConfigurationError` | Token ausente ou vazio |
|
|
161
|
+
| `AbacatePay::ApiError` | Erro da API, falha de rede ou timeout |
|
|
162
|
+
| `AbacatePay::Webhooks::SignatureError` | Assinatura de webhook ausente, vazia ou inválida |
|
|
163
|
+
| `AbacatePay::Webhooks::PayloadError` | Corpo do webhook malformado ou que não é um objeto JSON |
|
|
164
|
+
|
|
165
|
+
Erros de rede e timeout são normalizados para `ApiError`, com a mensagem da API preservada quando ela envia uma.
|
|
166
|
+
|
|
167
|
+
## Webhooks
|
|
96
168
|
|
|
97
|
-
**
|
|
169
|
+
Endpoints de webhook são públicos e não autenticados. Use `construct_event`, que **verifica a assinatura antes de fazer o parse** — é o único ponto de entrada que não permite agir sobre um payload não verificado.
|
|
170
|
+
|
|
171
|
+
</div>
|
|
98
172
|
|
|
99
173
|
```ruby
|
|
100
|
-
|
|
174
|
+
payload = request.body.read
|
|
175
|
+
signature = request.headers['X-Webhook-Signature']
|
|
176
|
+
secret = ENV['ABACATEPAY_WEBHOOK_SECRET']
|
|
177
|
+
|
|
178
|
+
begin
|
|
179
|
+
event = AbacatePay::Webhooks.construct_event(
|
|
180
|
+
payload: payload, signature: signature, secret: secret
|
|
181
|
+
)
|
|
182
|
+
rescue AbacatePay::Webhooks::SignatureError
|
|
183
|
+
return head :unauthorized
|
|
184
|
+
rescue AbacatePay::Webhooks::PayloadError
|
|
185
|
+
return head :bad_request
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
case event.type
|
|
189
|
+
when 'checkout.completed' then handle_payment(event.data)
|
|
190
|
+
when 'checkout.refunded' then handle_refund(event.data)
|
|
191
|
+
when 'subscription.renewed' then handle_renewal(event.data)
|
|
192
|
+
end
|
|
101
193
|
```
|
|
102
194
|
|
|
103
|
-
|
|
195
|
+
<div align="center">
|
|
104
196
|
|
|
105
|
-
|
|
197
|
+
Header ausente, secret vazio, assinatura forjada e corpo malformado são todos tratados como casos esperados — levantam erro tipado em vez de derrubar o endpoint. A comparação de assinatura é feita em tempo constante.
|
|
198
|
+
|
|
199
|
+
Os métodos de baixo nível continuam disponíveis:
|
|
200
|
+
|
|
201
|
+
</div>
|
|
106
202
|
|
|
107
203
|
```ruby
|
|
108
|
-
|
|
204
|
+
# Levanta SignatureError se a assinatura estiver ausente ou inválida
|
|
205
|
+
AbacatePay::Webhooks.verify!(payload: payload, signature: signature, secret: secret)
|
|
206
|
+
|
|
207
|
+
# Contraparte booleana — nunca levanta exceção
|
|
208
|
+
AbacatePay::Webhooks.valid?(payload: payload, signature: signature, secret: secret)
|
|
209
|
+
|
|
210
|
+
# Faz parse de um corpo já verificado
|
|
211
|
+
AbacatePay::Webhooks.parse(payload)
|
|
109
212
|
```
|
|
110
213
|
|
|
111
|
-
|
|
214
|
+
<div align="center">
|
|
215
|
+
|
|
216
|
+
### Eventos disponíveis
|
|
217
|
+
|
|
218
|
+
| Categoria | Eventos |
|
|
219
|
+
|---|---|
|
|
220
|
+
| Checkout | `checkout.completed`, `checkout.refunded`, `checkout.disputed` |
|
|
221
|
+
| Transparent | `transparent.completed`, `transparent.refunded`, `transparent.disputed` |
|
|
222
|
+
| Subscription | `subscription.completed`, `subscription.renewed`, `subscription.cancelled` |
|
|
223
|
+
| Transfer | `transfer.completed`, `transfer.failed` |
|
|
224
|
+
| Payout | `payout.completed`, `payout.failed` |
|
|
112
225
|
|
|
113
|
-
|
|
226
|
+
## Recursos
|
|
227
|
+
|
|
228
|
+
Todos os recursos são acessíveis pela fachada `AbacatePay.<recurso>`.
|
|
229
|
+
|
|
230
|
+
| Recurso | Métodos |
|
|
231
|
+
|---|---|
|
|
232
|
+
| `customers` | `list` `get` `create` `delete` |
|
|
233
|
+
| `products` | `list` `get` `create` `delete` |
|
|
234
|
+
| `coupons` | `list` `get` `create` `delete` `toggle` |
|
|
235
|
+
| `checkouts` | `list` `get` `create` `refund` |
|
|
236
|
+
| `subscriptions` | `list` `create` `cancel` `change_plan` `record_usage` |
|
|
237
|
+
| `transparents` | `list` `create` `check` `simulate_payment` `refund` |
|
|
238
|
+
| `pix` | `list` `get` `send_pix` |
|
|
239
|
+
| `payouts` | `list` `get` `create` |
|
|
240
|
+
| `store` | `get` `merchant_info` `mrr` `revenue` |
|
|
241
|
+
| `payment_links` | `list` `get` `create` `refund` |
|
|
242
|
+
| `webhook_endpoints` | `list` `get` `create` `delete` |
|
|
243
|
+
|
|
244
|
+
### Clientes
|
|
245
|
+
|
|
246
|
+
</div>
|
|
114
247
|
|
|
115
248
|
```ruby
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
249
|
+
AbacatePay.customers.list
|
|
250
|
+
AbacatePay.customers.get('cust_123')
|
|
251
|
+
AbacatePay.customers.delete('cust_123')
|
|
252
|
+
|
|
253
|
+
AbacatePay.customers.create(
|
|
254
|
+
AbacatePay::Resources::Customers.new(
|
|
255
|
+
metadata: AbacatePay::Resources::Customers::Metadata.new(
|
|
119
256
|
name: 'Abacate Lover',
|
|
120
257
|
cellphone: '01912341234',
|
|
121
258
|
email: 'lover@abacate.com',
|
|
@@ -125,42 +262,309 @@ customer_client.create(
|
|
|
125
262
|
)
|
|
126
263
|
```
|
|
127
264
|
|
|
128
|
-
|
|
265
|
+
<div align="center">
|
|
129
266
|
|
|
130
|
-
|
|
131
|
-
https://abacatepay.readme.io/reference
|
|
267
|
+
### Produtos
|
|
132
268
|
|
|
133
|
-
|
|
269
|
+
</div>
|
|
134
270
|
|
|
135
|
-
|
|
271
|
+
```ruby
|
|
272
|
+
AbacatePay.products.create(
|
|
273
|
+
AbacatePay::Resources::Products.new(
|
|
274
|
+
external_id: 'my-product-1',
|
|
275
|
+
name: 'Monthly Plan',
|
|
276
|
+
price: 2990, # R$ 29,90 em centavos
|
|
277
|
+
currency: 'BRL',
|
|
278
|
+
description: 'Acesso a todos os recursos',
|
|
279
|
+
cycle: 'MONTHLY' # ou nil para pagamento único
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
```
|
|
136
283
|
|
|
137
|
-
|
|
138
|
-
2. Create a new branch for your feature or fix:
|
|
284
|
+
<div align="center">
|
|
139
285
|
|
|
140
|
-
|
|
141
|
-
|
|
286
|
+
### Cupons
|
|
287
|
+
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
AbacatePay.coupons.create(
|
|
292
|
+
AbacatePay::Resources::Coupons.new(
|
|
293
|
+
code: 'SAVE20',
|
|
294
|
+
discount: 20,
|
|
295
|
+
discount_kind: 'PERCENTAGE', # ou 'FIXED'
|
|
296
|
+
max_redeems: 100
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
AbacatePay.coupons.toggle('coup_123')
|
|
142
301
|
```
|
|
143
302
|
|
|
144
|
-
|
|
303
|
+
<div align="center">
|
|
145
304
|
|
|
146
|
-
|
|
147
|
-
|
|
305
|
+
### Assinaturas
|
|
306
|
+
|
|
307
|
+
Exigem exatamente um produto com `cycle` definido.
|
|
308
|
+
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
```ruby
|
|
312
|
+
AbacatePay.subscriptions.create(
|
|
313
|
+
AbacatePay::Resources::Subscriptions.new(
|
|
314
|
+
methods: ['PIX'],
|
|
315
|
+
customer: AbacatePay::Resources::Customers.new(id: 'cust_123'),
|
|
316
|
+
products: [
|
|
317
|
+
AbacatePay::Resources::Billings::Product.new(
|
|
318
|
+
external_id: 'plan-monthly',
|
|
319
|
+
name: 'Monthly Plan',
|
|
320
|
+
price: 2990,
|
|
321
|
+
quantity: 1
|
|
322
|
+
)
|
|
323
|
+
]
|
|
324
|
+
)
|
|
325
|
+
)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
<div align="center">
|
|
329
|
+
|
|
330
|
+
### PIX transparente (QR Code)
|
|
331
|
+
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
```ruby
|
|
335
|
+
AbacatePay.transparents.create(
|
|
336
|
+
AbacatePay::Resources::Transparents.new(
|
|
337
|
+
amount: 1000,
|
|
338
|
+
description: 'Pedido #123',
|
|
339
|
+
expires_in: 3600
|
|
340
|
+
)
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
AbacatePay.transparents.check('tr_123')
|
|
344
|
+
AbacatePay.transparents.simulate_payment('tr_123') # apenas em dev mode
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
<div align="center">
|
|
348
|
+
|
|
349
|
+
### Transferências PIX
|
|
350
|
+
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
```ruby
|
|
354
|
+
AbacatePay.pix.send_pix(
|
|
355
|
+
AbacatePay::Resources::PixTransfers.new(
|
|
356
|
+
amount: 500,
|
|
357
|
+
external_id: 'transfer-001',
|
|
358
|
+
description: 'Pagamento ao fornecedor',
|
|
359
|
+
key: '12345678900',
|
|
360
|
+
key_type: 'CPF' # CPF, CNPJ, PHONE, EMAIL, RANDOM, BR_CODE
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
<div align="center">
|
|
366
|
+
|
|
367
|
+
### Saques
|
|
368
|
+
|
|
369
|
+
Valor mínimo de R$ 3,50.
|
|
370
|
+
|
|
371
|
+
</div>
|
|
372
|
+
|
|
373
|
+
```ruby
|
|
374
|
+
AbacatePay.payouts.create(
|
|
375
|
+
AbacatePay::Resources::Payouts.new(
|
|
376
|
+
amount: 5000,
|
|
377
|
+
external_id: 'withdrawal-001',
|
|
378
|
+
description: 'Saque mensal'
|
|
379
|
+
)
|
|
380
|
+
)
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
<div align="center">
|
|
384
|
+
|
|
385
|
+
### Boleto
|
|
386
|
+
|
|
387
|
+
Boleto tem vencimento, juros e multa próprios. Todos os valores em centavos.
|
|
388
|
+
|
|
389
|
+
</div>
|
|
390
|
+
|
|
391
|
+
```ruby
|
|
392
|
+
AbacatePay.checkouts.create(
|
|
393
|
+
AbacatePay::Resources::Checkouts.new(
|
|
394
|
+
methods: ['BOLETO'],
|
|
395
|
+
due_date: '2026-08-15', # opcional; default 3 dias úteis
|
|
396
|
+
interest: { value: 100 }, # juros ao mês
|
|
397
|
+
fine: { value: 200, type: 'PERCENTAGE' }, # ou type: 'FIXED'
|
|
398
|
+
products: [
|
|
399
|
+
AbacatePay::Resources::Billings::Product.new(external_id: 'prod_123', quantity: 1)
|
|
400
|
+
]
|
|
401
|
+
)
|
|
402
|
+
)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
<div align="center">
|
|
406
|
+
|
|
407
|
+
No checkout transparente, o boleto exige nome e CPF/CNPJ do pagador — o SDK valida antes de chamar a API:
|
|
408
|
+
|
|
409
|
+
</div>
|
|
410
|
+
|
|
411
|
+
```ruby
|
|
412
|
+
charge = AbacatePay::Resources::Transparents.new(amount: 25_000, due_date: '2026-08-15')
|
|
413
|
+
# charge.customer precisa ter metadata.name e metadata.tax_id
|
|
414
|
+
|
|
415
|
+
boleto = AbacatePay.transparents.create(charge, method: 'BOLETO')
|
|
416
|
+
boleto.bar_code # linha digitável
|
|
417
|
+
boleto.url # PDF para impressão
|
|
418
|
+
boleto.br_code # PIX alternativo da mesma cobrança
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
<div align="center">
|
|
422
|
+
|
|
423
|
+
### Parcelamento e order bump
|
|
424
|
+
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
```ruby
|
|
428
|
+
AbacatePay.checkouts.create(
|
|
429
|
+
AbacatePay::Resources::Checkouts.new(
|
|
430
|
+
methods: ['CARD'],
|
|
431
|
+
max_installments: 12,
|
|
432
|
+
up_sell_product_id: 'prod_bump',
|
|
433
|
+
custom_metadata: { origem: 'app-mobile' },
|
|
434
|
+
products: [
|
|
435
|
+
AbacatePay::Resources::Billings::Product.new(external_id: 'prod_123', quantity: 1)
|
|
436
|
+
]
|
|
437
|
+
)
|
|
438
|
+
)
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
<div align="center">
|
|
442
|
+
|
|
443
|
+
### Links de pagamento
|
|
444
|
+
|
|
445
|
+
Um link reutilizável, pago por vários clientes de forma independente — vendas em massa, rifas, formulários de inscrição. Para uma cobrança por cliente, use `checkouts`.
|
|
446
|
+
|
|
447
|
+
</div>
|
|
448
|
+
|
|
449
|
+
```ruby
|
|
450
|
+
link = AbacatePay.payment_links.create(
|
|
451
|
+
AbacatePay::Resources::Checkouts.new(
|
|
452
|
+
methods: ['PIX', 'CARD'],
|
|
453
|
+
external_id: 'campanha-black-friday',
|
|
454
|
+
products: [
|
|
455
|
+
AbacatePay::Resources::Billings::Product.new(external_id: 'prod_123', quantity: 1)
|
|
456
|
+
]
|
|
457
|
+
)
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
puts link.url # compartilhe esta URL
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
<div align="center">
|
|
464
|
+
|
|
465
|
+
### Estornos
|
|
466
|
+
|
|
467
|
+
O estorno é sempre integral — a AbacatePay não faz estorno parcial.
|
|
468
|
+
|
|
469
|
+
</div>
|
|
470
|
+
|
|
471
|
+
```ruby
|
|
472
|
+
AbacatePay.checkouts.refund('bill_abc123xyz')
|
|
473
|
+
AbacatePay.transparents.refund('pix_char_abc123xyz')
|
|
474
|
+
AbacatePay.payment_links.refund('char_abc123xyz')
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
<div align="center">
|
|
478
|
+
|
|
479
|
+
### Cancelar assinatura
|
|
480
|
+
|
|
481
|
+
Cancela imediatamente; parcelas futuras pendentes são canceladas junto.
|
|
482
|
+
|
|
483
|
+
</div>
|
|
484
|
+
|
|
485
|
+
```ruby
|
|
486
|
+
AbacatePay.subscriptions.cancel('subs_abc123xyz')
|
|
487
|
+
|
|
488
|
+
# Upgrade/downgrade — vale a partir do próximo ciclo
|
|
489
|
+
AbacatePay.subscriptions.change_plan('subs_abc123xyz', product_id: 'prod_pro', quantity: 1)
|
|
490
|
+
|
|
491
|
+
# Cobrança por uso — produto sem ciclo
|
|
492
|
+
AbacatePay.subscriptions.record_usage('subs_abc123xyz', product_id: 'prod_api', units: 50)
|
|
148
493
|
```
|
|
149
494
|
|
|
150
|
-
|
|
495
|
+
<div align="center">
|
|
496
|
+
|
|
497
|
+
### Registro de webhooks
|
|
498
|
+
|
|
499
|
+
Isto gerencia **para onde** a AbacatePay entrega os eventos. Para verificar uma entrega recebida, use `Webhooks.construct_event`.
|
|
500
|
+
|
|
501
|
+
</div>
|
|
502
|
+
|
|
503
|
+
```ruby
|
|
504
|
+
AbacatePay.webhook_endpoints.create(
|
|
505
|
+
name: 'Pagamentos',
|
|
506
|
+
endpoint: 'https://meusite.com/webhooks/abacatepay', # precisa ser HTTPS
|
|
507
|
+
secret: ENV['ABACATEPAY_WEBHOOK_SECRET'],
|
|
508
|
+
events: ['checkout.completed', 'subscription.renewed']
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
AbacatePay.webhook_endpoints.list
|
|
512
|
+
AbacatePay.webhook_endpoints.delete('wh_123')
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
<div align="center">
|
|
516
|
+
|
|
517
|
+
### Loja
|
|
518
|
+
|
|
519
|
+
</div>
|
|
520
|
+
|
|
521
|
+
```ruby
|
|
522
|
+
store = AbacatePay.store.get
|
|
523
|
+
store.balance.available # => 10000
|
|
524
|
+
store.balance.pending # => 500
|
|
525
|
+
store.balance.blocked # => 0
|
|
526
|
+
|
|
527
|
+
AbacatePay.store.revenue(start_date: '2026-01-01', end_date: '2026-03-30')
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
<div align="center">
|
|
531
|
+
|
|
532
|
+
## Enums
|
|
533
|
+
|
|
534
|
+
Os valores são validados na construção do recurso — um valor inválido levanta `ArgumentError` antes de qualquer chamada de rede.
|
|
535
|
+
|
|
536
|
+
| Enum | Valores |
|
|
537
|
+
|---|---|
|
|
538
|
+
| `Billings::Methods` | `PIX`, `CARD` |
|
|
539
|
+
| `Billings::Frequencies` | `ONE_TIME`, `WEEKLY`, `MONTHLY`, `SEMIANNUALLY`, `ANNUALLY`, `MULTIPLE_PAYMENTS` |
|
|
540
|
+
| `Billings::Statuses` | `PENDING`, `EXPIRED`, `CANCELLED`, `PAID`, `REFUNDED` |
|
|
541
|
+
| `Products::Cycles` | `WEEKLY`, `MONTHLY`, `SEMIANNUALLY`, `ANNUALLY` |
|
|
542
|
+
| `Coupons::Statuses` | `ACTIVE`, `INACTIVE`, `EXPIRED` |
|
|
543
|
+
| `Coupons::DiscountKinds` | `PERCENTAGE`, `FIXED` |
|
|
544
|
+
| `Pix::KeyTypes` | `CPF`, `CNPJ`, `PHONE`, `EMAIL`, `RANDOM`, `BR_CODE` |
|
|
545
|
+
| `Transfers::Statuses` | `PENDING`, `COMPLETE`, `CANCELLED`, `EXPIRED`, `REFUNDED`, `FAILED` |
|
|
546
|
+
| `Payouts::Statuses` | `PENDING`, `COMPLETE`, `CANCELLED`, `EXPIRED`, `REFUNDED` |
|
|
547
|
+
|
|
548
|
+
## Contribuindo
|
|
549
|
+
|
|
550
|
+
</div>
|
|
151
551
|
|
|
152
552
|
```bash
|
|
153
|
-
git
|
|
553
|
+
git clone https://github.com/AbacatePay/abacatepay-ruby-sdk.git
|
|
554
|
+
cd abacatepay-ruby-sdk
|
|
555
|
+
bundle install
|
|
556
|
+
bundle exec rake # specs + rubocop
|
|
154
557
|
```
|
|
155
558
|
|
|
156
|
-
|
|
559
|
+
<div align="center">
|
|
560
|
+
|
|
561
|
+
Antes de abrir um PR, garanta que `bundle exec rake` passa e que a cobertura não caiu — o CI roda os specs em Ruby 3.2, 3.3, 3.4 e 4.0, mais RuboCop, auditoria de dependências e build do gem.
|
|
562
|
+
|
|
563
|
+
## Licença
|
|
157
564
|
|
|
158
|
-
|
|
565
|
+
MIT
|
|
159
566
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
- Includes appropriate tests
|
|
163
|
-
- Passes all existing tests (bundle exec rspec)
|
|
164
|
-
- Passes code style checks (bundle exec rubocop)
|
|
567
|
+
Feito com 🥑 pela equipe AbacatePay</br>
|
|
568
|
+
Open source, de verdade.
|
|
165
569
|
|
|
166
|
-
|
|
570
|
+
</div>
|
data/Rakefile
CHANGED