bunny_app 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +308 -92
- data/lib/bunny_app/subscription.rb +67 -0
- data/lib/bunny_app/tenant.rb +42 -3
- data/lib/bunny_app/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 00eca4966ed34106d628843766d2f00f2e8a53b7611154ade889825d0add544c
|
|
4
|
+
data.tar.gz: 6994edecbd4d80b5b0e6dc53e79603629ca7ff85b1cf799f9bb9bab310ba52b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e2f422c3846e990cd2a35bc0b0b7ace22e099228cf54cca3641a3dc8915f3aec1a0681e31a1ff0c2db579e27b422fbffad0305d48eb25373488e024e27284c5d
|
|
7
|
+
data.tar.gz: 43a366d65b9f131444e08b7cc1caa9ec6c83a0feb8803e16560d2450d64fbbe406db89030d5781b69c3e602d5ea9b947267b2f4d62d24c59ab3255cc77be452b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.4.0 (14-Apr-2026)
|
|
4
|
+
|
|
5
|
+
- Add `Subscription.quantity_update` for updating subscription charge quantities
|
|
6
|
+
- Add `Subscription.trial_convert` for converting a trial to a paid subscription
|
|
7
|
+
|
|
8
|
+
## 2.3.0 (22-Nov-2025)
|
|
9
|
+
|
|
10
|
+
- Remove deprecated `latestProvisioningChange` fields from `Tenant.find_by` query
|
|
11
|
+
|
|
3
12
|
## 2.2.2 (13-Sep-2025)
|
|
4
13
|
|
|
5
14
|
- Make returnUrl optional in PortalSession.create mutation
|
data/README.md
CHANGED
|
@@ -1,182 +1,398 @@
|
|
|
1
1
|
# bunny-ruby
|
|
2
2
|
|
|
3
|
-
Ruby SDK for Bunny
|
|
3
|
+
Ruby SDK for [Bunny](https://www.bunny.com) — the subscription billing and management platform.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Add to your Gemfile:
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
10
|
gem 'bunny_app'
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Then run:
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
|
|
16
|
+
bundle install
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Or install
|
|
19
|
+
Or install directly:
|
|
20
20
|
|
|
21
21
|
```sh
|
|
22
|
-
|
|
22
|
+
gem install bunny_app
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Configuration
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
### With client credentials (recommended)
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Using `client_id` and `client_secret` enables automatic token refresh when the access token expires.
|
|
30
30
|
|
|
31
31
|
```ruby
|
|
32
32
|
require 'bunny_app'
|
|
33
33
|
|
|
34
|
-
# We recommend using the client_id/secret of your Bunny client app
|
|
35
|
-
# this will enable automatic retries when the access token expires
|
|
36
34
|
BunnyApp.config do |c|
|
|
37
|
-
c.client_id
|
|
38
|
-
c.client_secret = '
|
|
39
|
-
c.scope
|
|
40
|
-
c.base_uri
|
|
35
|
+
c.client_id = 'your-client-id'
|
|
36
|
+
c.client_secret = 'your-client-secret'
|
|
37
|
+
c.scope = 'standard:read standard:write'
|
|
38
|
+
c.base_uri = 'https://<subdomain>.bunny.com'
|
|
41
39
|
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### With a pre-existing access token
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
If you manage token lifecycle yourself, you can pass the token directly. An `AuthorizationError` will be raised if the token expires.
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
45
47
|
BunnyApp.config do |c|
|
|
46
|
-
c.access_token = '
|
|
47
|
-
c.base_uri
|
|
48
|
+
c.access_token = 'your-access-token'
|
|
49
|
+
c.base_uri = 'https://<subdomain>.bunny.com'
|
|
48
50
|
end
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
>
|
|
53
|
+
> **Never commit secrets to source control.** Load credentials from environment variables or a secret store.
|
|
52
54
|
|
|
53
|
-
###
|
|
55
|
+
### Rails initializer
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
Generate a config file at `config/initializers/bunny_app.rb`:
|
|
56
58
|
|
|
57
59
|
```sh
|
|
58
|
-
|
|
60
|
+
bin/rails g bunny_app:install
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This creates a template that reads credentials from environment variables:
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
BunnyApp.config do |c|
|
|
67
|
+
c.client_id = ENV['BUNNY_APP_CLIENT_ID']
|
|
68
|
+
c.client_secret = ENV['BUNNY_APP_CLIENT_SECRET']
|
|
69
|
+
c.scope = ENV['BUNNY_APP_SCOPE']
|
|
70
|
+
c.base_uri = 'https://<subdomain>.bunny.com'
|
|
71
|
+
end
|
|
59
72
|
```
|
|
60
73
|
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Subscriptions
|
|
77
|
+
|
|
61
78
|
### Create a subscription
|
|
62
79
|
|
|
80
|
+
Create a new subscription for an account. You can create the account inline or attach to an existing account by ID.
|
|
81
|
+
|
|
63
82
|
```ruby
|
|
64
|
-
|
|
83
|
+
# Create with a new account
|
|
84
|
+
subscription = BunnyApp::Subscription.create(
|
|
85
|
+
price_list_code: 'starter',
|
|
86
|
+
options: {
|
|
87
|
+
account_name: 'Acme Corp',
|
|
88
|
+
first_name: 'Jane',
|
|
89
|
+
last_name: 'Smith',
|
|
90
|
+
email: 'jane@acme.com',
|
|
91
|
+
trial: true,
|
|
92
|
+
tenant_code: 'acme-123',
|
|
93
|
+
tenant_name: 'Acme Corp'
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Create against an existing account
|
|
98
|
+
subscription = BunnyApp::Subscription.create(
|
|
65
99
|
price_list_code: 'starter',
|
|
66
100
|
options: {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
email: "meg@example.com",
|
|
71
|
-
trial: true,
|
|
72
|
-
tenant_code: "123456",
|
|
73
|
-
tenant_name: "Superdesk"
|
|
101
|
+
account_id: '456',
|
|
102
|
+
tenant_code: 'acme-123',
|
|
103
|
+
tenant_name: 'Acme Corp'
|
|
74
104
|
}
|
|
75
105
|
)
|
|
76
106
|
```
|
|
77
107
|
|
|
78
|
-
|
|
108
|
+
Returns a hash containing the created subscription, including `id`, `state`, `trialStartDate`, `trialEndDate`, `plan`, `priceList`, and `tenant`.
|
|
109
|
+
|
|
110
|
+
### Update subscription quantities
|
|
111
|
+
|
|
112
|
+
Adjust the quantities for one or more charges on an existing subscription.
|
|
79
113
|
|
|
80
114
|
```ruby
|
|
81
|
-
|
|
82
|
-
|
|
115
|
+
quote = BunnyApp::Subscription.quantity_update(
|
|
116
|
+
subscription_id: '456123',
|
|
117
|
+
quantities: [
|
|
118
|
+
{ code: 'users', quantity: 25 }
|
|
119
|
+
],
|
|
120
|
+
options: {
|
|
121
|
+
invoice_immediately: true,
|
|
122
|
+
start_date: '2024-06-01',
|
|
123
|
+
name: 'Add users — June',
|
|
124
|
+
allow_quantity_limits_override: false
|
|
125
|
+
}
|
|
83
126
|
)
|
|
84
127
|
```
|
|
85
128
|
|
|
86
|
-
|
|
129
|
+
Returns a `quote` hash with `id` and `name`.
|
|
87
130
|
|
|
88
|
-
|
|
131
|
+
### Convert a trial to paid
|
|
89
132
|
|
|
90
133
|
```ruby
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
134
|
+
# Convert using a price list code
|
|
135
|
+
result = BunnyApp::Subscription.trial_convert(
|
|
136
|
+
subscription_id: '456123',
|
|
137
|
+
price_list_code: 'starter'
|
|
138
|
+
)
|
|
94
139
|
|
|
95
|
-
#
|
|
96
|
-
|
|
97
|
-
|
|
140
|
+
# Convert using a price list ID and payment method
|
|
141
|
+
result = BunnyApp::Subscription.trial_convert(
|
|
142
|
+
subscription_id: '456123',
|
|
143
|
+
price_list_id: '789',
|
|
144
|
+
payment_id: '101112'
|
|
145
|
+
)
|
|
98
146
|
```
|
|
99
147
|
|
|
100
|
-
|
|
148
|
+
Returns a hash containing `invoice` (with `amount`, `id`, `number`, `subtotal`) and `subscription` (with `id`, `name`).
|
|
101
149
|
|
|
102
|
-
|
|
150
|
+
### Cancel a subscription
|
|
103
151
|
|
|
104
152
|
```ruby
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
id
|
|
111
|
-
quantity
|
|
112
|
-
usageAt
|
|
113
|
-
tenant {
|
|
114
|
-
id
|
|
115
|
-
code
|
|
116
|
-
name
|
|
117
|
-
}
|
|
118
|
-
feature {
|
|
119
|
-
id
|
|
120
|
-
code
|
|
121
|
-
name
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
GRAPHQL
|
|
153
|
+
BunnyApp::Subscription.cancel(subscription_id: '456123')
|
|
154
|
+
# => true
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
127
158
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
159
|
+
## Tenants
|
|
160
|
+
|
|
161
|
+
### Create a tenant
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
tenant = BunnyApp::Tenant.create(
|
|
165
|
+
name: 'Acme Corp',
|
|
166
|
+
code: 'acme-123',
|
|
167
|
+
account_id: '456',
|
|
168
|
+
platform_code: 'main' # optional, defaults to 'main'
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Returns a hash with `id`, `code`, `name`, `subdomain`, `lastLogin`, and `platform`.
|
|
173
|
+
|
|
174
|
+
### Update a tenant
|
|
175
|
+
|
|
176
|
+
Either `id` or `code` must be provided to identify the tenant. If only `code` is given, a lookup is performed first to resolve the ID.
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
# Identify by id
|
|
180
|
+
tenant = BunnyApp::Tenant.update(
|
|
181
|
+
id: '456123',
|
|
182
|
+
name: 'Acme Corp',
|
|
183
|
+
subdomain: 'acme',
|
|
184
|
+
last_login: '2024-06-01T12:00:00Z'
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Identify by code
|
|
188
|
+
tenant = BunnyApp::Tenant.update(
|
|
189
|
+
code: 'acme-123',
|
|
190
|
+
name: 'Acme Corp',
|
|
191
|
+
subdomain: 'acme'
|
|
192
|
+
)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Only the fields you provide will be sent. Returns a hash with `id`, `code`, `name`, `subdomain`, `lastLogin`, and `platform`.
|
|
196
|
+
|
|
197
|
+
### Find a tenant by code
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
tenant = BunnyApp::Tenant.find_by(code: 'acme-123')
|
|
201
|
+
# => { "id" => "1", "code" => "acme-123", "name" => "Acme Corp",
|
|
202
|
+
# "subdomain" => "acme", "lastLogin" => "...", "account" => { "id" => "456", ... } }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Returns `nil` if no tenant is found.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Tenant Metrics
|
|
210
|
+
|
|
211
|
+
Push usage and engagement metrics to Bunny for a tenant. Useful for health scoring and churn signals.
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
BunnyApp::TenantMetrics.update(
|
|
215
|
+
code: 'acme-123',
|
|
216
|
+
last_login: '2024-06-01T12:00:00Z',
|
|
217
|
+
user_count: 42,
|
|
218
|
+
utilization_metrics: {
|
|
219
|
+
projects_created: 10,
|
|
220
|
+
exports_run: 3
|
|
134
221
|
}
|
|
135
|
-
|
|
222
|
+
)
|
|
223
|
+
# => true
|
|
224
|
+
```
|
|
136
225
|
|
|
137
|
-
|
|
226
|
+
`utilization_metrics` is optional and accepts any key/value pairs you want to track.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Feature Usage
|
|
231
|
+
|
|
232
|
+
Track usage of individual features for usage-based billing or analytics.
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
# Track usage now
|
|
236
|
+
usage = BunnyApp::FeatureUsage.create(
|
|
237
|
+
quantity: 5,
|
|
238
|
+
feature_code: 'api_calls',
|
|
239
|
+
subscription_id: '456123'
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Track usage for a specific date
|
|
243
|
+
usage = BunnyApp::FeatureUsage.create(
|
|
244
|
+
quantity: 5,
|
|
245
|
+
feature_code: 'api_calls',
|
|
246
|
+
subscription_id: '456123',
|
|
247
|
+
usage_at: '2024-03-10'
|
|
248
|
+
)
|
|
138
249
|
```
|
|
139
250
|
|
|
140
|
-
|
|
251
|
+
Returns a hash with `id`, `quantity`, `usageAt`, `subscription`, and `feature`.
|
|
252
|
+
|
|
253
|
+
---
|
|
141
254
|
|
|
142
|
-
|
|
255
|
+
## Portal Sessions
|
|
256
|
+
|
|
257
|
+
Generate a short-lived token to embed the Bunny billing portal in your app using Bunny.js.
|
|
143
258
|
|
|
144
259
|
```ruby
|
|
145
|
-
|
|
260
|
+
# Basic session
|
|
261
|
+
token = BunnyApp::PortalSession.create(tenant_code: 'acme-123')
|
|
262
|
+
|
|
263
|
+
# With a return URL and custom expiry (in hours, default: 24)
|
|
264
|
+
token = BunnyApp::PortalSession.create(
|
|
265
|
+
tenant_code: 'acme-123',
|
|
266
|
+
return_url: 'https://yourapp.com/billing',
|
|
267
|
+
expiry_hours: 4
|
|
268
|
+
)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Returns the session token string.
|
|
272
|
+
|
|
273
|
+
---
|
|
146
274
|
|
|
147
|
-
|
|
275
|
+
## Platforms
|
|
276
|
+
|
|
277
|
+
Platforms allow you to group tenants. Create a platform before assigning tenants to it.
|
|
278
|
+
|
|
279
|
+
```ruby
|
|
280
|
+
platform = BunnyApp::Platform.create(
|
|
281
|
+
name: 'My SaaS Platform',
|
|
282
|
+
code: 'my-saas'
|
|
283
|
+
)
|
|
284
|
+
# => { "id" => "1", "name" => "My SaaS Platform", "code" => "my-saas" }
|
|
148
285
|
```
|
|
149
286
|
|
|
150
|
-
|
|
287
|
+
---
|
|
151
288
|
|
|
152
|
-
|
|
289
|
+
## Webhooks
|
|
153
290
|
|
|
154
|
-
|
|
291
|
+
Bunny sends webhooks for events like subscription state changes. Verify the `x-bunny-signature` header to confirm the payload is authentic.
|
|
292
|
+
|
|
293
|
+
```ruby
|
|
294
|
+
payload = request.raw_post
|
|
295
|
+
signature = request.headers['x-bunny-signature']
|
|
296
|
+
signing_key = ENV['BUNNY_WEBHOOK_SECRET']
|
|
297
|
+
|
|
298
|
+
if BunnyApp::Webhook.verify(signature, payload, signing_key)
|
|
299
|
+
# payload is authentic — process the event
|
|
300
|
+
event = JSON.parse(payload)
|
|
301
|
+
case event['type']
|
|
302
|
+
when 'SubscriptionProvisioningChange'
|
|
303
|
+
# handle provisioning change
|
|
304
|
+
end
|
|
305
|
+
else
|
|
306
|
+
head :unauthorized
|
|
307
|
+
end
|
|
308
|
+
```
|
|
155
309
|
|
|
156
|
-
|
|
310
|
+
---
|
|
157
311
|
|
|
158
|
-
|
|
312
|
+
## Custom GraphQL Queries
|
|
159
313
|
|
|
160
|
-
You can
|
|
314
|
+
You can send any GraphQL query or mutation directly if you need fields not covered by the convenience methods.
|
|
161
315
|
|
|
162
|
-
|
|
316
|
+
### Synchronous query
|
|
163
317
|
|
|
164
|
-
```
|
|
165
|
-
|
|
318
|
+
```ruby
|
|
319
|
+
query = <<~GRAPHQL
|
|
320
|
+
query GetTenant($code: String!) {
|
|
321
|
+
tenant(code: $code) {
|
|
322
|
+
id
|
|
323
|
+
name
|
|
324
|
+
account {
|
|
325
|
+
id
|
|
326
|
+
name
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
GRAPHQL
|
|
331
|
+
|
|
332
|
+
response = BunnyApp.query(query, { code: 'acme-123' })
|
|
333
|
+
tenant = response['data']['tenant']
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Asynchronous query (fire-and-forget)
|
|
337
|
+
|
|
338
|
+
Runs the request in a background thread. Useful for non-critical tracking calls where you don't want to block the request cycle.
|
|
339
|
+
|
|
340
|
+
```ruby
|
|
341
|
+
BunnyApp.query_async(query, variables)
|
|
166
342
|
```
|
|
167
343
|
|
|
168
|
-
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Error Handling
|
|
347
|
+
|
|
348
|
+
All convenience methods raise on error. Two exception classes are provided:
|
|
349
|
+
|
|
350
|
+
| Exception | When raised |
|
|
351
|
+
|---|---|
|
|
352
|
+
| `BunnyApp::AuthorizationError` | Invalid or expired credentials |
|
|
353
|
+
| `BunnyApp::ResponseError` | API returned errors in the response body |
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
begin
|
|
357
|
+
BunnyApp::Subscription.cancel(subscription_id: '456123')
|
|
358
|
+
rescue BunnyApp::AuthorizationError => e
|
|
359
|
+
# Re-authenticate or alert
|
|
360
|
+
Rails.logger.error "Bunny auth failed: #{e.message}"
|
|
361
|
+
rescue BunnyApp::ResponseError => e
|
|
362
|
+
# The API rejected the request
|
|
363
|
+
Rails.logger.error "Bunny error: #{e.message}"
|
|
364
|
+
end
|
|
365
|
+
```
|
|
169
366
|
|
|
170
|
-
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Requirements
|
|
370
|
+
|
|
371
|
+
Ruby 2.5+
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Development
|
|
171
376
|
|
|
172
377
|
```sh
|
|
173
|
-
|
|
378
|
+
bundle install # install dependencies
|
|
379
|
+
bundle exec rake spec # run tests
|
|
380
|
+
bin/console # interactive console
|
|
174
381
|
```
|
|
175
382
|
|
|
176
|
-
|
|
383
|
+
Set `IGNORE_SSL=true` when running locally to suppress SSL warnings:
|
|
177
384
|
|
|
178
385
|
```sh
|
|
386
|
+
IGNORE_SSL=true bin/console
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Publishing
|
|
390
|
+
|
|
391
|
+
Update `lib/bunny_app/version.rb`, then:
|
|
392
|
+
|
|
393
|
+
```sh
|
|
394
|
+
gem build
|
|
179
395
|
gem push bunny_app-x.x.x.gem
|
|
180
396
|
```
|
|
181
397
|
|
|
182
|
-
The
|
|
398
|
+
The RubyGems account is protected by MFA and managed by @richet.
|
|
@@ -39,6 +39,36 @@ module BunnyApp
|
|
|
39
39
|
}
|
|
40
40
|
GRAPHQL
|
|
41
41
|
|
|
42
|
+
@subscription_quantity_update_mutation = <<-GRAPHQL
|
|
43
|
+
mutation subscriptionQuantityUpdate ($subscriptionId: ID!, $quantities: [SubscriptionChargeQuantityAttributes!]!, $invoiceImmediately: Boolean!, $startDate: ISO8601Date!, $name: String!, $allowQuantityLimitsOverride: Boolean!) {
|
|
44
|
+
subscriptionQuantityUpdate (subscriptionId: $subscriptionId, quantities: $quantities, invoiceImmediately: $invoiceImmediately, startDate: $startDate, name: $name, allowQuantityLimitsOverride: $allowQuantityLimitsOverride) {
|
|
45
|
+
errors
|
|
46
|
+
quote {
|
|
47
|
+
id
|
|
48
|
+
name
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
GRAPHQL
|
|
53
|
+
|
|
54
|
+
@subscription_trial_convert_mutation = <<-GRAPHQL
|
|
55
|
+
mutation subscriptionTrialConvert ($subscriptionId: ID!, $paymentId: ID!, $priceListId: ID!, $priceListCode: String!) {
|
|
56
|
+
subscriptionTrialConvert (subscriptionId: $subscriptionId, paymentId: $paymentId, priceListId: $priceListId, priceListCode: $priceListCode) {
|
|
57
|
+
errors
|
|
58
|
+
invoice {
|
|
59
|
+
amount
|
|
60
|
+
id
|
|
61
|
+
number
|
|
62
|
+
subtotal
|
|
63
|
+
}
|
|
64
|
+
subscription {
|
|
65
|
+
id
|
|
66
|
+
name
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
GRAPHQL
|
|
71
|
+
|
|
42
72
|
@subscription_cancel_mutation = <<-GRAPHQL
|
|
43
73
|
mutation subscriptionCancel ($ids: [ID!]!) {
|
|
44
74
|
subscriptionCancel (ids: $ids) {
|
|
@@ -83,6 +113,43 @@ module BunnyApp
|
|
|
83
113
|
res['data']['subscriptionCreate']['subscription']
|
|
84
114
|
end
|
|
85
115
|
|
|
116
|
+
def self.quantity_update(subscription_id:, quantities:, options: {})
|
|
117
|
+
variables = {
|
|
118
|
+
subscriptionId: subscription_id,
|
|
119
|
+
quantities:,
|
|
120
|
+
invoiceImmediately: options[:invoice_immediately] || false,
|
|
121
|
+
startDate: options[:start_date],
|
|
122
|
+
name: options[:name],
|
|
123
|
+
allowQuantityLimitsOverride: options[:allow_quantity_limits_override] || false
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
res = Client.new.query(@subscription_quantity_update_mutation, variables)
|
|
127
|
+
if res['data']['subscriptionQuantityUpdate']['errors']
|
|
128
|
+
raise ResponseError,
|
|
129
|
+
res['data']['subscriptionQuantityUpdate']['errors'].join(',')
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
res['data']['subscriptionQuantityUpdate']['quote']
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def self.trial_convert(subscription_id:, price_list_id: nil, price_list_code: nil, payment_id: nil)
|
|
136
|
+
variables = {
|
|
137
|
+
subscriptionId: subscription_id
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
variables[:paymentId] = payment_id if payment_id
|
|
141
|
+
variables[:priceListId] = price_list_id if price_list_id
|
|
142
|
+
variables[:priceListCode] = price_list_code if price_list_code
|
|
143
|
+
|
|
144
|
+
res = Client.new.query(@subscription_trial_convert_mutation, variables)
|
|
145
|
+
if res['data']['subscriptionTrialConvert']['errors']
|
|
146
|
+
raise ResponseError,
|
|
147
|
+
res['data']['subscriptionTrialConvert']['errors'].join(',')
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
res['data']['subscriptionTrialConvert']
|
|
151
|
+
end
|
|
152
|
+
|
|
86
153
|
def self.cancel(subscription_id:)
|
|
87
154
|
variables = {
|
|
88
155
|
ids: [subscription_id]
|
data/lib/bunny_app/tenant.rb
CHANGED
|
@@ -7,6 +7,8 @@ module BunnyApp
|
|
|
7
7
|
code
|
|
8
8
|
id
|
|
9
9
|
name
|
|
10
|
+
subdomain
|
|
11
|
+
lastLogin
|
|
10
12
|
platform {
|
|
11
13
|
id
|
|
12
14
|
name
|
|
@@ -25,6 +27,7 @@ module BunnyApp
|
|
|
25
27
|
code
|
|
26
28
|
name
|
|
27
29
|
subdomain
|
|
30
|
+
lastLogin
|
|
28
31
|
account {
|
|
29
32
|
id
|
|
30
33
|
name
|
|
@@ -34,6 +37,26 @@ module BunnyApp
|
|
|
34
37
|
}
|
|
35
38
|
GRAPHQL
|
|
36
39
|
|
|
40
|
+
@tenant_update_mutation = <<-GRAPHQL
|
|
41
|
+
mutation tenantUpdate ($id: ID, $code: String, $attributes: TenantAttributes!) {
|
|
42
|
+
tenantUpdate (id: $id, code: $code, attributes: $attributes) {
|
|
43
|
+
errors
|
|
44
|
+
tenant {
|
|
45
|
+
code
|
|
46
|
+
id
|
|
47
|
+
name
|
|
48
|
+
subdomain
|
|
49
|
+
lastLogin
|
|
50
|
+
platform {
|
|
51
|
+
id
|
|
52
|
+
name
|
|
53
|
+
code
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
GRAPHQL
|
|
59
|
+
|
|
37
60
|
def self.create(name:, code:, account_id:, platform_code: 'main')
|
|
38
61
|
variables = {
|
|
39
62
|
attributes: {
|
|
@@ -50,10 +73,26 @@ module BunnyApp
|
|
|
50
73
|
res['data']['tenantCreate']['tenant']
|
|
51
74
|
end
|
|
52
75
|
|
|
76
|
+
def self.update(id: nil, code: nil, **attrs)
|
|
77
|
+
raise ArgumentError, 'id or code is required' unless id || code
|
|
78
|
+
|
|
79
|
+
attributes = {
|
|
80
|
+
name: attrs[:name],
|
|
81
|
+
code: attrs[:code],
|
|
82
|
+
subdomain: attrs[:subdomain],
|
|
83
|
+
lastLogin: attrs[:last_login]
|
|
84
|
+
}.compact
|
|
85
|
+
|
|
86
|
+
variables = { id:, code:, attributes: }
|
|
87
|
+
|
|
88
|
+
res = Client.new.query(@tenant_update_mutation, variables)
|
|
89
|
+
raise ResponseError, res['data']['tenantUpdate']['errors'].join(',') if res['data']['tenantUpdate']['errors']
|
|
90
|
+
|
|
91
|
+
res['data']['tenantUpdate']['tenant']
|
|
92
|
+
end
|
|
93
|
+
|
|
53
94
|
def self.find_by(code:)
|
|
54
|
-
variables = {
|
|
55
|
-
code:
|
|
56
|
-
}
|
|
95
|
+
variables = { code: }
|
|
57
96
|
|
|
58
97
|
res = Client.new.query(@tenant_query, variables)
|
|
59
98
|
|
data/lib/bunny_app/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bunny_app
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bunny
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2026-05-22 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: httparty
|