licensechain_ruby_sdk 1.0.1

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.
data/README.md ADDED
@@ -0,0 +1,492 @@
1
+ # LicenseChain Ruby SDK
2
+
3
+ [![License](https://img.shields.io/badge/license-Elastic--2.0-blue.svg)](LICENSE)
4
+ [![Ruby](https://img.shields.io/badge/Ruby-3.0+-red.svg)](https://www.ruby-lang.org/)
5
+ [![Gem](https://img.shields.io/gem/v/licensechain-sdk)](https://rubygems.org/gems/licensechain-sdk)
6
+ [![Downloads](https://img.shields.io/gem/dt/licensechain-sdk)](https://rubygems.org/gems/licensechain-sdk)
7
+
8
+ Official Ruby SDK for LicenseChain - Secure license management for Ruby applications.
9
+
10
+ ## šŸš€ Features
11
+
12
+ - **šŸ” Secure Authentication** - User registration, login, and session management
13
+ - **šŸ“œ License Management** - Create, validate, update, and revoke licenses
14
+ - **šŸ›”ļø Hardware ID Validation** - Prevent license sharing and unauthorized access
15
+ - **šŸ”” Webhook Support** - Real-time license events and notifications
16
+ - **šŸ“Š Analytics Integration** - Track license usage and performance metrics
17
+ - **⚔ High Performance** - Optimized for production workloads
18
+ - **šŸ”„ Async Operations** - Non-blocking HTTP requests and data processing
19
+ - **šŸ› ļø Easy Integration** - Simple API with comprehensive documentation
20
+
21
+ ## šŸ“¦ Installation
22
+
23
+ ### Method 1: RubyGems (Recommended)
24
+
25
+ ```bash
26
+ # Install via gem
27
+ gem install licensechain-sdk
28
+
29
+ # Or add to Gemfile
30
+ gem 'licensechain-sdk', '~> 1.0'
31
+ ```
32
+
33
+ ### Method 2: Bundler
34
+
35
+ ```bash
36
+ # Add to Gemfile
37
+ gem 'licensechain-sdk', '~> 1.0'
38
+
39
+ # Install dependencies
40
+ bundle install
41
+ ```
42
+
43
+ ### Method 3: Manual Installation
44
+
45
+ 1. Download the latest release from [GitHub Releases](https://github.com/LicenseChain/LicenseChain-Ruby-SDK/releases)
46
+ 2. Extract to your project directory
47
+ 3. Install dependencies
48
+
49
+ ## šŸš€ Quick Start
50
+
51
+ ### Basic Setup
52
+
53
+ ```ruby
54
+ require 'licensechain-sdk'
55
+
56
+ # Initialize the client
57
+ client = LicenseChain::Client.new(
58
+ api_key: 'your-api-key',
59
+ app_name: 'your-app-name',
60
+ version: '1.0.0'
61
+ )
62
+
63
+ # Connect to LicenseChain
64
+ begin
65
+ client.connect
66
+ puts "Connected to LicenseChain successfully!"
67
+ rescue => e
68
+ puts "Failed to connect: #{e.message}"
69
+ end
70
+ ```
71
+
72
+ ### User Authentication
73
+
74
+ ```ruby
75
+ # Register a new user
76
+ begin
77
+ user = client.register('username', 'password', 'email@example.com')
78
+ puts "User registered successfully!"
79
+ puts "User ID: #{user.id}"
80
+ rescue => e
81
+ puts "Registration failed: #{e.message}"
82
+ end
83
+
84
+ # Login existing user
85
+ begin
86
+ user = client.login('username', 'password')
87
+ puts "User logged in successfully!"
88
+ puts "Session ID: #{user.session_id}"
89
+ rescue => e
90
+ puts "Login failed: #{e.message}"
91
+ end
92
+ ```
93
+
94
+ ### License Management
95
+
96
+ ```ruby
97
+ # Validate a license
98
+ begin
99
+ license = client.validate_license('LICENSE-KEY-HERE')
100
+ puts "License is valid!"
101
+ puts "License Key: #{license.key}"
102
+ puts "Status: #{license.status}"
103
+ puts "Expires: #{license.expires}"
104
+ puts "Features: #{license.features.join(', ')}"
105
+ puts "User: #{license.user}"
106
+ rescue => e
107
+ puts "License validation failed: #{e.message}"
108
+ end
109
+
110
+ # Get user's licenses
111
+ begin
112
+ licenses = client.get_user_licenses
113
+ puts "Found #{licenses.length} licenses:"
114
+ licenses.each_with_index do |license, index|
115
+ puts " #{index + 1}. #{license.key} - #{license.status} (Expires: #{license.expires})"
116
+ end
117
+ rescue => e
118
+ puts "Failed to get licenses: #{e.message}"
119
+ end
120
+ ```
121
+
122
+ ### Hardware ID Validation
123
+
124
+ ```ruby
125
+ # Get hardware ID (automatically generated)
126
+ hardware_id = client.get_hardware_id
127
+ puts "Hardware ID: #{hardware_id}"
128
+
129
+ # Validate hardware ID with license
130
+ begin
131
+ is_valid = client.validate_hardware_id('LICENSE-KEY-HERE', hardware_id)
132
+ if is_valid
133
+ puts "Hardware ID is valid for this license!"
134
+ else
135
+ puts "Hardware ID is not valid for this license."
136
+ end
137
+ rescue => e
138
+ puts "Hardware ID validation failed: #{e.message}"
139
+ end
140
+ ```
141
+
142
+ ### Webhook Integration
143
+
144
+ ```ruby
145
+ # Set up webhook handler
146
+ client.set_webhook_handler do |event, data|
147
+ puts "Webhook received: #{event}"
148
+
149
+ case event
150
+ when 'license.created'
151
+ puts "New license created: #{data['licenseKey']}"
152
+ when 'license.updated'
153
+ puts "License updated: #{data['licenseKey']}"
154
+ when 'license.revoked'
155
+ puts "License revoked: #{data['licenseKey']}"
156
+ end
157
+ end
158
+
159
+ # Start webhook listener
160
+ client.start_webhook_listener
161
+ ```
162
+
163
+ ## šŸ“š API Endpoints
164
+
165
+ Use the canonical API base URL `https://api.licensechain.app/v1`. The SDK also accepts the root host and normalizes requests to the same API version.
166
+
167
+ ### Base URL
168
+ - **Production**: `https://api.licensechain.app/v1`
169
+ - **Development**: `https://api.licensechain.app/v1`
170
+
171
+ ### Available Endpoints
172
+
173
+ | Method | Endpoint | Description |
174
+ |--------|----------|-------------|
175
+ | `GET` | `/v1/health` | Health check |
176
+ | `POST` | `/v1/auth/login` | User login |
177
+ | `POST` | `/v1/auth/register` | User registration |
178
+ | `GET` | `/v1/apps` | List applications |
179
+ | `POST` | `/v1/apps` | Create application |
180
+ | `GET` | `/v1/licenses` | List licenses |
181
+ | `POST` | `/v1/licenses/verify` | Verify license |
182
+ | `GET` | `/v1/webhooks` | List webhooks |
183
+ | `POST` | `/v1/webhooks` | Create webhook |
184
+ | `GET` | `/v1/analytics` | Get analytics |
185
+
186
+ **Note**: The SDK accepts either the root host or the canonical `/v1` base and normalizes endpoint requests automatically.
187
+
188
+ ## šŸ“š API Reference
189
+
190
+ ### LicenseChain::Client
191
+
192
+ #### Constructor
193
+
194
+ ```ruby
195
+ client = LicenseChain::Client.new(
196
+ api_key: 'your-api-key',
197
+ app_name: 'your-app-name',
198
+ version: '1.0.0',
199
+ base_url: 'https://api.licensechain.app/v1' # Optional
200
+ )
201
+ ```
202
+
203
+ #### Methods
204
+
205
+ ##### Connection Management
206
+
207
+ ```ruby
208
+ # Connect to LicenseChain
209
+ client.connect
210
+
211
+ # Disconnect from LicenseChain
212
+ client.disconnect
213
+
214
+ # Check connection status
215
+ is_connected = client.connected?
216
+ ```
217
+
218
+ ##### User Authentication
219
+
220
+ ```ruby
221
+ # Register a new user
222
+ user = client.register(username, password, email)
223
+
224
+ # Login existing user
225
+ user = client.login(username, password)
226
+
227
+ # Logout current user
228
+ client.logout
229
+
230
+ # Get current user info
231
+ user = client.get_current_user
232
+ ```
233
+
234
+ ##### License Management
235
+
236
+ ```ruby
237
+ # Validate a license
238
+ license = client.validate_license(license_key)
239
+
240
+ # Get user's licenses
241
+ licenses = client.get_user_licenses
242
+
243
+ # Create a new license
244
+ license = client.create_license(user_id, features, expires)
245
+
246
+ # Update a license
247
+ license = client.update_license(license_key, updates)
248
+
249
+ # Revoke a license
250
+ client.revoke_license(license_key)
251
+
252
+ # Extend a license
253
+ license = client.extend_license(license_key, days)
254
+ ```
255
+
256
+ ##### Hardware ID Management
257
+
258
+ ```ruby
259
+ # Get hardware ID
260
+ hardware_id = client.get_hardware_id
261
+
262
+ # Validate hardware ID
263
+ is_valid = client.validate_hardware_id(license_key, hardware_id)
264
+
265
+ # Bind hardware ID to license
266
+ client.bind_hardware_id(license_key, hardware_id)
267
+ ```
268
+
269
+ ##### Webhook Management
270
+
271
+ ```ruby
272
+ # Set webhook handler
273
+ client.set_webhook_handler(handler)
274
+
275
+ # Start webhook listener
276
+ client.start_webhook_listener
277
+
278
+ # Stop webhook listener
279
+ client.stop_webhook_listener
280
+ ```
281
+
282
+ ##### Analytics
283
+
284
+ ```ruby
285
+ # Track event
286
+ client.track_event(event_name, properties)
287
+
288
+ # Get analytics data
289
+ analytics = client.get_analytics(time_range)
290
+ ```
291
+
292
+ ## šŸ”§ Configuration
293
+
294
+ ### Environment Variables
295
+
296
+ Set these in your environment or through your build process:
297
+
298
+ ```bash
299
+ # Required
300
+ export LICENSECHAIN_API_KEY=your-api-key
301
+ export LICENSECHAIN_APP_NAME=your-app-name
302
+ export LICENSECHAIN_APP_VERSION=1.0.0
303
+
304
+ # Optional
305
+ export LICENSECHAIN_BASE_URL=https://api.licensechain.app/v1
306
+ export LICENSECHAIN_DEBUG=true
307
+ ```
308
+
309
+ ### Advanced Configuration
310
+
311
+ ```ruby
312
+ client = LicenseChain::Client.new(
313
+ api_key: 'your-api-key',
314
+ app_name: 'your-app-name',
315
+ version: '1.0.0',
316
+ base_url: 'https://api.licensechain.app/v1',
317
+ timeout: 30, # Request timeout in seconds
318
+ retries: 3, # Number of retry attempts
319
+ debug: false, # Enable debug logging
320
+ user_agent: 'MyApp/1.0.0' # Custom user agent
321
+ )
322
+ ```
323
+
324
+ ## šŸ›”ļø Security Features
325
+
326
+ ### Hardware ID Protection
327
+
328
+ The SDK automatically generates and manages hardware IDs to prevent license sharing:
329
+
330
+ ```ruby
331
+ # Hardware ID is automatically generated and stored
332
+ hardware_id = client.get_hardware_id
333
+
334
+ # Validate against license
335
+ is_valid = client.validate_hardware_id(license_key, hardware_id)
336
+ ```
337
+
338
+ ### Secure Communication
339
+
340
+ - All API requests use HTTPS
341
+ - API keys are securely stored and transmitted
342
+ - Session tokens are automatically managed
343
+ - Webhook signatures are verified
344
+
345
+ ### License Validation
346
+
347
+ - Real-time license validation
348
+ - Hardware ID binding
349
+ - Expiration checking
350
+ - Feature-based access control
351
+
352
+ ## šŸ“Š Analytics and Monitoring
353
+
354
+ ### Event Tracking
355
+
356
+ ```ruby
357
+ # Track custom events
358
+ client.track_event('app.started', {
359
+ level: 1,
360
+ playerCount: 10
361
+ })
362
+
363
+ # Track license events
364
+ client.track_event('license.validated', {
365
+ licenseKey: 'LICENSE-KEY',
366
+ features: 'premium,unlimited'
367
+ })
368
+ ```
369
+
370
+ ### Performance Monitoring
371
+
372
+ ```ruby
373
+ # Get performance metrics
374
+ metrics = client.get_performance_metrics
375
+ puts "API Response Time: #{metrics.average_response_time}ms"
376
+ puts "Success Rate: #{(metrics.success_rate * 100).round(2)}%"
377
+ puts "Error Count: #{metrics.error_count}"
378
+ ```
379
+
380
+ ## šŸ”„ Error Handling
381
+
382
+ ### Custom Exception Types
383
+
384
+ ```ruby
385
+ begin
386
+ license = client.validate_license('invalid-key')
387
+ rescue LicenseChain::InvalidLicenseError
388
+ puts "License key is invalid"
389
+ rescue LicenseChain::ExpiredLicenseError
390
+ puts "License has expired"
391
+ rescue LicenseChain::NetworkError => e
392
+ puts "Network connection failed: #{e.message}"
393
+ rescue LicenseChain::LicenseChainError => e
394
+ puts "LicenseChain error: #{e.message}"
395
+ end
396
+ ```
397
+
398
+ ### Retry Logic
399
+
400
+ ```ruby
401
+ # Automatic retry for network errors
402
+ client = LicenseChain::Client.new(
403
+ api_key: 'your-api-key',
404
+ app_name: 'your-app-name',
405
+ version: '1.0.0',
406
+ retries: 3, # Retry up to 3 times
407
+ timeout: 30 # Wait 30 seconds for each request
408
+ )
409
+ ```
410
+
411
+ ## 🧪 Testing
412
+
413
+ ### Unit Tests
414
+
415
+ ```bash
416
+ # Run tests
417
+ rspec
418
+
419
+ # Run tests with coverage
420
+ rspec --format documentation
421
+
422
+ # Run specific test
423
+ rspec spec/client_spec.rb
424
+ ```
425
+
426
+ ### Integration Tests
427
+
428
+ ```bash
429
+ # Test with real API
430
+ rspec spec/integration/
431
+ ```
432
+
433
+ ## License assertion JWT (RS256 + JWKS)
434
+
435
+ When Core API returns **`license_token`** and **`license_jwks_uri`**, verify offline with **`LicenseChain::LicenseAssertion.verify_license_assertion_jwt`** (`jwt` gem + JWKS fetch; claim **`token_use`** = **`licensechain_license_v1`**). A minimal **JWKS-only** CLI (no prior `verify` call in-process) is **[`examples/jwks_only.rb`](examples/jwks_only.rb)** — set **`LICENSECHAIN_LICENSE_TOKEN`** and **`LICENSECHAIN_LICENSE_JWKS_URI`** (optional **`LICENSECHAIN_EXPECTED_APP_ID`**), then `ruby examples/jwks_only.rb` (same env names as Go/Rust/PHP; [JWKS_EXAMPLE_PRIORITY](https://docs.licensechain.app/)).
436
+
437
+ ## šŸ“ Examples
438
+
439
+ See the `examples/` directory for complete examples:
440
+
441
+ - `basic_usage.rb` - Basic SDK usage
442
+ - `advanced_features.rb` - Advanced features and configuration
443
+ - `webhook_integration.rb` - Webhook handling
444
+ - `jwks_only.rb` — RS256 `license_token` via JWKS only ([JWKS_EXAMPLE_PRIORITY](https://docs.licensechain.app/))
445
+
446
+ ## šŸ¤ Contributing
447
+
448
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
449
+
450
+ ### Development Setup
451
+
452
+ 1. Clone the repository
453
+ 2. Install Ruby 3.0 or later
454
+ 3. Install dependencies: `bundle install`
455
+ 4. Build: `gem build licensechain-sdk.gemspec`
456
+ 5. Test: `rspec`
457
+
458
+ ## šŸ“„ License
459
+
460
+ This project is licensed under the Elastic License 2.0 (ELv2) — see the [LICENSE](LICENSE) file for details.
461
+
462
+ ## šŸ†˜ Support
463
+
464
+ - **Documentation**: [https://docs.licensechain.app/ruby](https://docs.licensechain.app/ruby)
465
+ - **Issues**: [GitHub Issues](https://github.com/LicenseChain/LicenseChain-Ruby-SDK/issues)
466
+ - **Discord**: [LicenseChain Discord](https://discord.gg/licensechain)
467
+ - **Email**: support@licensechain.app
468
+
469
+ ## šŸ”— Related Projects
470
+
471
+ - [LicenseChain JavaScript SDK](https://github.com/LicenseChain/LicenseChain-JavaScript-SDK)
472
+ - [LicenseChain Python SDK](https://github.com/LicenseChain/LicenseChain-Python-SDK)
473
+ - [LicenseChain Node.js SDK](https://github.com/LicenseChain/LicenseChain-NodeJS-SDK)
474
+ ---
475
+
476
+ **Made with ā¤ļø for the Ruby community**
477
+
478
+ ## LicenseChain API (v1)
479
+
480
+ This SDK targets the **LicenseChain HTTP API v1** implemented by the LicenseChain API service.
481
+
482
+ - **Production base URL:** https://api.licensechain.app/v1
483
+ - **API reference:** [docs.licensechain.app](https://docs.licensechain.app/)
484
+ - **Baseline REST mapping (documented for integrators):**
485
+ - GET /health
486
+ - POST /auth/register
487
+ - POST /licenses/verify
488
+ - PATCH /licenses/:id/revoke
489
+ - PATCH /licenses/:id/activate
490
+ - PATCH /licenses/:id/extend
491
+ - GET /analytics/stats
492
+
data/ROADMAP.md ADDED
@@ -0,0 +1,13 @@
1
+ # LicenseChain Ruby SDK Roadmap
2
+
3
+ ## Current Focus
4
+
5
+ - Keep default API base aligned with `https://api.licensechain.app/v1`.
6
+ - Maintain `verify_with_details` and JWT/JWKS helper compatibility.
7
+ - Preserve contract parity for verification responses and license assertions.
8
+
9
+ ## Next Operational Tasks
10
+
11
+ - Re-run conformance after Core API contract updates.
12
+ - Keep docs links and examples synchronized with canonical docs.
13
+ - Record release-impacting changes in `CHANGELOG.md`.
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/licensechain_ruby_sdk'
4
+
5
+ # Configure the SDK
6
+ LicenseChainRubySdk.configure do |config|
7
+ config.api_key = 'your-api-key-here'
8
+ config.base_url = 'https://api.licensechain.app/v1'
9
+ config.timeout = 30
10
+ config.retries = 3
11
+ end
12
+
13
+ # Initialize the client
14
+ client = LicenseChainRubySdk.client
15
+
16
+ puts "šŸš€ LicenseChain Ruby SDK - Basic Usage Example\n"
17
+
18
+ begin
19
+ # 1. License Management
20
+ puts "šŸ”‘ License Management:"
21
+
22
+ # Create a license
23
+ metadata = {
24
+ 'platform' => 'ruby',
25
+ 'version' => '1.0.0',
26
+ 'features' => ['validation', 'webhooks']
27
+ }
28
+
29
+ license = client.licenses.create('user123', 'product456', metadata)
30
+ puts "āœ… License created: #{license.id}"
31
+ puts " License Key: #{license.license_key}"
32
+ puts " Status: #{license.status}"
33
+
34
+ # Validate a license
35
+ license_key = LicenseChain::Utils.generate_license_key
36
+ puts "\nšŸ” Validating license key: #{license_key}"
37
+
38
+ is_valid = client.licenses.validate(license_key)
39
+ if is_valid
40
+ puts "āœ… License is valid"
41
+ else
42
+ puts "āŒ License is invalid"
43
+ end
44
+
45
+ # Get license stats
46
+ stats = client.licenses.stats
47
+ puts "\nšŸ“Š License Statistics:"
48
+ puts " Total: #{stats.total}"
49
+ puts " Active: #{stats.active}"
50
+ puts " Expired: #{stats.expired}"
51
+ puts " Revenue: $#{stats.revenue}"
52
+
53
+ # 2. User Management
54
+ puts "\nšŸ‘¤ User Management:"
55
+
56
+ # Create a user
57
+ user_metadata = {
58
+ 'source' => 'ruby-sdk',
59
+ 'plan' => 'premium'
60
+ }
61
+
62
+ user = client.users.create('user@example.com', 'John Doe', user_metadata)
63
+ puts "āœ… User created: #{user.id}"
64
+ puts " Email: #{user.email}"
65
+ puts " Name: #{user.name}"
66
+
67
+ # Get user stats
68
+ user_stats = client.users.stats
69
+ puts "\nšŸ“Š User Statistics:"
70
+ puts " Total: #{user_stats.total}"
71
+ puts " Active: #{user_stats.active}"
72
+ puts " Inactive: #{user_stats.inactive}"
73
+
74
+ # 3. Product Management
75
+ puts "\nšŸ“¦ Product Management:"
76
+
77
+ # Create a product
78
+ product_metadata = {
79
+ 'category' => 'software',
80
+ 'tags' => ['premium', 'enterprise']
81
+ }
82
+
83
+ product = client.products.create(
84
+ 'My Software Product',
85
+ 'A great software product',
86
+ 99.99,
87
+ 'USD',
88
+ product_metadata
89
+ )
90
+ puts "āœ… Product created: #{product.id}"
91
+ puts " Name: #{product.name}"
92
+ puts " Price: $#{product.price} #{product.currency}"
93
+
94
+ # Get product stats
95
+ product_stats = client.products.stats
96
+ puts "\nšŸ“Š Product Statistics:"
97
+ puts " Total: #{product_stats.total}"
98
+ puts " Active: #{product_stats.active}"
99
+ puts " Revenue: $#{product_stats.revenue}"
100
+
101
+ # 4. Webhook Management
102
+ puts "\nšŸ”— Webhook Management:"
103
+
104
+ # Create a webhook
105
+ events = [
106
+ LicenseChain::WebhookEvents::LICENSE_CREATED,
107
+ LicenseChain::WebhookEvents::LICENSE_UPDATED,
108
+ LicenseChain::WebhookEvents::USER_CREATED
109
+ ]
110
+
111
+ webhook = client.webhooks.create('https://example.com/webhook', events, 'webhook-secret')
112
+ puts "āœ… Webhook created: #{webhook.id}"
113
+ puts " URL: #{webhook.url}"
114
+ puts " Events: #{webhook.events.join(', ')}"
115
+
116
+ # 5. Webhook Processing
117
+ puts "\nšŸ”„ Webhook Processing:"
118
+
119
+ webhook_handler = LicenseChain::WebhookHandler.new('webhook-secret')
120
+
121
+ # Simulate a webhook event
122
+ webhook_event = {
123
+ id: 'evt_123',
124
+ type: LicenseChain::WebhookEvents::LICENSE_CREATED,
125
+ data: {
126
+ id: 'lic_123',
127
+ user_id: 'user_123',
128
+ product_id: 'prod_123',
129
+ license_key: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345',
130
+ status: 'active',
131
+ created_at: '2023-01-01T00:00:00Z'
132
+ },
133
+ timestamp: '2023-01-01T00:00:00Z',
134
+ signature: 'signature_here'
135
+ }
136
+
137
+ webhook_handler.process_event(webhook_event)
138
+ puts "āœ… Webhook event processed successfully"
139
+
140
+ # 6. Utility Functions
141
+ puts "\nšŸ› ļø Utility Functions:"
142
+
143
+ # Email validation
144
+ email = 'test@example.com'
145
+ puts "Email '#{email}' is valid: #{LicenseChain::Utils.validate_email(email)}"
146
+
147
+ # License key validation
148
+ license_key = LicenseChain::Utils.generate_license_key
149
+ puts "License key '#{license_key}' is valid: #{LicenseChain::Utils.validate_license_key(license_key)}"
150
+
151
+ # Generate UUID
152
+ uuid = LicenseChain::Utils.generate_uuid
153
+ puts "Generated UUID: #{uuid}"
154
+
155
+ # Format bytes
156
+ bytes = 1024 * 1024
157
+ puts "#{bytes} bytes = #{LicenseChain::Utils.format_bytes(bytes)}"
158
+
159
+ # Format duration
160
+ seconds = 3661
161
+ puts "Duration: #{LicenseChain::Utils.format_duration(seconds)}"
162
+
163
+ # String utilities
164
+ text = 'Hello World'
165
+ puts "Capitalize first: #{LicenseChain::Utils.capitalize_first(text)}"
166
+ puts "To snake_case: #{LicenseChain::Utils.to_snake_case('HelloWorld')}"
167
+ puts "To PascalCase: #{LicenseChain::Utils.to_pascal_case('hello_world')}"
168
+ puts "Slugify: #{LicenseChain::Utils.slugify('Hello World!')}"
169
+
170
+ # 7. Error Handling
171
+ puts "\nšŸ›”ļø Error Handling:"
172
+
173
+ begin
174
+ client.licenses.get('invalid-id')
175
+ rescue LicenseChain::ValidationError => e
176
+ puts "āœ… Caught expected validation error: #{e.message}"
177
+ end
178
+
179
+ begin
180
+ client.users.create('invalid-email', 'John Doe')
181
+ rescue LicenseChain::ValidationError => e
182
+ puts "āœ… Caught expected validation error: #{e.message}"
183
+ end
184
+
185
+ puts "\nāœ… Basic usage example completed successfully!"
186
+
187
+ rescue => e
188
+ puts "āŒ Error: #{e.class} - #{e.message}"
189
+ puts e.backtrace if ENV['DEBUG']
190
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # JWKS-only: verify license_token with token + JWKS URI (parity with Go/Rust/PHP jwks_only).
5
+ # export LICENSECHAIN_LICENSE_TOKEN="eyJ..."
6
+ # export LICENSECHAIN_LICENSE_JWKS_URI="https://api.licensechain.app/v1/licenses/jwks"
7
+ # # optional: LICENSECHAIN_EXPECTED_APP_ID=<uuid>
8
+ # ruby examples/jwks_only.rb
9
+
10
+ require 'json'
11
+ require_relative '../lib/licensechain_ruby_sdk'
12
+
13
+ token = ENV.fetch('LICENSECHAIN_LICENSE_TOKEN', '').strip
14
+ jwks = ENV.fetch('LICENSECHAIN_LICENSE_JWKS_URI', '').strip
15
+ if token.empty? || jwks.empty?
16
+ warn 'Set LICENSECHAIN_LICENSE_TOKEN and LICENSECHAIN_LICENSE_JWKS_URI'
17
+ exit 1
18
+ end
19
+
20
+ opts = {}
21
+ app = ENV['LICENSECHAIN_EXPECTED_APP_ID']&.strip
22
+ opts[:expected_app_id] = app unless app.nil? || app.empty?
23
+
24
+ payload = LicenseChain::LicenseAssertion.verify_license_assertion_jwt(token, jwks, **opts)
25
+ puts JSON.pretty_generate(payload)