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.
- checksums.yaml +7 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +60 -0
- data/.github/workflows/publish-rubygems.yml +24 -0
- data/.gitignore +96 -0
- data/CHANGELOG.md +26 -0
- data/Gemfile +16 -0
- data/LICENSE +95 -0
- data/README.md +492 -0
- data/ROADMAP.md +13 -0
- data/examples/basic_usage.rb +190 -0
- data/examples/jwks_only.rb +25 -0
- data/lib/licensechain/api_client.rb +139 -0
- data/lib/licensechain/client.rb +37 -0
- data/lib/licensechain/configuration.rb +41 -0
- data/lib/licensechain/enhanced_client.rb +320 -0
- data/lib/licensechain/errors.rb +15 -0
- data/lib/licensechain/license_assertion.rb +67 -0
- data/lib/licensechain/license_validator.rb +174 -0
- data/lib/licensechain/models.rb +354 -0
- data/lib/licensechain/services/license_service.rb +108 -0
- data/lib/licensechain/services/product_service.rb +49 -0
- data/lib/licensechain/services/user_service.rb +59 -0
- data/lib/licensechain/services/webhook_service.rb +74 -0
- data/lib/licensechain/utils.rb +211 -0
- data/lib/licensechain/webhook_handler.rb +157 -0
- data/lib/licensechain/webhook_verifier.rb +184 -0
- data/lib/licensechain_ruby_sdk/version.rb +3 -0
- data/lib/licensechain_ruby_sdk.rb +36 -0
- data/licensechain_ruby_sdk.gemspec +44 -0
- metadata +275 -0
data/README.md
ADDED
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
# LicenseChain Ruby SDK
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://www.ruby-lang.org/)
|
|
5
|
+
[](https://rubygems.org/gems/licensechain-sdk)
|
|
6
|
+
[](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)
|