license_manager 0.2.0 → 0.3.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +1 -1
  4. data/docs/BillingPortalSession.md +18 -0
  5. data/docs/CreateBillingPortalSession.md +18 -0
  6. data/docs/CreateIntegrationMapping.md +1 -1
  7. data/docs/Integration.md +6 -0
  8. data/docs/IntegrationMapping.md +2 -0
  9. data/docs/IntegrationMappingRecurring.md +20 -0
  10. data/docs/License.md +4 -0
  11. data/docs/LicenseCustomer.md +22 -0
  12. data/docs/LicenseSubscription.md +34 -0
  13. data/docs/LicensesApi.md +5 -5
  14. data/docs/Mrr.md +26 -0
  15. data/docs/MrrByIntervalInner.md +24 -0
  16. data/docs/MrrByPolicyInner.md +22 -0
  17. data/docs/PortalApi.md +79 -0
  18. data/docs/ProductStats.md +2 -0
  19. data/docs/Revenue.md +8 -4
  20. data/docs/StripePermission.md +3 -1
  21. data/docs/StripePrice.md +2 -0
  22. data/docs/StripePriceRecurring.md +20 -0
  23. data/docs/SubscriptionStats.md +38 -0
  24. data/docs/UpdateIntegration.md +3 -1
  25. data/lib/license_manager/api/licenses_api.rb +6 -6
  26. data/lib/license_manager/api/portal_api.rb +70 -0
  27. data/lib/license_manager/models/billing_portal_session.rb +178 -0
  28. data/lib/license_manager/models/create_billing_portal_session.rb +161 -0
  29. data/lib/license_manager/models/create_integration_mapping.rb +1 -1
  30. data/lib/license_manager/models/event_type.rb +4 -1
  31. data/lib/license_manager/models/integration.rb +82 -1
  32. data/lib/license_manager/models/integration_mapping.rb +13 -1
  33. data/lib/license_manager/models/integration_mapping_recurring.rb +228 -0
  34. data/lib/license_manager/models/license.rb +39 -1
  35. data/lib/license_manager/models/license_customer.rb +217 -0
  36. data/lib/license_manager/models/license_subscription.rb +388 -0
  37. data/lib/license_manager/models/mrr.rb +291 -0
  38. data/lib/license_manager/models/mrr_by_interval_inner.rb +279 -0
  39. data/lib/license_manager/models/mrr_by_policy_inner.rb +229 -0
  40. data/lib/license_manager/models/product_stats.rb +14 -1
  41. data/lib/license_manager/models/revenue.rb +60 -6
  42. data/lib/license_manager/models/stripe_permission.rb +31 -4
  43. data/lib/license_manager/models/stripe_price.rb +13 -1
  44. data/lib/license_manager/models/stripe_price_recurring.rb +228 -0
  45. data/lib/license_manager/models/subscription_stats.rb +456 -0
  46. data/lib/license_manager/models/update_integration.rb +42 -4
  47. data/lib/license_manager/version.rb +1 -1
  48. data/lib/license_manager.rb +10 -0
  49. data/openapi.json +599 -14
  50. data/spec/api/licenses_api_spec.rb +3 -3
  51. data/spec/api/portal_api_spec.rb +13 -0
  52. data/spec/models/billing_portal_session_spec.rb +36 -0
  53. data/spec/models/create_billing_portal_session_spec.rb +36 -0
  54. data/spec/models/create_token_spec.rb +1 -1
  55. data/spec/models/integration_mapping_recurring_spec.rb +46 -0
  56. data/spec/models/integration_mapping_spec.rb +6 -0
  57. data/spec/models/integration_spec.rb +18 -0
  58. data/spec/models/license_customer_spec.rb +48 -0
  59. data/spec/models/license_spec.rb +12 -0
  60. data/spec/models/license_subscription_spec.rb +88 -0
  61. data/spec/models/mrr_by_interval_inner_spec.rb +58 -0
  62. data/spec/models/mrr_by_policy_inner_spec.rb +48 -0
  63. data/spec/models/mrr_spec.rb +60 -0
  64. data/spec/models/product_stats_spec.rb +6 -0
  65. data/spec/models/revenue_spec.rb +12 -0
  66. data/spec/models/stripe_permission_spec.rb +6 -0
  67. data/spec/models/stripe_price_recurring_spec.rb +46 -0
  68. data/spec/models/stripe_price_spec.rb +6 -0
  69. data/spec/models/subscription_stats_spec.rb +96 -0
  70. data/spec/models/token_spec.rb +1 -1
  71. data/spec/models/update_integration_spec.rb +6 -0
  72. metadata +41 -1
data/openapi.json CHANGED
@@ -1004,6 +1004,17 @@
1004
1004
  ],
1005
1005
  "description": "Cancellations of the licenses with a money back guarantee. `null` when no license of the product has one."
1006
1006
  },
1007
+ "subscriptions": {
1008
+ "anyOf": [
1009
+ {
1010
+ "$ref": "#/components/schemas/SubscriptionStats"
1011
+ },
1012
+ {
1013
+ "type": "null"
1014
+ }
1015
+ ],
1016
+ "description": "Subscriptions of the product. `null` when it has never had one."
1017
+ },
1007
1018
  "revenue": {
1008
1019
  "type": "array",
1009
1020
  "items": {
@@ -1112,6 +1123,7 @@
1112
1123
  "newLicensesByMonth",
1113
1124
  "newLicensesByPolicy",
1114
1125
  "cancellation",
1126
+ "subscriptions",
1115
1127
  "revenue",
1116
1128
  "activationsByVersion",
1117
1129
  "activeMachinesByVersion",
@@ -1210,6 +1222,197 @@
1210
1222
  "description": "- `too_expensive`: too expensive.\n- `missing_features`: missing features i need.\n- `not_working`: doesn't work as expected.\n- `switched`: found an alternative.\n- `unused`: no longer needed.\n- `other`: other.",
1211
1223
  "example": "too_expensive"
1212
1224
  },
1225
+ "SubscriptionStats": {
1226
+ "type": "object",
1227
+ "properties": {
1228
+ "activeCount": {
1229
+ "type": "integer",
1230
+ "description": "Number of ongoing subscriptions: `active`, `trialing` or `past_due`.",
1231
+ "example": 120
1232
+ },
1233
+ "trialingCount": {
1234
+ "type": "integer",
1235
+ "example": 4
1236
+ },
1237
+ "pastDueCount": {
1238
+ "type": "integer",
1239
+ "description": "Number of them whose renewal failed, as Stripe retries it: their licenses stay valid for the integration's grace days.",
1240
+ "example": 3
1241
+ },
1242
+ "cancelingCount": {
1243
+ "type": "integer",
1244
+ "description": "Number of them set to end at the end of their period.",
1245
+ "example": 5
1246
+ },
1247
+ "newCount": {
1248
+ "type": "integer",
1249
+ "description": "Number of subscriptions started in the last 30 days.",
1250
+ "example": 14
1251
+ },
1252
+ "endedCount": {
1253
+ "type": "integer",
1254
+ "description": "Number of subscriptions that ended in the last 30 days.",
1255
+ "example": 3
1256
+ },
1257
+ "churnRate": {
1258
+ "type": "number",
1259
+ "description": "Share of the subscriptions ongoing 30 days ago that ended since, from 0 to 1.",
1260
+ "example": 0.025
1261
+ },
1262
+ "newByMonth": {
1263
+ "type": "array",
1264
+ "items": {
1265
+ "type": "integer"
1266
+ },
1267
+ "description": "Number of subscriptions started each month, aligned on `months`.",
1268
+ "example": [
1269
+ 3,
1270
+ 5,
1271
+ 8
1272
+ ]
1273
+ },
1274
+ "endedByMonth": {
1275
+ "type": "array",
1276
+ "items": {
1277
+ "type": "integer"
1278
+ },
1279
+ "description": "Number of subscriptions that ended each month, aligned on `months`.",
1280
+ "example": [
1281
+ 0,
1282
+ 1,
1283
+ 2
1284
+ ]
1285
+ },
1286
+ "activeByMonth": {
1287
+ "type": "array",
1288
+ "items": {
1289
+ "type": "integer"
1290
+ },
1291
+ "description": "Number of ongoing subscriptions at the end of each month, aligned on `months`, today for this month.",
1292
+ "example": [
1293
+ 90,
1294
+ 104,
1295
+ 120
1296
+ ]
1297
+ },
1298
+ "mrr": {
1299
+ "type": "array",
1300
+ "items": {
1301
+ "$ref": "#/components/schemas/Mrr"
1302
+ },
1303
+ "description": "MRR in each currency, from the highest number of subscriptions. Amounts aren't converted between currencies."
1304
+ }
1305
+ },
1306
+ "required": [
1307
+ "activeCount",
1308
+ "trialingCount",
1309
+ "pastDueCount",
1310
+ "cancelingCount",
1311
+ "newCount",
1312
+ "endedCount",
1313
+ "churnRate",
1314
+ "newByMonth",
1315
+ "endedByMonth",
1316
+ "activeByMonth",
1317
+ "mrr"
1318
+ ],
1319
+ "description": "Subscriptions to recurring Stripe prices, each with its subscription license."
1320
+ },
1321
+ "Mrr": {
1322
+ "type": "object",
1323
+ "properties": {
1324
+ "currency": {
1325
+ "type": "string",
1326
+ "example": "USD"
1327
+ },
1328
+ "mrr": {
1329
+ "type": "number",
1330
+ "description": "Monthly recurring revenue: the price of every ongoing subscription in the currency, times its quantity, brought to one month (a year counts for a twelfth), before taxes and discounts.",
1331
+ "example": 1240
1332
+ },
1333
+ "byInterval": {
1334
+ "type": "array",
1335
+ "items": {
1336
+ "type": "object",
1337
+ "properties": {
1338
+ "interval": {
1339
+ "type": "string",
1340
+ "enum": [
1341
+ "day",
1342
+ "week",
1343
+ "month",
1344
+ "year"
1345
+ ]
1346
+ },
1347
+ "intervalCount": {
1348
+ "type": "integer",
1349
+ "example": 1
1350
+ },
1351
+ "subscriptionCount": {
1352
+ "type": "integer",
1353
+ "example": 80
1354
+ },
1355
+ "mrr": {
1356
+ "type": "number",
1357
+ "example": 800
1358
+ }
1359
+ },
1360
+ "required": [
1361
+ "interval",
1362
+ "intervalCount",
1363
+ "subscriptionCount",
1364
+ "mrr"
1365
+ ]
1366
+ },
1367
+ "description": "Ongoing subscriptions and MRR of each billing period, like monthly and yearly, from the highest MRR."
1368
+ },
1369
+ "byPolicy": {
1370
+ "type": "array",
1371
+ "items": {
1372
+ "type": "object",
1373
+ "properties": {
1374
+ "policyId": {
1375
+ "type": "string",
1376
+ "example": "p4Rt8LmQ"
1377
+ },
1378
+ "subscriptionCount": {
1379
+ "type": "integer",
1380
+ "example": 80
1381
+ },
1382
+ "mrr": {
1383
+ "type": "number",
1384
+ "example": 800
1385
+ }
1386
+ },
1387
+ "required": [
1388
+ "policyId",
1389
+ "subscriptionCount",
1390
+ "mrr"
1391
+ ]
1392
+ },
1393
+ "description": "MRR of each policy, from the highest."
1394
+ },
1395
+ "byMonth": {
1396
+ "type": "array",
1397
+ "items": {
1398
+ "type": "number"
1399
+ },
1400
+ "description": "MRR at the end of each month, aligned on `months`, today for this month, at the current prices of the subscriptions.",
1401
+ "example": [
1402
+ 900,
1403
+ 1100,
1404
+ 1240
1405
+ ]
1406
+ }
1407
+ },
1408
+ "required": [
1409
+ "currency",
1410
+ "mrr",
1411
+ "byInterval",
1412
+ "byPolicy",
1413
+ "byMonth"
1414
+ ]
1415
+ },
1213
1416
  "Revenue": {
1214
1417
  "type": "object",
1215
1418
  "properties": {
@@ -1220,22 +1423,32 @@
1220
1423
  },
1221
1424
  "licenseCount": {
1222
1425
  "type": "integer",
1223
- "description": "Number of licenses sold in the currency, free ones included, canceled ones left out.",
1426
+ "description": "Number of licenses sold once in the currency, free ones included, canceled ones and subscription licenses left out.",
1224
1427
  "example": 42
1225
1428
  },
1226
1429
  "total": {
1227
1430
  "type": "number",
1228
- "description": "Sum of the prices of the licenses sold in the currency, in major units, canceled ones left out.",
1431
+ "description": "Revenue in the currency, in major units: the prices of the licenses sold once, canceled ones left out, plus the payments of the subscriptions, `subscriptionTotal`.",
1229
1432
  "example": 2099.58
1230
1433
  },
1434
+ "subscriptionTotal": {
1435
+ "type": "number",
1436
+ "description": "Sum of the payments of the subscription licenses in the currency, before taxes, in major units.",
1437
+ "example": 480
1438
+ },
1439
+ "paymentCount": {
1440
+ "type": "integer",
1441
+ "description": "Number of payments of the subscription licenses in the currency.",
1442
+ "example": 48
1443
+ },
1231
1444
  "canceledLicenseCount": {
1232
1445
  "type": "integer",
1233
- "description": "Number of canceled licenses sold in the currency.",
1446
+ "description": "Number of canceled licenses sold once in the currency.",
1234
1447
  "example": 2
1235
1448
  },
1236
1449
  "canceledTotal": {
1237
1450
  "type": "number",
1238
- "description": "Sum of the prices of the canceled licenses sold in the currency, in major units: revenue lost to cancellations.",
1451
+ "description": "Sum of the prices of the canceled licenses sold once in the currency, in major units: revenue lost to cancellations.",
1239
1452
  "example": 99.98
1240
1453
  },
1241
1454
  "thisMonth": {
@@ -1324,6 +1537,8 @@
1324
1537
  "currency",
1325
1538
  "licenseCount",
1326
1539
  "total",
1540
+ "subscriptionTotal",
1541
+ "paymentCount",
1327
1542
  "canceledLicenseCount",
1328
1543
  "canceledTotal",
1329
1544
  "thisMonth",
@@ -1333,7 +1548,7 @@
1333
1548
  "byMonth",
1334
1549
  "byPolicy"
1335
1550
  ],
1336
- "description": "Revenue of the licenses sold in one currency, each license counting on the day it was created. Licenses without a price are left out, and so are canceled ones, but in `canceledLicenseCount` and `canceledTotal`."
1551
+ "description": "Revenue in one currency: the licenses sold once, each counting on the day it was created, and the payments of the subscription licenses, each counting on the day it was paid, with the license's policy. Licenses without a price are left out, and so are canceled ones, but in `canceledLicenseCount` and `canceledTotal`."
1337
1552
  },
1338
1553
  "PolicyList": {
1339
1554
  "type": "object",
@@ -1888,6 +2103,9 @@
1888
2103
  "type": "string",
1889
2104
  "example": "c9Wd3RtE"
1890
2105
  },
2106
+ "customer": {
2107
+ "$ref": "#/components/schemas/LicenseCustomer"
2108
+ },
1891
2109
  "key": {
1892
2110
  "type": "string",
1893
2111
  "description": "Unique within the product. Case sensitive: `validateKey` compares keys exactly.",
@@ -1994,6 +2212,9 @@
1994
2212
  }
1995
2213
  ]
1996
2214
  },
2215
+ "subscription": {
2216
+ "$ref": "#/components/schemas/LicenseSubscription"
2217
+ },
1997
2218
  "cancellationComment": {
1998
2219
  "type": [
1999
2220
  "string",
@@ -2022,6 +2243,7 @@
2022
2243
  "productId",
2023
2244
  "policyId",
2024
2245
  "customerId",
2246
+ "customer",
2025
2247
  "key",
2026
2248
  "createdAt",
2027
2249
  "expiresAt",
@@ -2037,11 +2259,40 @@
2037
2259
  "cancellableUntil",
2038
2260
  "canceledAt",
2039
2261
  "cancellationReason",
2262
+ "subscription",
2040
2263
  "cancellationComment",
2041
2264
  "stripeRefundId",
2042
2265
  "updatedAt"
2043
2266
  ]
2044
2267
  },
2268
+ "LicenseCustomer": {
2269
+ "type": "object",
2270
+ "properties": {
2271
+ "id": {
2272
+ "type": "string",
2273
+ "example": "c9Wd3RtE"
2274
+ },
2275
+ "email": {
2276
+ "type": "string",
2277
+ "format": "email",
2278
+ "example": "jane@example.com"
2279
+ },
2280
+ "name": {
2281
+ "type": [
2282
+ "string",
2283
+ "null"
2284
+ ],
2285
+ "description": "Display name of the customer. `null` when unknown.",
2286
+ "example": "Jane Doe"
2287
+ }
2288
+ },
2289
+ "required": [
2290
+ "id",
2291
+ "email",
2292
+ "name"
2293
+ ],
2294
+ "description": "Customer the license belongs to, the one of `customerId`."
2295
+ },
2045
2296
  "LicenseMaxVersion": {
2046
2297
  "type": "object",
2047
2298
  "properties": {
@@ -2114,6 +2365,80 @@
2114
2365
  "description": "`canceled` when the license was canceled, `expired` when it has expired, `active` when a machine validated its key in the last 90 days, `inactive` otherwise.",
2115
2366
  "example": "active"
2116
2367
  },
2368
+ "LicenseSubscription": {
2369
+ "type": [
2370
+ "object",
2371
+ "null"
2372
+ ],
2373
+ "properties": {
2374
+ "stripeSubscriptionId": {
2375
+ "type": "string",
2376
+ "example": "sub_1MowQVLkdIwHu7ix"
2377
+ },
2378
+ "status": {
2379
+ "type": "string",
2380
+ "description": "Status of the subscription in Stripe: `active`, `trialing`, `past_due` while a failed payment is retried, `canceled`, `unpaid`, `paused`, `incomplete` or `incomplete_expired`.",
2381
+ "example": "active"
2382
+ },
2383
+ "interval": {
2384
+ "type": "string",
2385
+ "enum": [
2386
+ "day",
2387
+ "week",
2388
+ "month",
2389
+ "year"
2390
+ ]
2391
+ },
2392
+ "intervalCount": {
2393
+ "type": "integer",
2394
+ "description": "Number of intervals between payments.",
2395
+ "example": 1
2396
+ },
2397
+ "quantity": {
2398
+ "type": "integer",
2399
+ "description": "Units subscribed, multiplying the policy's machines.",
2400
+ "example": 1
2401
+ },
2402
+ "price": {
2403
+ "allOf": [
2404
+ {
2405
+ "$ref": "#/components/schemas/Price"
2406
+ },
2407
+ {
2408
+ "description": "Price of one unit for one period, before taxes."
2409
+ }
2410
+ ]
2411
+ },
2412
+ "currentPeriodEnd": {
2413
+ "type": "string",
2414
+ "format": "date-time",
2415
+ "description": "End of the paid period, when the subscription renews or ends."
2416
+ },
2417
+ "cancelAtPeriodEnd": {
2418
+ "type": "boolean",
2419
+ "description": "Whether the subscription ends at the end of the period."
2420
+ },
2421
+ "endedAt": {
2422
+ "type": [
2423
+ "string",
2424
+ "null"
2425
+ ],
2426
+ "format": "date-time"
2427
+ }
2428
+ },
2429
+ "required": [
2430
+ "stripeSubscriptionId",
2431
+ "status",
2432
+ "interval",
2433
+ "intervalCount",
2434
+ "quantity",
2435
+ "price",
2436
+ "currentPeriodEnd",
2437
+ "cancelAtPeriodEnd",
2438
+ "endedAt"
2439
+ ],
2440
+ "description": "The Stripe subscription keeping the license valid, `null` when it isn't a subscription license. The license expires when it stops being paid, includes every update, and can't be canceled during a money back guarantee."
2441
+ },
2117
2442
  "CreateLicense": {
2118
2443
  "type": "object",
2119
2444
  "properties": {
@@ -2616,6 +2941,30 @@
2616
2941
  "expiresInSeconds"
2617
2942
  ]
2618
2943
  },
2944
+ "BillingPortalSession": {
2945
+ "type": "object",
2946
+ "properties": {
2947
+ "url": {
2948
+ "type": "string",
2949
+ "description": "Link to the Stripe customer portal, to open right away: it expires shortly.",
2950
+ "example": "https://billing.stripe.com/p/session/test_YWNjdF8x"
2951
+ }
2952
+ },
2953
+ "required": [
2954
+ "url"
2955
+ ]
2956
+ },
2957
+ "CreateBillingPortalSession": {
2958
+ "type": "object",
2959
+ "properties": {
2960
+ "returnUrl": {
2961
+ "type": "string",
2962
+ "format": "uri",
2963
+ "description": "Where the Stripe customer portal links back to: a page of the customer portal, or of one of the product's `returnDomains`. The customer portal of the product by default.",
2964
+ "example": "https://example.com/account"
2965
+ }
2966
+ }
2967
+ },
2619
2968
  "SendPortalLink": {
2620
2969
  "type": "object",
2621
2970
  "properties": {
@@ -2786,6 +3135,7 @@
2786
3135
  "enum": [
2787
3136
  "customer:read",
2788
3137
  "customer:write",
3138
+ "customer:billing",
2789
3139
  "license:read",
2790
3140
  "license:write",
2791
3141
  "license:cancel",
@@ -2822,6 +3172,7 @@
2822
3172
  "enum": [
2823
3173
  "customer:read",
2824
3174
  "customer:write",
3175
+ "customer:billing",
2825
3176
  "license:read",
2826
3177
  "license:write",
2827
3178
  "license:cancel",
@@ -2944,9 +3295,12 @@
2944
3295
  "license.canceled",
2945
3296
  "license.refunded",
2946
3297
  "license.refund_failed",
3298
+ "license.payment_succeeded",
3299
+ "license.payment_failed",
3300
+ "license.subscription_ended",
2947
3301
  "machine.deactivated"
2948
3302
  ],
2949
- "description": "- `policy.created`: `payload` holds the policy's settings.\n- `policy.updated`: `payload.changes` maps each changed field to its previous (`from`) and new (`to`) value. Making a policy the default also updates the previous default one.\n- `license.created`: `payload` holds the license's key, `policyId`, `customerId` and settings. Not linked to the policy.\n- `license.updated`: `payload.changes`, like `policy.updated`.\n- `license.validated`: a machine validated the license key. `payload` holds the machine's `fingerprint`, `name` and `version`, whether the validation created the machine (`machineCreated`) or activated it again (`machineActivated`), the `overriddenMachineId` of the machine it deactivated to take its place, `null` when none, and the `lockedMachineIds` of the machines it locked as it took its place back (see `validateKey`).\n- `license.validation_failed`: validating the license key failed. `payload` holds the error `code` and `message`, and the `fingerprint`, `name`, `platform` and `version` sent, the name and platform being the machine's when none is sent. Linked to the machine when it exists.\n- `license.canceled`: the license was canceled. `payload` holds the cancellation `reason` and `comment`, and whether its Stripe payment is being refunded (`refund`).\n- `license.refunded`: the Stripe payment of a canceled license was refunded. `payload` holds the Stripe `refundId` and the `amount` refunded. Has the `integration` actor.\n- `license.refund_failed`: refunding the Stripe payment of a canceled license failed. `payload` holds the error `message`: refund it from Stripe. Has the `integration` actor.\n- `machine.deactivated`: a machine was deactivated or locked, so it no longer counts as using the license. `payload` holds the machine's `fingerprint` and `name`, the `overriddenByMachineId` of the machine that took its place, `null` when it was deactivated through the API, and whether it was `locked`. Deactivations by validations have the `automation` actor. Linked to the license and the machine.\n\nAn update that changes nothing records no event. Deletions record no events, and customers, products and releases have none.",
3303
+ "description": "- `policy.created`: `payload` holds the policy's settings.\n- `policy.updated`: `payload.changes` maps each changed field to its previous (`from`) and new (`to`) value. Making a policy the default also updates the previous default one.\n- `license.created`: `payload` holds the license's key, `policyId`, `customerId` and settings. Not linked to the policy.\n- `license.updated`: `payload.changes`, like `policy.updated`.\n- `license.validated`: a machine validated the license key. `payload` holds the machine's `fingerprint`, `name` and `version`, whether the validation created the machine (`machineCreated`) or activated it again (`machineActivated`), the `overriddenMachineId` of the machine it deactivated to take its place, `null` when none, and the `lockedMachineIds` of the machines it locked as it took its place back (see `validateKey`).\n- `license.validation_failed`: validating the license key failed. `payload` holds the error `code` and `message`, and the `fingerprint`, `name`, `platform` and `version` sent, the name and platform being the machine's when none is sent. Linked to the machine when it exists.\n- `license.canceled`: the license was canceled. `payload` holds the cancellation `reason` and `comment`, and whether its Stripe payment is being refunded (`refund`).\n- `license.refunded`: the Stripe payment of a canceled license was refunded. `payload` holds the Stripe `refundId` and the `amount` refunded. Has the `integration` actor.\n- `license.refund_failed`: refunding the Stripe payment of a canceled license failed. `payload` holds the error `message`: refund it from Stripe. Has the `integration` actor.\n- `license.payment_succeeded`: a payment of the license's Stripe subscription succeeded, its first one included. `payload` holds the Stripe `invoiceId`, the `amount` paid before taxes and its `currency`, Stripe's `billingReason` (`subscription_create`, `subscription_cycle`, `subscription_update`…) and the `periodStart` and `periodEnd` it pays for. Invoices with nothing to pay, like for a trial, record none. Has the `integration` actor.\n- `license.payment_failed`: a payment of the license's Stripe subscription failed. `payload` holds the Stripe `invoiceId`, the `amount` due and its `currency`, the `attemptCount` and when Stripe tries again (`nextAttemptAt`, `null` when it won't). The license stays valid for the integration's grace days after its period ends. Has the `integration` actor.\n- `license.subscription_ended`: the license's Stripe subscription ended, canceled or no longer paid, so the license expired. `payload` holds the subscription's `status` and Stripe's cancellation `reason`. Has the `integration` actor.\n- `machine.deactivated`: a machine was deactivated or locked, so it no longer counts as using the license. `payload` holds the machine's `fingerprint` and `name`, the `overriddenByMachineId` of the machine that took its place, `null` when it was deactivated through the API, and whether it was `locked`. Deactivations by validations have the `automation` actor. Linked to the license and the machine.\n\nAn update that changes nothing records no event. Deletions record no events, and customers, products and releases have none.",
2950
3304
  "example": "license.validated"
2951
3305
  },
2952
3306
  "EventMachine": {
@@ -3216,13 +3570,19 @@
3216
3570
  "type": "boolean",
3217
3571
  "description": "Whether the key can connect without it, losing what it's for, explained by `reason`.",
3218
3572
  "example": false
3573
+ },
3574
+ "subscriptions": {
3575
+ "type": "boolean",
3576
+ "description": "Whether it's only needed to sell subscriptions: checked once a recurring price is mapped.",
3577
+ "example": false
3219
3578
  }
3220
3579
  },
3221
3580
  "required": [
3222
3581
  "resource",
3223
3582
  "access",
3224
3583
  "reason",
3225
- "optional"
3584
+ "optional",
3585
+ "subscriptions"
3226
3586
  ]
3227
3587
  },
3228
3588
  "Integration": {
@@ -3297,6 +3657,21 @@
3297
3657
  "description": "Whether the API key has the optional Refunds: Write permission, checked when connecting and every day. Without it, canceled licenses aren't refunded: refund their payments from Stripe.",
3298
3658
  "example": true
3299
3659
  },
3660
+ "canManageSubscriptions": {
3661
+ "type": "boolean",
3662
+ "description": "Whether the API key has the optional Subscriptions: Write permission, checked when connecting and every day. Without it, subscription licenses can't be canceled from the app: cancel their subscriptions from Stripe.",
3663
+ "example": true
3664
+ },
3665
+ "canBillingPortal": {
3666
+ "type": "boolean",
3667
+ "description": "Whether the API key has the optional Customer portal: Write permission, checked when connecting and every day. Without it, the license portal doesn't link to the Stripe customer portal.",
3668
+ "example": true
3669
+ },
3670
+ "subscriptionGraceDays": {
3671
+ "type": "integer",
3672
+ "description": "Days a subscription license stays valid after its period ends while its renewal is unpaid, as Stripe retries the payment. 7 by default.",
3673
+ "example": 7
3674
+ },
3300
3675
  "createdAt": {
3301
3676
  "type": "string",
3302
3677
  "format": "date-time"
@@ -3315,6 +3690,9 @@
3315
3690
  "lastEventAt",
3316
3691
  "autoRefund",
3317
3692
  "canRefund",
3693
+ "canManageSubscriptions",
3694
+ "canBillingPortal",
3695
+ "subscriptionGraceDays",
3318
3696
  "createdAt"
3319
3697
  ]
3320
3698
  },
@@ -3362,6 +3740,32 @@
3362
3740
  "price": {
3363
3741
  "$ref": "#/components/schemas/Price"
3364
3742
  },
3743
+ "recurring": {
3744
+ "type": [
3745
+ "object",
3746
+ "null"
3747
+ ],
3748
+ "properties": {
3749
+ "interval": {
3750
+ "type": "string",
3751
+ "enum": [
3752
+ "day",
3753
+ "week",
3754
+ "month",
3755
+ "year"
3756
+ ]
3757
+ },
3758
+ "intervalCount": {
3759
+ "type": "integer",
3760
+ "example": 1
3761
+ }
3762
+ },
3763
+ "required": [
3764
+ "interval",
3765
+ "intervalCount"
3766
+ ],
3767
+ "description": "Billing period of a recurring price, `null` for a one-time price. Subscribing to a recurring price creates one subscription license, valid while it's paid."
3768
+ },
3365
3769
  "status": {
3366
3770
  "type": "string",
3367
3771
  "enum": [
@@ -3391,6 +3795,7 @@
3391
3795
  "stripePriceId",
3392
3796
  "productName",
3393
3797
  "price",
3798
+ "recurring",
3394
3799
  "status",
3395
3800
  "statusReason",
3396
3801
  "createdAt"
@@ -3417,6 +3822,13 @@
3417
3822
  "type": "boolean",
3418
3823
  "description": "Whether canceling a license bought through Stripe refunds its payment. Can only be turned on when the integration's `canRefund` is true: the API key has the Refunds: Write permission.",
3419
3824
  "example": false
3825
+ },
3826
+ "subscriptionGraceDays": {
3827
+ "type": "integer",
3828
+ "minimum": 0,
3829
+ "maximum": 60,
3830
+ "description": "Days a subscription license stays valid after its period ends while its renewal is unpaid, as Stripe retries the payment.",
3831
+ "example": 7
3420
3832
  }
3421
3833
  }
3422
3834
  },
@@ -3445,6 +3857,32 @@
3445
3857
  "price": {
3446
3858
  "$ref": "#/components/schemas/Price"
3447
3859
  },
3860
+ "recurring": {
3861
+ "type": [
3862
+ "object",
3863
+ "null"
3864
+ ],
3865
+ "properties": {
3866
+ "interval": {
3867
+ "type": "string",
3868
+ "enum": [
3869
+ "day",
3870
+ "week",
3871
+ "month",
3872
+ "year"
3873
+ ]
3874
+ },
3875
+ "intervalCount": {
3876
+ "type": "integer",
3877
+ "example": 1
3878
+ }
3879
+ },
3880
+ "required": [
3881
+ "interval",
3882
+ "intervalCount"
3883
+ ],
3884
+ "description": "Billing period of a recurring price, `null` for a one-time price."
3885
+ },
3448
3886
  "mapped": {
3449
3887
  "type": "boolean",
3450
3888
  "description": "Whether the price already creates licenses of a product."
@@ -3456,6 +3894,7 @@
3456
3894
  "productName",
3457
3895
  "nickname",
3458
3896
  "price",
3897
+ "recurring",
3459
3898
  "mapped"
3460
3899
  ]
3461
3900
  },
@@ -3465,7 +3904,7 @@
3465
3904
  "stripePriceId": {
3466
3905
  "type": "string",
3467
3906
  "minLength": 1,
3468
- "description": "Active one-time price of the Stripe account.",
3907
+ "description": "Active one-time or licensed recurring price of the Stripe account.",
3469
3908
  "example": "price_1MoBy5LkdIwHu7ix"
3470
3909
  },
3471
3910
  "policyId": {
@@ -5995,11 +6434,11 @@
5995
6434
  "type": "string",
5996
6435
  "minLength": 1,
5997
6436
  "maxLength": 200,
5998
- "description": "Case-insensitive text the key contains.",
6437
+ "description": "Case-insensitive text the key or the email of the customer contains.",
5999
6438
  "example": "X9ZPA"
6000
6439
  },
6001
6440
  "required": false,
6002
- "description": "Case-insensitive text the key contains.",
6441
+ "description": "Case-insensitive text the key or the email of the customer contains.",
6003
6442
  "name": "search",
6004
6443
  "in": "query"
6005
6444
  },
@@ -6507,11 +6946,11 @@
6507
6946
  "type": "string",
6508
6947
  "minLength": 1,
6509
6948
  "maxLength": 200,
6510
- "description": "Case-insensitive text the key contains.",
6949
+ "description": "Case-insensitive text the key or the email of the customer contains.",
6511
6950
  "example": "X9ZPA"
6512
6951
  },
6513
6952
  "required": false,
6514
- "description": "Case-insensitive text the key contains.",
6953
+ "description": "Case-insensitive text the key or the email of the customer contains.",
6515
6954
  "name": "search",
6516
6955
  "in": "query"
6517
6956
  },
@@ -6984,6 +7423,16 @@
6984
7423
  }
6985
7424
  }
6986
7425
  },
7426
+ "409": {
7427
+ "description": "`subscription_managed`: the license is a subscription license, whose `expiresAt`, `price`, `maxVersion` and `cancellableUntil` follow its Stripe subscription",
7428
+ "content": {
7429
+ "application/json": {
7430
+ "schema": {
7431
+ "$ref": "#/components/schemas/Error"
7432
+ }
7433
+ }
7434
+ }
7435
+ },
6987
7436
  "422": {
6988
7437
  "description": "`validation_error`: invalid path, query or body, with the `issues`",
6989
7438
  "content": {
@@ -7204,7 +7653,7 @@
7204
7653
  ],
7205
7654
  "operationId": "cancelLicense",
7206
7655
  "summary": "Cancel a license",
7207
- "description": "Cancels the license right away: validating its key then fails with `license_expired`, like an expired license, and its status is `canceled`. A cancellation can't be undone.\n\nCustomers can cancel their licenses until their `cancellableUntil`, the end of their money back guarantee: tokens can't cancel a license past it, or without one. API keys and sessions can cancel any license.\n\nWhen the license was bought through the Stripe integration, its payment is refunded in the background, unless the integration disables `autoRefund`, its API key lacks the optional Refunds: Write permission (`canRefund`), or `refund` is `false`: the refund is its price paid, taxes included, and its id is set as `stripeRefundId`. The customer is emailed a confirmation, and the owners and admins of the organization are told about the cancellation and its refund. In test products, the customer is only emailed when their email is a member's of the organization.\n\nRecords a `license.canceled` event, then a `license.refunded` or `license.refund_failed` one once the refund is done.\n\n**Token access**: customer, license tokens with the `license:cancel` permission (or the matching write one). A token reaches the license it's scoped to, or the licenses of its customer.\n\n**Required role permission**: `license:cancel` when called with a session, granted by the member's role. API keys aren't limited by roles.",
7656
+ "description": "Cancels the license right away: validating its key then fails with `license_expired`, like an expired license, and its status is `canceled`. A cancellation can't be undone.\n\nCustomers can cancel their licenses until their `cancellableUntil`, the end of their money back guarantee: tokens can't cancel a license past it, or without one. API keys and sessions can cancel any license.\n\nWhen the license was bought through the Stripe integration, its payment is refunded in the background, unless the integration disables `autoRefund`, its API key lacks the optional Refunds: Write permission (`canRefund`), or `refund` is `false`: the refund is its price paid, taxes included, and its id is set as `stripeRefundId`. The customer is emailed a confirmation, and the owners and admins of the organization are told about the cancellation and its refund. In test products, the customer is only emailed when their email is a member's of the organization.\n\nA subscription license isn't refunded: its Stripe subscription is canceled at once, without proration, which needs the integration's `canManageSubscriptions`.\n\nRecords a `license.canceled` event, then a `license.refunded` or `license.refund_failed` one once the refund is done.\n\n**Token access**: customer, license tokens with the `license:cancel` permission (or the matching write one). A token reaches the license it's scoped to, or the licenses of its customer.\n\n**Required role permission**: `license:cancel` when called with a session, granted by the member's role. API keys aren't limited by roles.",
7208
7657
  "security": [
7209
7658
  {
7210
7659
  "apiKey": []
@@ -7291,7 +7740,7 @@
7291
7740
  }
7292
7741
  },
7293
7742
  "409": {
7294
- "description": "`already_canceled`: the license is already canceled. `not_cancellable`: for tokens, the license has no money back guarantee, or it has ended",
7743
+ "description": "`already_canceled`: the license is already canceled. `not_cancellable`: for tokens, the license has no money back guarantee, or it has ended, as subscription licenses have none. `subscription_managed`: the license is a subscription license, and the Stripe API key lacks the Subscriptions: Write permission to cancel its subscription",
7295
7744
  "content": {
7296
7745
  "application/json": {
7297
7746
  "schema": {
@@ -7928,6 +8377,142 @@
7928
8377
  }
7929
8378
  }
7930
8379
  },
8380
+ "/v1/customers/{id}/billing-portal-sessions": {
8381
+ "post": {
8382
+ "tags": [
8383
+ "Portal"
8384
+ ],
8385
+ "operationId": "createBillingPortalSession",
8386
+ "summary": "Open the Stripe customer portal of a customer",
8387
+ "description": "Link to the Stripe customer portal of the customer's Stripe customer, where they manage their subscriptions: change their plan or payment method, see their invoices, cancel. Needs the Stripe integration's `canBillingPortal`, and the customer portal configured in the Stripe account's settings. The changes come back through the integration's webhook, keeping the subscription licenses in line.\n\n**Token access**: customer tokens with the `customer:billing` permission (or the matching write one). A customer token reaches its own customer.\n\n**Required role permission**: `portalLink:create` when called with a session, granted by the member's role. API keys aren't limited by roles.",
8388
+ "security": [
8389
+ {
8390
+ "apiKey": []
8391
+ },
8392
+ {
8393
+ "token": [
8394
+ "customer:billing"
8395
+ ]
8396
+ }
8397
+ ],
8398
+ "x-required-permissions": [
8399
+ "portalLink:create"
8400
+ ],
8401
+ "parameters": [
8402
+ {
8403
+ "schema": {
8404
+ "type": "string",
8405
+ "example": "c9Wd3RtE"
8406
+ },
8407
+ "required": true,
8408
+ "name": "id",
8409
+ "in": "path"
8410
+ }
8411
+ ],
8412
+ "requestBody": {
8413
+ "required": false,
8414
+ "content": {
8415
+ "application/json": {
8416
+ "schema": {
8417
+ "$ref": "#/components/schemas/CreateBillingPortalSession"
8418
+ }
8419
+ }
8420
+ }
8421
+ },
8422
+ "responses": {
8423
+ "201": {
8424
+ "description": "Created session",
8425
+ "content": {
8426
+ "application/json": {
8427
+ "schema": {
8428
+ "$ref": "#/components/schemas/BillingPortalSession"
8429
+ }
8430
+ }
8431
+ }
8432
+ },
8433
+ "401": {
8434
+ "description": "`unauthorized`: no credentials. `invalid_api_key`: the API key doesn't exist, was revoked or has expired. `invalid_token`: the token's signature is invalid, it has expired, or the resource it's scoped to was deleted",
8435
+ "content": {
8436
+ "application/json": {
8437
+ "schema": {
8438
+ "$ref": "#/components/schemas/Error"
8439
+ }
8440
+ }
8441
+ }
8442
+ },
8443
+ "402": {
8444
+ "description": "`plan_limit_exceeded`: the organization stayed over its plan's limits after the grace period, its API is blocked until it upgrades. `plan_limit_reached`: creating the resource would go over the plan's limit",
8445
+ "content": {
8446
+ "application/json": {
8447
+ "schema": {
8448
+ "$ref": "#/components/schemas/Error"
8449
+ }
8450
+ }
8451
+ }
8452
+ },
8453
+ "403": {
8454
+ "description": "`token_not_allowed`: the endpoint doesn't accept tokens, or not of this type. `insufficient_scope`: the token lacks the permission. `no_active_organization` or `forbidden`: the session has no organization, or isn't a member of the one requested",
8455
+ "content": {
8456
+ "application/json": {
8457
+ "schema": {
8458
+ "$ref": "#/components/schemas/Error"
8459
+ }
8460
+ }
8461
+ }
8462
+ },
8463
+ "404": {
8464
+ "description": "Customer not found",
8465
+ "content": {
8466
+ "application/json": {
8467
+ "schema": {
8468
+ "$ref": "#/components/schemas/Error"
8469
+ }
8470
+ }
8471
+ }
8472
+ },
8473
+ "409": {
8474
+ "description": "`no_stripe_customer`: the customer didn't pay through the Stripe integration. `billing_portal_unavailable`: the Stripe integration isn't connected, or its API key lacks the Customer portal: Write permission",
8475
+ "content": {
8476
+ "application/json": {
8477
+ "schema": {
8478
+ "$ref": "#/components/schemas/Error"
8479
+ }
8480
+ }
8481
+ }
8482
+ },
8483
+ "422": {
8484
+ "description": "`validation_error`: invalid path, query or body, with the `issues`",
8485
+ "content": {
8486
+ "application/json": {
8487
+ "schema": {
8488
+ "$ref": "#/components/schemas/Error"
8489
+ }
8490
+ }
8491
+ }
8492
+ },
8493
+ "429": {
8494
+ "description": "`rate_limited`: the API key made more than 10 requests in the last second. Wait for the `Retry-After` seconds",
8495
+ "content": {
8496
+ "application/json": {
8497
+ "schema": {
8498
+ "$ref": "#/components/schemas/Error"
8499
+ }
8500
+ }
8501
+ }
8502
+ },
8503
+ "502": {
8504
+ "description": "`provider_unavailable`: Stripe refused or couldn't be reached, like when its customer portal isn't configured",
8505
+ "content": {
8506
+ "application/json": {
8507
+ "schema": {
8508
+ "$ref": "#/components/schemas/Error"
8509
+ }
8510
+ }
8511
+ }
8512
+ }
8513
+ }
8514
+ }
8515
+ },
7931
8516
  "/v1/customers/{id}/portal-links/send": {
7932
8517
  "post": {
7933
8518
  "tags": [