conexa 0.0.6 → 0.0.8
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/CHANGELOG.md +68 -26
- data/Gemfile +0 -2
- data/Gemfile.lock +9 -41
- data/README.md +297 -533
- data/README_pt-BR.md +7 -7
- data/REFERENCE.md +1152 -0
- data/docs/postman-collection.json +30785 -0
- data/lib/conexa/authenticator.rb +2 -0
- data/lib/conexa/model.rb +53 -2
- data/lib/conexa/object.rb +17 -0
- data/lib/conexa/resources/address.rb +16 -0
- data/lib/conexa/resources/auth.rb +38 -0
- data/lib/conexa/resources/charge.rb +82 -7
- data/lib/conexa/resources/contract.rb +58 -3
- data/lib/conexa/resources/credit_card.rb +2 -0
- data/lib/conexa/resources/customer.rb +93 -1
- data/lib/conexa/resources/invoicing_method.rb +2 -0
- data/lib/conexa/resources/person.rb +0 -14
- data/lib/conexa/resources/recurring_sale.rb +2 -0
- data/lib/conexa/resources/sale.rb +75 -1
- data/lib/conexa/version.rb +1 -1
- data/lib/conexa.rb +1 -0
- data/scripts/extract_fixtures.rb +35 -0
- metadata +8 -8
- data/.tool-versions +0 -1
- data/lib/conexa/resources/addres.rb +0 -4
data/README.md
CHANGED
|
@@ -1,640 +1,404 @@
|
|
|
1
|
-
# Conexa Ruby
|
|
1
|
+
# Conexa Ruby
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/rb/conexa)
|
|
4
|
+
[](https://github.com/guilhermegazzinelli/conexa-ruby/actions)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Documentation
|
|
8
|
-
|
|
9
|
-
- [Conexa Website](https://conexa.app/)
|
|
10
|
-
- [API Documentation](https://conexa.app/api-docs)
|
|
11
|
-
- [Conexa API Postman Collection](https://web.postman.co/workspace/8e1887b1-bef9-4e36-848f-2b6774a81022/collection/33452984-58f4d7ab-d280-4aac-8578-8366988ff7af)
|
|
12
|
-
|
|
13
|
-
## Getting Started
|
|
6
|
+
Ruby client for the [Conexa API](https://conexa.app/) - Billing and subscription management platform.
|
|
14
7
|
|
|
15
|
-
|
|
8
|
+
**[Versão em Português](README_pt-BR.md)**
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
gem install conexa-ruby
|
|
19
|
-
```
|
|
10
|
+
## Installation
|
|
20
11
|
|
|
21
|
-
|
|
12
|
+
Add to your Gemfile:
|
|
22
13
|
|
|
23
14
|
```ruby
|
|
24
|
-
gem 'conexa
|
|
15
|
+
gem 'conexa'
|
|
25
16
|
```
|
|
26
17
|
|
|
27
|
-
|
|
18
|
+
Or install directly:
|
|
28
19
|
|
|
29
|
-
|
|
20
|
+
```shell
|
|
21
|
+
gem install conexa
|
|
22
|
+
```
|
|
30
23
|
|
|
31
|
-
|
|
24
|
+
## Configuration
|
|
32
25
|
|
|
33
26
|
```ruby
|
|
34
27
|
Conexa.configure do |config|
|
|
35
|
-
config.
|
|
36
|
-
config.
|
|
28
|
+
config.subdomain = 'YOUR_SUBDOMAIN' # your-company.conexa.app
|
|
29
|
+
config.api_token = 'YOUR_API_TOKEN' # Application Token from Conexa
|
|
37
30
|
end
|
|
38
31
|
```
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
## Usage
|
|
43
|
-
|
|
44
|
-
After configuration, you can start using the gem to interact with Conexa API resources.
|
|
45
|
-
|
|
46
|
-
### Available Resources
|
|
47
|
-
|
|
48
|
-
- `Bill`
|
|
49
|
-
- `Charge`
|
|
50
|
-
- `Company`
|
|
51
|
-
- `Contract`
|
|
52
|
-
- `CreditCard`
|
|
53
|
-
- `Customer`
|
|
54
|
-
- `InvoicingMethod`
|
|
55
|
-
- `LegalPerson`
|
|
56
|
-
- `Person`
|
|
57
|
-
- `Plan`
|
|
58
|
-
- `Product`
|
|
59
|
-
- `RecurringSale`
|
|
60
|
-
- `Sale`
|
|
61
|
-
- `Supplier`
|
|
62
|
-
|
|
63
|
-
### Method Patterns
|
|
64
|
-
|
|
65
|
-
Each resource follows a consistent set of method patterns:
|
|
66
|
-
|
|
67
|
-
- `Conexa::<ResourceName>.new(params).create` - Create a new resource.
|
|
68
|
-
- `Conexa::<ResourceName>.all(params)` - Find all entities of the parameter.
|
|
69
|
-
- `Conexa::<ResourceName>.find(id)` - Retrieve a resource by ID.
|
|
70
|
-
- `Conexa::<ResourceName>.find(filter_hash, page, size)` - Retrieve resources matching filters with pagination.
|
|
71
|
-
- `Conexa::<ResourceName>.destroy(id)` - Delete a resource by ID.
|
|
72
|
-
- `Conexa::<ResourceName>.find(id).destroy` - Find and delete a resource.
|
|
73
|
-
|
|
74
|
-
### Pagination
|
|
75
|
-
|
|
76
|
-
The API uses `page` and `size` parameters for pagination with the following defaults:
|
|
77
|
-
|
|
78
|
-
- **page**: Current page number (default: `1`)
|
|
79
|
-
- **size**: Number of items per page (default: `100`)
|
|
80
|
-
|
|
81
|
-
#### Calling with Pagination
|
|
82
|
-
|
|
83
|
-
```ruby
|
|
84
|
-
# Default pagination (page 1, 100 items)
|
|
85
|
-
Conexa::Customer.all
|
|
86
|
-
|
|
87
|
-
# Positional arguments (page, size)
|
|
88
|
-
Conexa::Customer.all(2, 50) # page 2, 50 items per page
|
|
89
|
-
|
|
90
|
-
# Named parameters
|
|
91
|
-
Conexa::Customer.all(page: 3, size: 25)
|
|
92
|
-
|
|
93
|
-
# Using the `where` alias
|
|
94
|
-
Conexa::Customer.where(page: 2, size: 10)
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### Result Object
|
|
98
|
-
|
|
99
|
-
When listing resources, the API returns a `Conexa::Result` object containing:
|
|
100
|
-
|
|
101
|
-
- **data**: Array of resource objects
|
|
102
|
-
- **pagination**: `Conexa::Pagination` object with pagination metadata
|
|
103
|
-
|
|
104
|
-
```ruby
|
|
105
|
-
result = Conexa::Customer.all
|
|
106
|
-
|
|
107
|
-
# Access the data array
|
|
108
|
-
result.data # => Array of Conexa::Customer objects
|
|
109
|
-
|
|
110
|
-
# Access pagination info
|
|
111
|
-
result.pagination # => Conexa::Pagination object
|
|
112
|
-
```
|
|
33
|
+
### Authentication Methods
|
|
113
34
|
|
|
114
|
-
|
|
35
|
+
1. **Application Token** (recommended): Created in Conexa at **Config > Integrações > API / Token**
|
|
36
|
+
2. **Username/Password**: Use the `/auth` endpoint to get a JWT token
|
|
115
37
|
|
|
116
|
-
|
|
38
|
+
## Quick Start
|
|
117
39
|
|
|
118
40
|
```ruby
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
# These methods are delegated to result.data
|
|
122
|
-
result.first # => First Conexa::Customer (same as result.data.first)
|
|
123
|
-
result.second # => Second Conexa::Customer
|
|
124
|
-
result.last # => Last Conexa::Customer
|
|
125
|
-
result.length # => Number of items in current page
|
|
126
|
-
result.count # => Number of items in current page
|
|
127
|
-
result.empty? # => true if no results
|
|
128
|
-
|
|
129
|
-
# Direct iteration
|
|
130
|
-
result.each { |customer| puts customer.name }
|
|
131
|
-
|
|
132
|
-
# Transformations
|
|
133
|
-
result.map(&:name) # => Array of customer names
|
|
134
|
-
result.select { |c| c.active } # => Filter active customers
|
|
135
|
-
```
|
|
41
|
+
require 'conexa'
|
|
136
42
|
|
|
137
|
-
|
|
43
|
+
Conexa.configure do |config|
|
|
44
|
+
config.subdomain = 'mycompany'
|
|
45
|
+
config.api_token = ENV['CONEXA_API_TOKEN']
|
|
46
|
+
end
|
|
138
47
|
|
|
139
|
-
|
|
48
|
+
# List customers
|
|
49
|
+
customers = Conexa::Customer.list
|
|
50
|
+
customers.data.each do |customer|
|
|
51
|
+
puts "#{customer.customer_id}: #{customer.name}"
|
|
52
|
+
end
|
|
140
53
|
|
|
141
|
-
|
|
54
|
+
# Get a specific customer
|
|
55
|
+
customer = Conexa::Customer.retrieve(127)
|
|
56
|
+
puts customer.name
|
|
57
|
+
puts customer.address.city
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Convention
|
|
61
|
+
|
|
62
|
+
This gem follows Ruby conventions:
|
|
63
|
+
|
|
64
|
+
- **Parameters**: Use `snake_case` when calling methods - the gem automatically converts to `camelCase` for the API
|
|
65
|
+
- **Responses**: API responses are converted from `camelCase` to `snake_case`
|
|
66
|
+
- **Backwards compatibility**: `camelCase` methods are aliased (e.g., `customer.customer_id` and `customer.customerId` both work)
|
|
67
|
+
|
|
68
|
+
## Resources
|
|
69
|
+
|
|
70
|
+
### Customer
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
# Create a customer (Legal Person - PJ)
|
|
74
|
+
customer = Conexa::Customer.create(
|
|
75
|
+
company_id: 3,
|
|
76
|
+
name: 'Empresa ABC Ltda',
|
|
77
|
+
trade_name: 'ABC',
|
|
78
|
+
cell_number: '11999998888',
|
|
79
|
+
has_login_access: false,
|
|
80
|
+
legal_person: {
|
|
81
|
+
cnpj: '99.557.155/0001-90',
|
|
82
|
+
foundation_date: '2020-06-12'
|
|
83
|
+
},
|
|
84
|
+
address: {
|
|
85
|
+
zip_code: '13058-111',
|
|
86
|
+
state: 'SP',
|
|
87
|
+
city: 'Campinas',
|
|
88
|
+
street: 'Rua Principal',
|
|
89
|
+
number: '100',
|
|
90
|
+
neighborhood: 'Centro'
|
|
91
|
+
},
|
|
92
|
+
phones: ['(11) 3333-4444'],
|
|
93
|
+
emails_message: ['contato@empresa.com'],
|
|
94
|
+
emails_financial_messages: ['financeiro@empresa.com']
|
|
95
|
+
)
|
|
96
|
+
puts customer.id # => 114
|
|
97
|
+
|
|
98
|
+
# Create a customer (Natural Person - PF)
|
|
99
|
+
customer = Conexa::Customer.create(
|
|
100
|
+
company_id: 3,
|
|
101
|
+
name: 'João Silva',
|
|
102
|
+
natural_person: {
|
|
103
|
+
cpf: '516.079.209-05',
|
|
104
|
+
birth_date: '1990-05-15',
|
|
105
|
+
profession: 'Developer'
|
|
106
|
+
},
|
|
107
|
+
has_login_access: true,
|
|
108
|
+
login: 'joao.silva',
|
|
109
|
+
password: 'SecurePass123!'
|
|
110
|
+
)
|
|
142
111
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
Conexa::
|
|
153
|
-
|
|
154
|
-
#
|
|
155
|
-
Conexa::
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
legal_name: "Acme Corp",
|
|
159
|
-
cnpj: "17.992.846/0001-58",
|
|
160
|
-
city: "São Paulo",
|
|
161
|
-
active: 1,
|
|
112
|
+
# Retrieve customer
|
|
113
|
+
customer = Conexa::Customer.retrieve(127)
|
|
114
|
+
customer.name # => "Empresa ABC Ltda"
|
|
115
|
+
customer.company_id # => 3
|
|
116
|
+
customer.is_active # => true
|
|
117
|
+
customer.address.city # => "Campinas"
|
|
118
|
+
customer.legal_person['cnpj'] # => "99.557.155/0001-90"
|
|
119
|
+
|
|
120
|
+
# Update customer
|
|
121
|
+
Conexa::Customer.update(127, name: 'New Name', cell_number: '11888887777')
|
|
122
|
+
|
|
123
|
+
# List customers with filters
|
|
124
|
+
customers = Conexa::Customer.list(
|
|
125
|
+
company_id: [3],
|
|
126
|
+
is_active: true,
|
|
162
127
|
page: 1,
|
|
163
|
-
size:
|
|
128
|
+
size: 20
|
|
164
129
|
)
|
|
165
|
-
|
|
166
|
-
# Filter bills by status
|
|
167
|
-
Conexa::Bill.all(status: "pending", page: 1, size: 20)
|
|
168
|
-
|
|
169
|
-
# Filter sales by date range
|
|
170
|
-
Conexa::Sale.all(page: 2, size: 6)
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
#### Resource-Specific Filters
|
|
174
|
-
|
|
175
|
-
| Resource | Common Filters |
|
|
176
|
-
|----------|---------------|
|
|
177
|
-
| Customer | `name`, `id[]`, `companyId[]` |
|
|
178
|
-
| Company | `id[]`, `tradeName`, `legalName`, `cnpj`, `city`, `active` |
|
|
179
|
-
| Contract | `companyId[]`, `active` |
|
|
180
|
-
| Bill | `status`, `companyId[]` |
|
|
181
|
-
| Plan | `id[]` |
|
|
182
|
-
| Sale | `date` |
|
|
183
|
-
| InvoicingMethod | `id[]`, `companyId[]`, `isActive`, `type` |
|
|
184
|
-
|
|
185
|
-
### Resource-Specific Operations (Sub-processes)
|
|
186
|
-
|
|
187
|
-
Some resources have additional operations beyond CRUD:
|
|
188
|
-
|
|
189
|
-
#### Charge Operations
|
|
190
|
-
|
|
191
|
-
```ruby
|
|
192
|
-
# Settle (pay) a charge
|
|
193
|
-
charge = Conexa::Charge.find(charge_id)
|
|
194
|
-
charge.settle(payment_params)
|
|
195
|
-
|
|
196
|
-
# Or directly by ID
|
|
197
|
-
Conexa::Charge.settle(charge_id, payment_params)
|
|
198
|
-
|
|
199
|
-
# Get PIX QR Code for a charge
|
|
200
|
-
pix_data = Conexa::Charge.pix(charge_id)
|
|
201
|
-
# Returns QR code data for payment
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
#### Contract Operations
|
|
205
|
-
|
|
206
|
-
```ruby
|
|
207
|
-
# End/terminate a contract
|
|
208
|
-
contract = Conexa::Contract.find(contract_id)
|
|
209
|
-
contract.end_contract(end_date: "2024-12-31", reason: "Customer request")
|
|
210
|
-
|
|
211
|
-
# Or directly by ID
|
|
212
|
-
Conexa::Contract.end_contract(contract_id, end_date: "2024-12-31")
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
#### Recurring Sale Operations
|
|
216
|
-
|
|
217
|
-
```ruby
|
|
218
|
-
# End a recurring sale
|
|
219
|
-
recurring_sale = Conexa::RecurringSale.find(recurring_sale_id)
|
|
220
|
-
recurring_sale.end_recurring_sale(end_date: "2024-12-31")
|
|
221
|
-
|
|
222
|
-
# Or directly by ID
|
|
223
|
-
Conexa::RecurringSale.end_recurring_sale(recurring_sale_id, end_date: "2024-12-31")
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
#### Invoicing Method
|
|
227
|
-
|
|
228
|
-
```ruby
|
|
229
|
-
# List invoicing methods with filters
|
|
230
|
-
Conexa::InvoicingMethod.all("companyId[]": [3], is_active: 1, type: "billet")
|
|
231
|
-
|
|
232
|
-
# Find by ID
|
|
233
|
-
Conexa::InvoicingMethod.find(invoicing_method_id)
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
#### Credit Card
|
|
237
|
-
|
|
238
|
-
```ruby
|
|
239
|
-
# Create a credit card for a customer
|
|
240
|
-
Conexa::CreditCard.new(
|
|
241
|
-
customer_id: customer_id,
|
|
242
|
-
card_number: "4111111111111111",
|
|
243
|
-
card_holder_name: "John Doe",
|
|
244
|
-
expiration_date: "12/25",
|
|
245
|
-
cvv: "123"
|
|
246
|
-
).create
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
#### Supplier
|
|
250
|
-
|
|
251
|
-
```ruby
|
|
252
|
-
# Create a supplier (legal person)
|
|
253
|
-
Conexa::Supplier.new(
|
|
254
|
-
company_id: company_id,
|
|
255
|
-
legal_name: "Supplier Corp",
|
|
256
|
-
trade_name: "Supplier",
|
|
257
|
-
cnpj: "12.345.678/0001-90"
|
|
258
|
-
).create
|
|
259
|
-
|
|
260
|
-
# Create a supplier (natural person)
|
|
261
|
-
Conexa::Supplier.new(
|
|
262
|
-
company_id: company_id,
|
|
263
|
-
name: "John Supplier",
|
|
264
|
-
cpf: "123.456.789-00"
|
|
265
|
-
).create
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Navigating Between Pages
|
|
269
|
-
|
|
270
|
-
```ruby
|
|
271
|
-
# Fetch first page
|
|
272
|
-
page1 = Conexa::Customer.all(page: 1, size: 10)
|
|
273
|
-
puts "Page 1: #{page1.data.count} customers"
|
|
274
|
-
|
|
275
|
-
# Fetch next page
|
|
276
|
-
page2 = Conexa::Customer.all(page: 2, size: 10)
|
|
277
|
-
puts "Page 2: #{page2.data.count} customers"
|
|
278
|
-
|
|
279
|
-
# Check pagination metadata
|
|
280
|
-
pagination = page1.pagination
|
|
281
|
-
# pagination contains total records, total pages, etc.
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
#### Iterating Through All Pages
|
|
285
|
-
|
|
286
|
-
```ruby
|
|
287
|
-
page = 1
|
|
288
|
-
size = 100
|
|
289
|
-
|
|
290
|
-
loop do
|
|
291
|
-
result = Conexa::Customer.all(page: page, size: size)
|
|
292
|
-
|
|
293
|
-
break if result.data.empty?
|
|
294
|
-
|
|
295
|
-
result.each do |customer|
|
|
296
|
-
# Process each customer
|
|
297
|
-
puts customer.name
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
page += 1
|
|
301
|
-
end
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Examples
|
|
305
|
-
|
|
306
|
-
#### Customers
|
|
307
|
-
|
|
308
|
-
##### Creating a Customer
|
|
309
|
-
|
|
310
|
-
```ruby
|
|
311
|
-
customer = Conexa::Customer.new(
|
|
312
|
-
name: 'John Doe',
|
|
313
|
-
email: 'john.doe@example.com',
|
|
314
|
-
phone: '555-1234'
|
|
315
|
-
).create
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
##### Retrieving a Customer
|
|
319
|
-
|
|
320
|
-
```ruby
|
|
321
|
-
customer = Conexa::Customer.find(customer_id)
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
##### Updating a Customer
|
|
325
|
-
|
|
326
|
-
```ruby
|
|
327
|
-
customer = Conexa::Customer.find(customer_id)
|
|
328
|
-
customer.email = 'new.email@example.com'
|
|
329
|
-
customer.save
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
##### Deleting a Customer
|
|
333
|
-
|
|
334
|
-
```ruby
|
|
335
|
-
Conexa::Customer.destroy(customer_id)
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
Or:
|
|
339
|
-
|
|
340
|
-
```ruby
|
|
341
|
-
customer = Conexa::Customer.find(customer_id)
|
|
342
|
-
customer.destroy
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
##### Listing Customers
|
|
346
|
-
|
|
347
|
-
```ruby
|
|
348
|
-
customers = Conexa::Customer.find({ name: 'John Doe' }, 1, 20)
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
#### Bills
|
|
352
|
-
|
|
353
|
-
##### Creating a Bill
|
|
354
|
-
|
|
355
|
-
```ruby
|
|
356
|
-
bill = Conexa::Bill.new(
|
|
357
|
-
customer_id: customer_id,
|
|
358
|
-
amount: 1000, # in cents
|
|
359
|
-
due_date: '2024-12-31'
|
|
360
|
-
).create
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
##### Retrieving a Bill
|
|
364
|
-
|
|
365
|
-
```ruby
|
|
366
|
-
bill = Conexa::Bill.find(bill_id)
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
##### Deleting a Bill
|
|
370
|
-
|
|
371
|
-
```ruby
|
|
372
|
-
Conexa::Bill.destroy(bill_id)
|
|
373
130
|
```
|
|
374
131
|
|
|
375
|
-
|
|
132
|
+
### Contract
|
|
376
133
|
|
|
377
134
|
```ruby
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
charge = Conexa::Charge.new(
|
|
387
|
-
customer_id: customer_id,
|
|
388
|
-
amount: 1000, # in cents
|
|
389
|
-
payment_method: 'credit_card',
|
|
390
|
-
card_number: '4111111111111111',
|
|
391
|
-
card_holder_name: 'John Doe',
|
|
392
|
-
card_expiration_date: '12/25',
|
|
393
|
-
card_cvv: '123'
|
|
394
|
-
).create
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
##### Retrieving a Charge
|
|
135
|
+
# Create a contract
|
|
136
|
+
contract = Conexa::Contract.create(
|
|
137
|
+
customer_id: 127,
|
|
138
|
+
plan_id: 5,
|
|
139
|
+
start_date: '2024-01-01',
|
|
140
|
+
payment_day: 10,
|
|
141
|
+
invoicing_method_id: 1
|
|
142
|
+
)
|
|
398
143
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
144
|
+
# Create contract with custom items
|
|
145
|
+
contract = Conexa::Contract.create_with_products(
|
|
146
|
+
customer_id: 127,
|
|
147
|
+
start_date: '2024-01-01',
|
|
148
|
+
payment_day: 10,
|
|
149
|
+
items: [
|
|
150
|
+
{ product_id: 101, quantity: 1, amount: 299.90 },
|
|
151
|
+
{ product_id: 102, quantity: 2, amount: 49.90 }
|
|
152
|
+
]
|
|
153
|
+
)
|
|
402
154
|
|
|
403
|
-
|
|
155
|
+
# Retrieve contract
|
|
156
|
+
contract = Conexa::Contract.retrieve(456)
|
|
404
157
|
|
|
405
|
-
|
|
406
|
-
Conexa::
|
|
158
|
+
# Cancel contract
|
|
159
|
+
Conexa::Contract.cancel(456, cancel_date: '2024-12-31')
|
|
407
160
|
```
|
|
408
161
|
|
|
409
|
-
|
|
162
|
+
### Sale (One-time)
|
|
410
163
|
|
|
411
164
|
```ruby
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
165
|
+
# Create a one-time sale
|
|
166
|
+
sale = Conexa::Sale.create(
|
|
167
|
+
customer_id: 450,
|
|
168
|
+
requester_id: 458,
|
|
169
|
+
product_id: 2521,
|
|
170
|
+
quantity: 1,
|
|
171
|
+
amount: 80.99,
|
|
172
|
+
reference_date: '2024-09-24T17:24:00-03:00',
|
|
173
|
+
notes: 'WhatsApp order'
|
|
174
|
+
)
|
|
175
|
+
puts sale.id # => 188481
|
|
176
|
+
|
|
177
|
+
# Retrieve sale
|
|
178
|
+
sale = Conexa::Sale.retrieve(188510)
|
|
179
|
+
sale.status # => "notBilled"
|
|
180
|
+
sale.amount # => 80.99
|
|
181
|
+
sale.discount_value # => 69.21
|
|
182
|
+
|
|
183
|
+
# List sales
|
|
184
|
+
sales = Conexa::Sale.list(
|
|
185
|
+
customer_id: [450, 216],
|
|
186
|
+
status: 'notBilled',
|
|
187
|
+
date_from: '2024-01-01',
|
|
188
|
+
date_to: '2024-12-31',
|
|
189
|
+
page: 1,
|
|
190
|
+
size: 20
|
|
191
|
+
)
|
|
416
192
|
|
|
417
|
-
|
|
193
|
+
# Update sale
|
|
194
|
+
Conexa::Sale.update(188510, quantity: 2, amount: 150.00)
|
|
418
195
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
name: 'Premium Plan',
|
|
422
|
-
amount: 4990, # in cents
|
|
423
|
-
billing_cycle: 'monthly'
|
|
424
|
-
).create
|
|
196
|
+
# Delete sale (only if not billed)
|
|
197
|
+
Conexa::Sale.delete(188510)
|
|
425
198
|
```
|
|
426
199
|
|
|
427
|
-
|
|
200
|
+
### Recurring Sale
|
|
428
201
|
|
|
429
202
|
```ruby
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
203
|
+
# Create recurring sale
|
|
204
|
+
recurring = Conexa::RecurringSale.create(
|
|
205
|
+
customer_id: 127,
|
|
206
|
+
product_id: 101,
|
|
207
|
+
quantity: 1,
|
|
208
|
+
start_date: '2024-01-01'
|
|
209
|
+
)
|
|
434
210
|
|
|
435
|
-
|
|
436
|
-
Conexa::
|
|
211
|
+
# List recurring sales for a contract
|
|
212
|
+
Conexa::RecurringSale.list(contract_id: 456)
|
|
437
213
|
```
|
|
438
214
|
|
|
439
|
-
|
|
215
|
+
### Charge
|
|
440
216
|
|
|
441
217
|
```ruby
|
|
442
|
-
|
|
443
|
-
|
|
218
|
+
# Retrieve charge
|
|
219
|
+
charge = Conexa::Charge.retrieve(789)
|
|
220
|
+
charge.status # => "paid"
|
|
221
|
+
charge.amount # => 299.90
|
|
222
|
+
charge.due_date # => "2024-02-10"
|
|
444
223
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
plan_id: plan_id,
|
|
453
|
-
start_date: '2024-01-01',
|
|
454
|
-
end_date: '2024-12-31'
|
|
455
|
-
).create
|
|
456
|
-
```
|
|
224
|
+
# List charges
|
|
225
|
+
charges = Conexa::Charge.list(
|
|
226
|
+
customer_id: [127],
|
|
227
|
+
status: 'pending',
|
|
228
|
+
due_date_from: '2024-01-01',
|
|
229
|
+
due_date_to: '2024-12-31'
|
|
230
|
+
)
|
|
457
231
|
|
|
458
|
-
|
|
232
|
+
# Cancel charge
|
|
233
|
+
Conexa::Charge.cancel(789)
|
|
459
234
|
|
|
460
|
-
|
|
461
|
-
|
|
235
|
+
# Send charge by email
|
|
236
|
+
Conexa::Charge.send_email(789)
|
|
462
237
|
```
|
|
463
238
|
|
|
464
|
-
|
|
239
|
+
### Bill (Invoice)
|
|
465
240
|
|
|
466
241
|
```ruby
|
|
467
|
-
|
|
468
|
-
|
|
242
|
+
# Retrieve bill
|
|
243
|
+
bill = Conexa::Bill.retrieve(101)
|
|
469
244
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
245
|
+
# List bills
|
|
246
|
+
bills = Conexa::Bill.list(
|
|
247
|
+
customer_id: [127],
|
|
248
|
+
page: 1,
|
|
249
|
+
size: 50
|
|
250
|
+
)
|
|
474
251
|
```
|
|
475
252
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
##### Creating a Product
|
|
253
|
+
### Plan
|
|
479
254
|
|
|
480
255
|
```ruby
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
description: 'Product Description',
|
|
484
|
-
price: 2990 # in cents
|
|
485
|
-
).create
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
##### Retrieving a Product
|
|
256
|
+
# List plans
|
|
257
|
+
plans = Conexa::Plan.list(company_id: [3])
|
|
489
258
|
|
|
490
|
-
|
|
491
|
-
|
|
259
|
+
# Retrieve plan
|
|
260
|
+
plan = Conexa::Plan.retrieve(5)
|
|
261
|
+
plan.name # => "Plano Básico"
|
|
262
|
+
plan.price # => 99.90
|
|
492
263
|
```
|
|
493
264
|
|
|
494
|
-
|
|
265
|
+
### Product
|
|
495
266
|
|
|
496
267
|
```ruby
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
##### Listing Products
|
|
268
|
+
# List products
|
|
269
|
+
products = Conexa::Product.list(company_id: [3])
|
|
501
270
|
|
|
502
|
-
|
|
503
|
-
|
|
271
|
+
# Retrieve product
|
|
272
|
+
product = Conexa::Product.retrieve(101)
|
|
504
273
|
```
|
|
505
274
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
##### Creating a Sale
|
|
275
|
+
### Credit Card
|
|
509
276
|
|
|
510
277
|
```ruby
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
278
|
+
# Add credit card to customer
|
|
279
|
+
card = Conexa::CreditCard.create(
|
|
280
|
+
customer_id: 127,
|
|
281
|
+
card_number: '4111111111111111',
|
|
282
|
+
cardholder_name: 'JOAO SILVA',
|
|
283
|
+
expiration_month: '12',
|
|
284
|
+
expiration_year: '2025',
|
|
285
|
+
cvv: '123'
|
|
286
|
+
)
|
|
518
287
|
|
|
519
|
-
|
|
288
|
+
# List customer's cards
|
|
289
|
+
cards = Conexa::CreditCard.list(customer_id: 127)
|
|
520
290
|
|
|
521
|
-
|
|
522
|
-
|
|
291
|
+
# Delete card
|
|
292
|
+
Conexa::CreditCard.delete(card_id)
|
|
523
293
|
```
|
|
524
294
|
|
|
525
|
-
|
|
295
|
+
### Company (Unit)
|
|
526
296
|
|
|
527
297
|
```ruby
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
##### Listing Sales
|
|
298
|
+
# List companies/units
|
|
299
|
+
companies = Conexa::Company.list
|
|
532
300
|
|
|
533
|
-
|
|
534
|
-
|
|
301
|
+
# Retrieve company
|
|
302
|
+
company = Conexa::Company.retrieve(3)
|
|
303
|
+
company.name # => "Matriz"
|
|
304
|
+
company.document # => "12.345.678/0001-90"
|
|
535
305
|
```
|
|
536
306
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
##### Creating a Recurring Sale
|
|
540
|
-
|
|
541
|
-
```ruby
|
|
542
|
-
recurring_sale = Conexa::RecurringSale.new(
|
|
543
|
-
customer_id: customer_id,
|
|
544
|
-
plan_id: plan_id,
|
|
545
|
-
start_date: '2024-01-01'
|
|
546
|
-
).create
|
|
547
|
-
```
|
|
307
|
+
## Pagination
|
|
548
308
|
|
|
549
|
-
|
|
309
|
+
All list endpoints return paginated results:
|
|
550
310
|
|
|
551
311
|
```ruby
|
|
552
|
-
|
|
553
|
-
```
|
|
312
|
+
result = Conexa::Customer.list(page: 1, size: 20)
|
|
554
313
|
|
|
555
|
-
|
|
314
|
+
result.data # Array of customers
|
|
315
|
+
result.pagination.current_page # => 1
|
|
316
|
+
result.pagination.total_pages # => 10
|
|
317
|
+
result.pagination.total_items # => 195
|
|
318
|
+
result.pagination.item_per_page # => 20
|
|
556
319
|
|
|
557
|
-
|
|
558
|
-
|
|
320
|
+
# Iterate through pages
|
|
321
|
+
loop do
|
|
322
|
+
result.data.each { |customer| process(customer) }
|
|
323
|
+
break if result.pagination.current_page >= result.pagination.total_pages
|
|
324
|
+
result = Conexa::Customer.list(page: result.pagination.current_page + 1)
|
|
325
|
+
end
|
|
559
326
|
```
|
|
560
327
|
|
|
561
|
-
|
|
328
|
+
## Error Handling
|
|
562
329
|
|
|
563
330
|
```ruby
|
|
564
|
-
|
|
331
|
+
begin
|
|
332
|
+
customer = Conexa::Customer.create(name: '')
|
|
333
|
+
rescue Conexa::ValidationError => e
|
|
334
|
+
# Field validation errors (400)
|
|
335
|
+
e.errors.each do |error|
|
|
336
|
+
puts "#{error['field']}: #{error['messages'].join(', ')}"
|
|
337
|
+
end
|
|
338
|
+
rescue Conexa::AuthenticationError => e
|
|
339
|
+
# Authentication required (401)
|
|
340
|
+
puts e.message
|
|
341
|
+
rescue Conexa::AuthorizationError => e
|
|
342
|
+
# Not authorized (403)
|
|
343
|
+
puts e.message
|
|
344
|
+
rescue Conexa::NotFoundError => e
|
|
345
|
+
# Resource not found (404)
|
|
346
|
+
puts e.message
|
|
347
|
+
rescue Conexa::UnprocessableError => e
|
|
348
|
+
# Business logic error (422)
|
|
349
|
+
e.errors.each do |error|
|
|
350
|
+
puts "#{error['code']}: #{error['message']}"
|
|
351
|
+
end
|
|
352
|
+
rescue Conexa::RateLimitError => e
|
|
353
|
+
# Too many requests (429)
|
|
354
|
+
puts "Rate limit exceeded. Retry after #{e.retry_after} seconds"
|
|
355
|
+
rescue Conexa::ApiError => e
|
|
356
|
+
# Generic API error
|
|
357
|
+
puts "Error #{e.status}: #{e.message}"
|
|
358
|
+
end
|
|
565
359
|
```
|
|
566
360
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
Each resource provides common methods:
|
|
361
|
+
## Rate Limiting
|
|
570
362
|
|
|
571
|
-
|
|
572
|
-
- `find(id)` - Retrieve a specific record by ID.
|
|
573
|
-
- `find(filter_hash, page, size)` - Retrieve records matching filters with pagination.
|
|
574
|
-
- `destroy(id)` - Delete a record by ID.
|
|
575
|
-
- `find(id).destroy` - Find and delete a record.
|
|
363
|
+
The Conexa API has a limit of **100 requests per minute**. Response headers include:
|
|
576
364
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
## Validating Webhooks
|
|
582
|
-
|
|
583
|
-
To ensure that all received webhooks are sent by Conexa, you should validate the signature provided in the HTTP header `X-Signature`. You can validate it using the raw payload and the signature.
|
|
584
|
-
|
|
585
|
-
```ruby
|
|
586
|
-
valid = Conexa::Webhook.valid_request_signature?(raw_payload, signature)
|
|
587
|
-
```
|
|
365
|
+
- `X-Rate-Limit-Limit`: Maximum requests in 60s
|
|
366
|
+
- `X-Rate-Limit-Remaining`: Remaining requests in 60s
|
|
367
|
+
- `X-Rate-Limit-Reset`: Seconds until reset
|
|
588
368
|
|
|
589
|
-
|
|
369
|
+
## Documentation
|
|
590
370
|
|
|
591
|
-
|
|
371
|
+
- [Conexa Website](https://conexa.app/)
|
|
372
|
+
- [API Documentation](https://conexa.app/api-docs)
|
|
373
|
+
- [Postman Collection](https://documenter.getpostman.com/view/25182821/2s93RZMpcB)
|
|
374
|
+
- [Discord Community](https://discord.gg/zW28sJh7Nz) - Conexa for Developers
|
|
375
|
+
- [REFERENCE.md](REFERENCE.md) - Complete API reference for LLMs/AI agents
|
|
592
376
|
|
|
593
|
-
|
|
594
|
-
class WebhooksController < ApplicationController
|
|
595
|
-
skip_before_action :verify_authenticity_token
|
|
596
|
-
|
|
597
|
-
def receive
|
|
598
|
-
if valid_webhook?
|
|
599
|
-
# Handle your code here
|
|
600
|
-
# Webhook payload is in params
|
|
601
|
-
else
|
|
602
|
-
render_invalid_webhook_response
|
|
603
|
-
end
|
|
604
|
-
end
|
|
377
|
+
## Development
|
|
605
378
|
|
|
606
|
-
|
|
379
|
+
```bash
|
|
380
|
+
# Install dependencies
|
|
381
|
+
bundle install
|
|
607
382
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
signature = request.headers['X-Signature']
|
|
611
|
-
Conexa::Webhook.valid_request_signature?(raw_payload, signature)
|
|
612
|
-
end
|
|
383
|
+
# Run tests
|
|
384
|
+
bundle exec rspec
|
|
613
385
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
end
|
|
617
|
-
end
|
|
386
|
+
# Run linter
|
|
387
|
+
bundle exec rubocop
|
|
618
388
|
```
|
|
619
389
|
|
|
620
|
-
##
|
|
621
|
-
|
|
622
|
-
This gem is stable but under continuous development. This README provides a quick overview of its main features.
|
|
623
|
-
|
|
624
|
-
You can explore the source code to see all [supported resources](lib/conexa/resources). We will continue to document and add support for all features listed in the [Conexa API Documentation](https://conexa.app/api-docs).
|
|
390
|
+
## Contributing
|
|
625
391
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
392
|
+
1. Fork the repository
|
|
393
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
394
|
+
3. Commit your changes (`git commit -am 'Add amazing feature'`)
|
|
395
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
396
|
+
5. Create a Pull Request
|
|
631
397
|
|
|
632
398
|
## License
|
|
633
399
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
## Disclaimer
|
|
400
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
637
401
|
|
|
638
|
-
|
|
402
|
+
## Changelog
|
|
639
403
|
|
|
640
|
-
|
|
404
|
+
See [CHANGELOG.md](CHANGELOG.md) for release history.
|