loyverse_api 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8617a119e948c3c8d187b2046710f71dd442eed9bbde9772cf8f21bdb00c6b3b
4
+ data.tar.gz: 503a06d272a520ff26e4cc37526f23dacd10df170a9b536e599b83f56f9f20d5
5
+ SHA512:
6
+ metadata.gz: fa8bda79302447775991c4e267882f59c8e61bc4eed36aa1fa6f925f870de05f0f303b02bd309688a7715e5308c3c3ae90893f88fb338c13e836ba1a8e693b44
7
+ data.tar.gz: 4518e72787d2aa4ac351a2739568d1ddab7d80f076e204b646216a0dac342835242b6b97f55a0d6c3b31d9dc5daf1b0731e8aca0d16d8bf96e59931894805425
data/.env.sample ADDED
@@ -0,0 +1 @@
1
+ LOYVERSE_ACCESS_TOKEN=lv_1a2b3c4d5e6f7g8h9i0j_example_token_replace_with_your_actual_access_token
data/.gitignore ADDED
@@ -0,0 +1,54 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ .env
15
+
16
+ # Ignore Byebug command history file.
17
+ .byebug_history
18
+
19
+ # Ignore RubyMine files
20
+ .idea/
21
+
22
+ # Ignore VS Code files
23
+ .vscode/
24
+
25
+ # Ignore bundler config.
26
+ /.bundle/
27
+
28
+ # Ignore the default SQLite database.
29
+ /db/*.sqlite3
30
+ /db/*.sqlite3-*
31
+
32
+ # Ignore all logfiles and tempfiles.
33
+ /log/*
34
+ /tmp/*
35
+ !/log/.keep
36
+ !/tmp/.keep
37
+
38
+ # Ignore uploaded files in development.
39
+ /storage/*
40
+ !/storage/.keep
41
+
42
+ .DS_Store
43
+
44
+ # VCR cassettes
45
+ /spec/fixtures/vcr_cassettes/
46
+
47
+ # Vendor directory for bundled gems
48
+ /vendor/
49
+
50
+ # RSpec status file
51
+ .rspec_status
52
+
53
+ # Gemfile.lock for gems
54
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2026-01-26
4
+
5
+ ### Added
6
+ - Initial release of the Loyverse API Ruby gem
7
+ - Comprehensive error handling
8
+ - Custom exception classes for different error types
9
+ - Automatic retry logic with exponential backoff
10
+ - Rate limiting support
11
+ - Automatic retries for rate limit errors
12
+ - Configurable timeout settings
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in loyverse_api.gemspec
4
+ gemspec
5
+
6
+ gem "dotenv", "~> 2.0"
7
+ gem "pry", "~> 0.14"
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Álvaro Delgado
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Loyverse API Wrapper
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,516 @@
1
+ # Loyverse API Ruby Gem
2
+
3
+ A comprehensive Ruby wrapper for the [Loyverse API](https://developer.loyverse.com/docs/), providing easy access to all Loyverse resources including items, inventory, receipts, categories, and webhooks.
4
+
5
+ ## Features
6
+
7
+ - **Complete API Coverage**: Support for all major Loyverse API resources
8
+ - **Authentication**: Personal Access Token and OAuth 2.0 support
9
+ - **Error Handling**: Comprehensive error handling with custom exception classes
10
+ - **Rate Limiting**: Automatic retry logic with exponential backoff
11
+ - **Type Safety**: Well-documented methods with clear parameter types
12
+ - **Webhook Support**: Signature verification for secure webhook handling
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'loyverse_api'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ ```bash
25
+ $ bundle install
26
+ ```
27
+
28
+ Or install it yourself as:
29
+
30
+ ```bash
31
+ $ gem install loyverse_api
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### Configuration
37
+
38
+ Configure the gem with your Personal Access Token:
39
+
40
+ ```ruby
41
+ require 'loyverse_api'
42
+
43
+ LoyverseApi.configure do |config|
44
+ config.access_token = 'your_access_token_here'
45
+ end
46
+
47
+ # Create a client instance
48
+ client = LoyverseApi.client
49
+ ```
50
+
51
+ Alternatively, you can create a client directly:
52
+
53
+ ```ruby
54
+ configuration = LoyverseApi::Configuration.new
55
+ configuration.access_token = 'your_access_token_here'
56
+
57
+ client = LoyverseApi::Client.new(configuration)
58
+ ```
59
+
60
+ ### Getting Your Access Token
61
+
62
+ 1. Login to [Loyverse Back Office](https://r.loyverse.com/dashboard)
63
+ 2. Navigate to 'Access Tokens' section
64
+ 3. Click '+ Add access token'
65
+ 4. Fill in name and optional expiration date
66
+ 5. Save and copy the token (shown only once)
67
+
68
+ ## Usage
69
+
70
+ ### Categories
71
+
72
+ <details>
73
+ <summary>Click to see Categories examples</summary>
74
+
75
+ ```ruby
76
+ # List categories
77
+ categories = client.list_categories
78
+
79
+ # Get a specific category
80
+ category = client.get_category('category-uuid')
81
+
82
+ # Create a category
83
+ new_category = client.create_category(
84
+ name: 'Beverages',
85
+ color: 'BLUE'
86
+ )
87
+
88
+ # Delete a category
89
+ client.delete_category('category-uuid')
90
+ ```
91
+
92
+ </details>
93
+
94
+ ### Items
95
+
96
+ <details>
97
+ <summary>Click to see Items examples</summary>
98
+
99
+ ```ruby
100
+ # List items
101
+ items = client.list_items(limit: 50)
102
+
103
+ # Get items updated after a specific date
104
+ recent_items = client.list_items(
105
+ updated_at_min: Time.now - (7 * 24 * 60 * 60)
106
+ )
107
+
108
+ # Get a specific item
109
+ item = client.get_item('item-uuid')
110
+
111
+ # Create an item with variants
112
+ new_item = client.create_item(
113
+ item_name: 'Coffee',
114
+ category_id: 'category-uuid',
115
+ track_stock: true,
116
+ variants: [
117
+ {
118
+ sku: 'COFFEE-S',
119
+ price: 3.50,
120
+ cost: 1.50
121
+ }
122
+ ]
123
+ )
124
+
125
+ # Update an item
126
+ client.update_item(
127
+ 'item-uuid',
128
+ item_name: 'Premium Coffee'
129
+ )
130
+
131
+ # Delete an item
132
+ client.delete_item('item-uuid')
133
+ ```
134
+
135
+ </details>
136
+
137
+ ### Inventory
138
+
139
+ <details>
140
+ <summary>Click to see Inventory examples</summary>
141
+
142
+ ```ruby
143
+ # List inventory levels
144
+ inventory = client.list_inventory
145
+
146
+ # Filter by variant
147
+ variant_inventory = client.list_inventory(variant_id: 'variant-uuid')
148
+
149
+ # Filter by store
150
+ store_inventory = client.list_inventory(store_id: 'store-uuid')
151
+
152
+ # Update inventory level
153
+ client.update_inventory(
154
+ variant_id: 'variant-uuid',
155
+ store_id: 'store-uuid',
156
+ in_stock: 150
157
+ )
158
+ ```
159
+
160
+ </details>
161
+
162
+ ### Receipts
163
+
164
+ <details>
165
+ <summary>Click to see Receipts examples</summary>
166
+
167
+ ```ruby
168
+ # List receipts
169
+ receipts = client.list_receipts(order: 'DESC')
170
+
171
+ # Filter by store
172
+ store_receipts = client.list_receipts(store_id: 'store-uuid')
173
+
174
+ # Filter by date range
175
+ receipts_by_date = client.list_receipts(
176
+ created_at_min: '2024-01-01T00:00:00Z',
177
+ created_at_max: '2024-01-31T23:59:59Z'
178
+ )
179
+
180
+ # Get a specific receipt
181
+ receipt = client.get_receipt('12345')
182
+
183
+ # Create a receipt
184
+ new_receipt = client.create_receipt(
185
+ receipt_date: Time.now,
186
+ store_id: 'store-uuid',
187
+ line_items: [
188
+ {
189
+ variant_id: 'variant-uuid',
190
+ quantity: 2,
191
+ price: 10.00
192
+ }
193
+ ],
194
+ payments: [
195
+ {
196
+ payment_type_id: 'payment-type-uuid',
197
+ money_amount: 20.00
198
+ }
199
+ ]
200
+ )
201
+
202
+ # Create a refund
203
+ refund = client.create_refund(
204
+ '12345',
205
+ refund_date: Time.now,
206
+ line_items: [...],
207
+ payments: [...]
208
+ )
209
+ ```
210
+
211
+ </details>
212
+
213
+ ### Customers
214
+
215
+ <details>
216
+ <summary>Click to see Customers examples</summary>
217
+
218
+ ```ruby
219
+ # List customers
220
+ customers = client.list_customers
221
+
222
+ # Filter by email
223
+ customer = client.list_customers(email: 'customer@example.com')
224
+
225
+ # Filter by phone
226
+ customer = client.list_customers(phone_number: '+1234567890')
227
+
228
+ # Get a specific customer
229
+ customer = client.get_customer('customer-uuid')
230
+
231
+ # Create a customer
232
+ new_customer = client.create_customer(
233
+ name: 'John Doe',
234
+ email: 'john@example.com',
235
+ phone_number: '+1234567890',
236
+ address: '123 Main St',
237
+ city: 'New York',
238
+ postal_code: '10001'
239
+ )
240
+
241
+ # Update a customer
242
+ client.update_customer(
243
+ 'customer-uuid',
244
+ name: 'Jane Doe',
245
+ email: 'jane@example.com'
246
+ )
247
+
248
+ # Delete a customer
249
+ client.delete_customer('customer-uuid')
250
+ ```
251
+
252
+ </details>
253
+
254
+ ### Discounts
255
+
256
+ <details>
257
+ <summary>Click to see Discounts examples</summary>
258
+
259
+ ```ruby
260
+ # List discounts
261
+ discounts = client.list_discounts
262
+
263
+ # Get a specific discount
264
+ discount = client.get_discount('discount-uuid')
265
+
266
+ # Create a percentage discount
267
+ percentage_discount = client.create_discount(
268
+ name: '10% Off',
269
+ type: LoyverseApi::Endpoints::Discounts::TYPE_FIXED_PERCENT,
270
+ discount_amount: 10.0
271
+ )
272
+
273
+ # Create a fixed amount discount
274
+ fixed_discount = client.create_discount(
275
+ name: '$5 Off',
276
+ type: LoyverseApi::Endpoints::Discounts::TYPE_FIXED_AMOUNT,
277
+ discount_amount: 5.0,
278
+ applies_to: LoyverseApi::Endpoints::Discounts::APPLIES_TO_RECEIPT
279
+ )
280
+
281
+ # Update a discount
282
+ client.update_discount(
283
+ 'discount-uuid',
284
+ name: '15% Off',
285
+ discount_amount: 15.0
286
+ )
287
+
288
+ # Delete a discount
289
+ client.delete_discount('discount-uuid')
290
+ ```
291
+
292
+ </details>
293
+
294
+ ### Employees
295
+
296
+ <details>
297
+ <summary>Click to see Employees examples</summary>
298
+
299
+ ```ruby
300
+ # List employees
301
+ employees = client.list_employees
302
+
303
+ # Get a specific employee
304
+ employee = client.get_employee('employee-uuid')
305
+ ```
306
+
307
+ **Note:** Employees can only be read via the API. Create, update, and delete operations must be done through the Loyverse Back Office.
308
+
309
+ </details>
310
+
311
+ ### Modifiers
312
+
313
+ <details>
314
+ <summary>Click to see Modifiers examples</summary>
315
+
316
+ ```ruby
317
+ # List modifiers
318
+ modifiers = client.list_modifiers
319
+
320
+ # Get a specific modifier
321
+ modifier = client.get_modifier('modifier-uuid')
322
+
323
+ # Create a modifier with options
324
+ new_modifier = client.create_modifier(
325
+ name: 'Size',
326
+ options: [
327
+ { name: 'Small', price: 0 },
328
+ { name: 'Medium', price: 1.0 },
329
+ { name: 'Large', price: 2.0 }
330
+ ]
331
+ )
332
+
333
+ # Delete a modifier
334
+ client.delete_modifier('modifier-uuid')
335
+ ```
336
+
337
+ </details>
338
+
339
+ ### Webhooks
340
+
341
+ Webhooks allow you to receive real-time notifications when events occur in your Loyverse account.
342
+
343
+ **Setting up a webhook:**
344
+
345
+ 1. **Create the webhook** pointing to your server endpoint:
346
+ ```ruby
347
+ webhook = client.create_webhook(
348
+ url: 'https://your-server.com/webhooks/loyverse',
349
+ event_types: ['ORDER_CREATED', 'ITEM_UPDATED', 'INVENTORY_UPDATED'],
350
+ description: 'Production webhook'
351
+ )
352
+ ```
353
+
354
+ 2. **Handle webhook requests** in your application (Rails example):
355
+ ```ruby
356
+ # POST /webhooks/loyverse
357
+ def create
358
+ payload = request.raw_post
359
+ signature = request.headers['X-Loyverse-Signature']
360
+
361
+ # Verify signature
362
+ unless client.verify_webhook_signature(payload, signature, ENV['LOYVERSE_WEBHOOK_SECRET'])
363
+ return head :unauthorized
364
+ end
365
+
366
+ # Process the webhook
367
+ data = JSON.parse(payload)
368
+ case data['event_type']
369
+ when 'ORDER_CREATED'
370
+ # Handle new order
371
+ when 'ITEM_UPDATED'
372
+ # Handle item update
373
+ when 'INVENTORY_UPDATED'
374
+ # Handle inventory update
375
+ end
376
+
377
+ head :ok
378
+ end
379
+ ```
380
+
381
+ **Available event types:**
382
+ - `ORDER_CREATED` - New receipts/sales
383
+ - `ITEM_UPDATED` - Product changes
384
+ - `INVENTORY_UPDATED` - Stock level changes
385
+
386
+ 📖 **Full webhook documentation:** [Loyverse Webhooks Overview](https://developer.loyverse.com/docs/#section/Webhooks-overview/Adding-webhook)
387
+
388
+ <details>
389
+ <summary>Click to see more webhook management examples</summary>
390
+
391
+ ```ruby
392
+ # List webhooks
393
+ webhooks = client.list_webhooks
394
+
395
+ # Get a specific webhook
396
+ webhook = client.get_webhook('webhook-uuid')
397
+
398
+ # Delete a webhook
399
+ client.delete_webhook('webhook-uuid')
400
+ ```
401
+
402
+ </details>
403
+
404
+ ### Pagination
405
+
406
+ ```ruby
407
+ # Get first page
408
+ page = client.list_items(limit: 100)
409
+
410
+ # Get next page using cursor from response
411
+ next_page = client.list_items(limit: 100, cursor: page['cursor'])
412
+ ```
413
+
414
+ ### Error Handling
415
+
416
+ The gem provides specific exception classes for different error types:
417
+
418
+ ```ruby
419
+ begin
420
+ item = client.get_item('invalid-uuid')
421
+ rescue LoyverseApi::NotFoundError => e
422
+ puts "Item not found: #{e.message}"
423
+ rescue LoyverseApi::AuthenticationError => e
424
+ puts "Authentication failed: #{e.message}"
425
+ rescue LoyverseApi::RateLimitError => e
426
+ puts "Rate limit exceeded: #{e.message}"
427
+ rescue LoyverseApi::BadRequestError => e
428
+ puts "Bad request: #{e.message}"
429
+ puts "Error code: #{e.code}"
430
+ puts "Error details: #{e.details}"
431
+ rescue LoyverseApi::Error => e
432
+ puts "API error: #{e.message}"
433
+ end
434
+ ```
435
+
436
+ ### Date and Time Handling
437
+
438
+ All dates in the Loyverse API use ISO 8601 format. The gem automatically handles Time objects:
439
+
440
+ ```ruby
441
+ # Using Time objects (recommended)
442
+ items = client.items.list(
443
+ updated_at_min: Time.now - (7 * 24 * 60 * 60), # 7 days ago
444
+ updated_at_max: Time.now
445
+ )
446
+
447
+ # Using ISO 8601 strings
448
+ items = client.items.list(
449
+ updated_at_min: '2024-01-15T00:00:00Z',
450
+ updated_at_max: '2024-01-22T23:59:59Z'
451
+ )
452
+ ```
453
+
454
+ ## Configuration Options
455
+
456
+ ```ruby
457
+ LoyverseApi.configure do |config|
458
+ # Required: Your Personal Access Token
459
+ config.access_token = 'your_token'
460
+
461
+ # Optional: API base URL (default: 'https://api.loyverse.com')
462
+ config.api_base_url = 'https://api.loyverse.com'
463
+
464
+ # Optional: API version (default: 'v1.0')
465
+ config.api_version = 'v1.0'
466
+
467
+ # Optional: Request timeout in seconds (default: 30)
468
+ config.timeout = 30
469
+
470
+ # Optional: Connection open timeout in seconds (default: 10)
471
+ config.open_timeout = 10
472
+ end
473
+ ```
474
+
475
+ ## Rate Limiting
476
+
477
+ The Loyverse API has the following rate limits:
478
+
479
+ - **Standard**: 60 requests per minute
480
+ - **Alternative**: 300 requests per 300 seconds
481
+
482
+ The gem automatically handles rate limiting with:
483
+
484
+ - Automatic retry with exponential backoff
485
+ - Maximum 3 retry attempts
486
+ - Retries for 429, 500, 502, 503, 504 status codes
487
+
488
+ ## Available Event Types for Webhooks
489
+
490
+ - `ORDER_CREATED` - New receipts/sales
491
+ - `ITEM_UPDATED` - Product changes
492
+ - `INVENTORY_UPDATED` - Stock level changes
493
+
494
+ ## Development
495
+
496
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests.
497
+
498
+ ## Contributing
499
+
500
+ Bug reports and pull requests are welcome on GitHub.
501
+
502
+ ## License
503
+
504
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
505
+
506
+ ## Resources
507
+
508
+ - [Loyverse API Documentation](https://developer.loyverse.com/docs/)
509
+ - [Loyverse Developer Portal](https://developer.loyverse.com)
510
+ - [Loyverse Back Office](https://r.loyverse.com/dashboard)
511
+
512
+ ## Support
513
+
514
+ For issues related to the gem, please open an issue on GitHub.
515
+
516
+ For Loyverse API support, please contact [Loyverse Support](https://loyverse.com/support).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec