@owlmeans/payment 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +524 -0
  3. package/build/.gitkeep +0 -0
  4. package/build/consts.d.ts +62 -0
  5. package/build/consts.d.ts.map +1 -0
  6. package/build/consts.js +87 -0
  7. package/build/consts.js.map +1 -0
  8. package/build/errors.d.ts +42 -0
  9. package/build/errors.d.ts.map +1 -0
  10. package/build/errors.js +81 -0
  11. package/build/errors.js.map +1 -0
  12. package/build/helper.d.ts +3 -0
  13. package/build/helper.d.ts.map +1 -0
  14. package/build/helper.js +3 -0
  15. package/build/helper.js.map +1 -0
  16. package/build/i18n/en.json +39 -0
  17. package/build/i18n.d.ts +2 -0
  18. package/build/i18n.d.ts.map +1 -0
  19. package/build/i18n.js +4 -0
  20. package/build/i18n.js.map +1 -0
  21. package/build/index.d.ts +9 -0
  22. package/build/index.d.ts.map +1 -0
  23. package/build/index.js +8 -0
  24. package/build/index.js.map +1 -0
  25. package/build/model/index.d.ts +6 -0
  26. package/build/model/index.d.ts.map +1 -0
  27. package/build/model/index.js +6 -0
  28. package/build/model/index.js.map +1 -0
  29. package/build/model/localization.d.ts +4 -0
  30. package/build/model/localization.d.ts.map +1 -0
  31. package/build/model/localization.js +19 -0
  32. package/build/model/localization.js.map +1 -0
  33. package/build/model/plan.d.ts +4 -0
  34. package/build/model/plan.d.ts.map +1 -0
  35. package/build/model/plan.js +43 -0
  36. package/build/model/plan.js.map +1 -0
  37. package/build/model/product.d.ts +4 -0
  38. package/build/model/product.d.ts.map +1 -0
  39. package/build/model/product.js +17 -0
  40. package/build/model/product.js.map +1 -0
  41. package/build/model/subscription.d.ts +5 -0
  42. package/build/model/subscription.d.ts.map +1 -0
  43. package/build/model/subscription.js +46 -0
  44. package/build/model/subscription.js.map +1 -0
  45. package/build/model/utils.d.ts +5 -0
  46. package/build/model/utils.d.ts.map +1 -0
  47. package/build/model/utils.js +28 -0
  48. package/build/model/utils.js.map +1 -0
  49. package/build/modules.d.ts +2 -0
  50. package/build/modules.d.ts.map +1 -0
  51. package/build/modules.js +9 -0
  52. package/build/modules.js.map +1 -0
  53. package/build/service.d.ts +5 -0
  54. package/build/service.d.ts.map +1 -0
  55. package/build/service.js +71 -0
  56. package/build/service.js.map +1 -0
  57. package/build/types.d.ts +116 -0
  58. package/build/types.d.ts.map +1 -0
  59. package/build/types.js +2 -0
  60. package/build/types.js.map +1 -0
  61. package/build/utils/index.d.ts +2 -0
  62. package/build/utils/index.d.ts.map +1 -0
  63. package/build/utils/index.js +2 -0
  64. package/build/utils/index.js.map +1 -0
  65. package/build/utils/types.d.ts +7 -0
  66. package/build/utils/types.d.ts.map +1 -0
  67. package/build/utils/types.js +2 -0
  68. package/build/utils/types.js.map +1 -0
  69. package/package.json +52 -0
  70. package/src/consts.ts +100 -0
  71. package/src/errors.ts +102 -0
  72. package/src/helper.ts +5 -0
  73. package/src/i18n/en.json +39 -0
  74. package/src/i18n.ts +6 -0
  75. package/src/index.ts +9 -0
  76. package/src/model/index.ts +6 -0
  77. package/src/model/localization.ts +24 -0
  78. package/src/model/plan.ts +51 -0
  79. package/src/model/product.ts +21 -0
  80. package/src/model/subscription.ts +49 -0
  81. package/src/model/utils.ts +32 -0
  82. package/src/modules.ts +15 -0
  83. package/src/service.ts +95 -0
  84. package/src/types.ts +119 -0
  85. package/src/utils/index.ts +2 -0
  86. package/src/utils/types.ts +9 -0
  87. package/tsconfig.json +15 -0
  88. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,32 @@
1
+
2
+ import type { JSONSchemaType } from 'ajv'
3
+ import type { LimitConfig, CapabilityUsage } from '../types.js'
4
+ import { PlanDurationSchema } from '../consts.js'
5
+ import { DateSchema } from '@owlmeans/auth'
6
+
7
+ export const LimitConfigSchema: JSONSchemaType<LimitConfig> = {
8
+ type: 'object',
9
+ properties: {
10
+ interval: PlanDurationSchema,
11
+ measurment: { type: 'string', nullable: true, maxLength: 16 },
12
+ limit: { type: 'number' }
13
+ },
14
+ required: ['interval', 'limit'],
15
+ additionalProperties: false,
16
+ }
17
+
18
+ export const CapabilityUsageSchema: JSONSchemaType<CapabilityUsage> = {
19
+ type: 'object',
20
+ properties: {
21
+ interval: PlanDurationSchema,
22
+ measurment: { type: 'string', nullable: true, maxLength: 16 },
23
+ startedAt: DateSchema,
24
+ limit: { type: 'number' },
25
+ refreshedAt: { ...DateSchema, nullable: true },
26
+ refreshAt: { ...DateSchema, nullable: true },
27
+ lastConsumedAt: { ...DateSchema, nullable: true },
28
+ consumption: { type: 'number' }
29
+ },
30
+ required: ['interval', 'limit', 'startedAt', 'consumption'],
31
+ additionalProperties: false,
32
+ }
package/src/modules.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { body, filter, module } from '@owlmeans/module'
2
+ import { route, backend, RouteMethod } from '@owlmeans/route'
3
+ import { paymentApi } from './consts.js'
4
+ import { SubscriptionPropogateBodySchema } from './model/subscription.js'
5
+
6
+ export const modules = [
7
+ module(route(paymentApi.subscription.base, '/subscription', backend())),
8
+ module(
9
+ route(paymentApi.subscription.propogate, '/propogate', backend(
10
+ paymentApi.subscription.base,
11
+ RouteMethod.POST
12
+ )),
13
+ filter(body(SubscriptionPropogateBodySchema))
14
+ )
15
+ ]
package/src/service.ts ADDED
@@ -0,0 +1,95 @@
1
+ import { createService, assertContext } from '@owlmeans/context'
2
+ import type { ConfigRecord } from '@owlmeans/context'
3
+ import { DEFAULT_ALIAS, PaymentEntityType } from './consts.js'
4
+ import type { Localization, PaymentService } from './types.js'
5
+ import type { Config, Context } from './utils/types.js'
6
+ import { PLAN_RECORD_TYPE, PRODUCT_RECORD_PREFIX } from './consts.js'
7
+ import { PaymentIdentificationError, UnknownProduct } from './errors.js'
8
+ import type { Product, ProductPlan } from './types.js'
9
+ import { fromConfigRecord } from '@owlmeans/config'
10
+ import type { ResourceRecord } from '@owlmeans/resource'
11
+ import { l10nToId } from './helper.js'
12
+ import { DEFAULT_LNG } from '@owlmeans/i18n'
13
+ import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope'
14
+ import type { AuthCredentials } from '@owlmeans/auth'
15
+
16
+ export const makePaymentService = (alias: string = DEFAULT_ALIAS): PaymentService => {
17
+ const service: PaymentService = createService<PaymentService>(alias, {
18
+ product: async sku => {
19
+ const context = assertContext(service.ctx) as Context
20
+
21
+ const configRes = context.getConfigResource()
22
+
23
+ const result = fromConfigRecord<ConfigRecord, Product & ResourceRecord>(await configRes.get(`${PRODUCT_RECORD_PREFIX}:${sku}`))
24
+
25
+ if (result == null) {
26
+ throw new UnknownProduct(sku)
27
+ }
28
+
29
+ return result as Product
30
+ },
31
+
32
+ plans: async (productSku, duration) => {
33
+ const context = assertContext(service.ctx) as Context
34
+ const configRes = context.getConfigResource()
35
+
36
+ const result = await configRes.list({
37
+ productSku, duration, recordType: PLAN_RECORD_TYPE
38
+ })
39
+
40
+ return result.items.map(
41
+ item => fromConfigRecord<ConfigRecord, ProductPlan & ResourceRecord>(item)
42
+ ) as ProductPlan[]
43
+ },
44
+
45
+ localize: async (lng, entity) => {
46
+ const context = assertContext(service.ctx) as Context
47
+ const configRes = context.getConfigResource()
48
+
49
+ const idsToCheck: string[] = []
50
+ if ("productSku" in entity) {
51
+ idsToCheck.push(l10nToId({ lng, type: PaymentEntityType.Plan, sku: entity.sku }, true))
52
+ idsToCheck.push(l10nToId({ lng: DEFAULT_LNG, type: PaymentEntityType.Plan, sku: entity.sku }, true))
53
+
54
+ idsToCheck.push(l10nToId({ lng, type: PaymentEntityType.Product, sku: entity.productSku }, true))
55
+ idsToCheck.push(l10nToId({ lng: DEFAULT_LNG, type: PaymentEntityType.Product, sku: entity.productSku }, true))
56
+ } else if ("sku" in entity) {
57
+ idsToCheck.push(l10nToId({ lng, type: PaymentEntityType.Product, sku: entity.sku }, true))
58
+ idsToCheck.push(l10nToId({ lng: DEFAULT_LNG, type: PaymentEntityType.Product, sku: entity.sku }, true))
59
+ } else {
60
+ idsToCheck.push(l10nToId({ lng, type: PaymentEntityType.CapabilitySet, sku: entity.scope }, true))
61
+ idsToCheck.push(l10nToId({ lng: DEFAULT_LNG, type: PaymentEntityType.CapabilitySet, sku: entity.scope }, true))
62
+ }
63
+
64
+ for (let id of idsToCheck) {
65
+ const l10n = await configRes.load(id)
66
+ if (l10n != null) {
67
+ return fromConfigRecord<ConfigRecord, Localization & ResourceRecord>(l10n)
68
+ }
69
+ }
70
+
71
+ return null
72
+ },
73
+
74
+ shallowAuthentication: async token => {
75
+ if (token == null) {
76
+ throw new PaymentIdentificationError('token')
77
+ }
78
+ const envelope = makeEnvelopeModel<AuthCredentials>(token, EnvelopeKind.Token)
79
+ const auth = envelope.message()
80
+ if (auth.profileId == null) {
81
+ throw new PaymentIdentificationError('profileId')
82
+ }
83
+
84
+ return auth.profileId
85
+ }
86
+ }, service => async () => {
87
+ service.initialized = true
88
+ })
89
+
90
+ return service
91
+ }
92
+
93
+ export const appendPaymentService = <C extends Config, T extends Context<C>>(
94
+ context: T, alias: string = DEFAULT_ALIAS
95
+ ): T => context.registerService(makePaymentService(alias))
package/src/types.ts ADDED
@@ -0,0 +1,119 @@
1
+ import type { InitializedService } from '@owlmeans/context'
2
+
3
+ import type { PaymentEntityType, PlanDuration, PlanStatus, ProductType, SubscriptionStatus } from './consts.js'
4
+ import type { PermissionSet } from '@owlmeans/auth'
5
+
6
+ export interface Product {
7
+ type: ProductType
8
+ sku: string
9
+ title: string
10
+ description?: string
11
+ defaultLng?: string
12
+ /**
13
+ * Related software services.
14
+ * Actually even if it's a physical product, it can have related
15
+ * software services.
16
+ */
17
+ services?: string[]
18
+ capabilities?: PermissionSet[]
19
+ }
20
+
21
+ export interface Localization {
22
+ sku: string
23
+ type: PaymentEntityType
24
+ lng: string
25
+ title?: string
26
+ description?: string
27
+ keywords?: { [key: string]: string }
28
+ }
29
+
30
+ export interface ProductPlan {
31
+ productSku: string
32
+ sku: string
33
+ status: PlanStatus
34
+ payagateAliases?: {
35
+ [paygate: string]: string
36
+ }
37
+ duration: PlanDuration
38
+ trial?: number
39
+ gatedTrial?: boolean
40
+ price: number
41
+ order?: number
42
+ highlight?: string
43
+ currency?: string
44
+ originalPrice?: number
45
+ discount?: number
46
+ awaitingInterval?: number
47
+ title: string
48
+ description?: string
49
+ customUrl?: string
50
+ createdAt?: Date
51
+ archivedAt?: Date
52
+ /**
53
+ * Deprecation date should be set alongside suspnsion date (that should be set to the future).
54
+ * This way we can plan event when users loose access in case they don't upgrade.
55
+ */
56
+ deprecatedAt?: Date
57
+ supsendedAt?: Date
58
+ capabilities?: PermissionSet[]
59
+ limits?: { [key: string]: LimitConfig }
60
+ }
61
+
62
+ export interface PlanSubscription {
63
+ sku: string
64
+ entityId: string
65
+ paymentMethod?: string
66
+ externalId?: string
67
+ createdAt: Date
68
+ lastPaymentAt?: Date
69
+ trialUntil?: Date
70
+ startsdAt: Date
71
+ expirationAt?: Date
72
+ endsAt?: Date
73
+ archiveAt?: Date
74
+ suspendedAt?: Date
75
+ canceledAt?: Date
76
+ blockedAt?: Date
77
+ suspendedUntil?: Date
78
+ status: SubscriptionStatus
79
+ /**
80
+ * Custom capabilities for the particular subscription.
81
+ */
82
+ capabilities?: PermissionSet[]
83
+ /**
84
+ * Custom limits for the particular subscription.
85
+ */
86
+ limits?: { [key: string]: LimitConfig }
87
+ consumptions?: { [key: string]: CapabilityUsage }
88
+ }
89
+
90
+ export interface SubscriptionPropogateBody extends PlanSubscription {
91
+ service: string
92
+ externalId: string
93
+ }
94
+
95
+ export interface LimitConfig {
96
+ interval: PlanDuration
97
+ limit: number
98
+ measurment?: string
99
+ }
100
+
101
+ export interface CapabilityUsage extends LimitConfig {
102
+ startedAt: Date
103
+ refreshedAt?: Date
104
+ refreshAt?: Date
105
+ lastConsumedAt?: Date
106
+ consumption: number
107
+ }
108
+
109
+ type PaymentEntity = Product | ProductPlan | PermissionSet
110
+
111
+ export interface PaymentService extends InitializedService {
112
+ product: (sku: string) => Promise<Product>
113
+
114
+ plans: (productSku: string, duration: PlanDuration) => Promise<ProductPlan[]>
115
+
116
+ localize: (lng: string, entity: PaymentEntity) => Promise<Localization | null>
117
+
118
+ shallowAuthentication: (token: string | null) => Promise<string>
119
+ }
@@ -0,0 +1,2 @@
1
+
2
+ export type * from './types.js'
@@ -0,0 +1,9 @@
1
+
2
+ import type { CommonConfig, ConfigResourceAppend } from '@owlmeans/config'
3
+ import type { BasicContext } from '@owlmeans/context'
4
+
5
+ export interface Config extends CommonConfig { }
6
+
7
+ export interface Context<C extends Config = Config> extends BasicContext<C>
8
+ , ConfigResourceAppend {
9
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": [
3
+ "../tsconfig.default.json",
4
+ ],
5
+ "compilerOptions": {
6
+ "rootDir": "./src/", /* Specify the root folder within your source files. */
7
+ "outDir": "./build/", /* Specify an output folder for all emitted files. */
8
+ "moduleResolution": "Bundler"
9
+ },
10
+ "exclude": [
11
+ "./dist/**/*",
12
+ "./build/**/*",
13
+ "./*.ts"
14
+ ]
15
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/consts.ts","./src/errors.ts","./src/helper.ts","./src/i18n.ts","./src/index.ts","./src/modules.ts","./src/service.ts","./src/types.ts","./src/model/index.ts","./src/model/localization.ts","./src/model/plan.ts","./src/model/product.ts","./src/model/subscription.ts","./src/model/utils.ts","./src/utils/index.ts","./src/utils/types.ts"],"version":"5.6.3"}