@misterhomer1992/payment-manager 1.0.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 (89) hide show
  1. package/.editorconfig +7 -0
  2. package/.prettierrc +5 -0
  3. package/README.md +181 -0
  4. package/USAGE.md +400 -0
  5. package/dist/collections/payments.d.ts +6 -0
  6. package/dist/collections/payments.js +60 -0
  7. package/dist/collections/plans.d.ts +6 -0
  8. package/dist/collections/plans.js +50 -0
  9. package/dist/collections/subscriptions.d.ts +6 -0
  10. package/dist/collections/subscriptions.js +47 -0
  11. package/dist/collections/tokenPacks.d.ts +6 -0
  12. package/dist/collections/tokenPacks.js +42 -0
  13. package/dist/collections/userTokenBalances.d.ts +4 -0
  14. package/dist/collections/userTokenBalances.js +41 -0
  15. package/dist/config.d.ts +20 -0
  16. package/dist/config.js +14 -0
  17. package/dist/constants/defaultPlans.d.ts +2 -0
  18. package/dist/constants/defaultPlans.js +37 -0
  19. package/dist/index.d.ts +51 -0
  20. package/dist/index.js +88 -0
  21. package/dist/services/paymentService.d.ts +8 -0
  22. package/dist/services/paymentService.js +173 -0
  23. package/dist/services/planService.d.ts +7 -0
  24. package/dist/services/planService.js +69 -0
  25. package/dist/services/subscriptionService.d.ts +13 -0
  26. package/dist/services/subscriptionService.js +183 -0
  27. package/dist/services/tokenPackService.d.ts +15 -0
  28. package/dist/services/tokenPackService.js +80 -0
  29. package/dist/services/webhookService.d.ts +3 -0
  30. package/dist/services/webhookService.js +210 -0
  31. package/dist/types/errors.d.ts +31 -0
  32. package/dist/types/errors.js +74 -0
  33. package/dist/types/index.d.ts +7 -0
  34. package/dist/types/index.js +14 -0
  35. package/dist/types/orderReference.d.ts +18 -0
  36. package/dist/types/orderReference.js +2 -0
  37. package/dist/types/payment.d.ts +14 -0
  38. package/dist/types/payment.js +2 -0
  39. package/dist/types/plan.d.ts +10 -0
  40. package/dist/types/plan.js +2 -0
  41. package/dist/types/subscription.d.ts +12 -0
  42. package/dist/types/subscription.js +2 -0
  43. package/dist/types/tokenPack.d.ts +15 -0
  44. package/dist/types/tokenPack.js +2 -0
  45. package/dist/types/webhook.d.ts +32 -0
  46. package/dist/types/webhook.js +2 -0
  47. package/dist/utils/gracePeriod.d.ts +3 -0
  48. package/dist/utils/gracePeriod.js +19 -0
  49. package/dist/utils/orderReference.d.ts +3 -0
  50. package/dist/utils/orderReference.js +52 -0
  51. package/dist/utils/proration.d.ts +10 -0
  52. package/dist/utils/proration.js +32 -0
  53. package/dist/utils/webhookResponse.d.ts +2 -0
  54. package/dist/utils/webhookResponse.js +10 -0
  55. package/jest.config.ts +11 -0
  56. package/package.json +29 -0
  57. package/src/collections/payments.ts +61 -0
  58. package/src/collections/plans.ts +53 -0
  59. package/src/collections/subscriptions.ts +52 -0
  60. package/src/collections/tokenPacks.ts +42 -0
  61. package/src/collections/userTokenBalances.ts +46 -0
  62. package/src/config.ts +34 -0
  63. package/src/index.ts +129 -0
  64. package/src/services/deactivationCheckHandler.test.ts +248 -0
  65. package/src/services/paymentCheckHandler.test.ts +384 -0
  66. package/src/services/paymentService.ts +166 -0
  67. package/src/services/planService.ts +46 -0
  68. package/src/services/subscriptionService.ts +183 -0
  69. package/src/services/tokenPackService.ts +54 -0
  70. package/src/services/webhookService.ts +217 -0
  71. package/src/types/errors.ts +72 -0
  72. package/src/types/index.ts +18 -0
  73. package/src/types/orderReference.ts +19 -0
  74. package/src/types/payment.ts +14 -0
  75. package/src/types/plan.ts +10 -0
  76. package/src/types/subscription.ts +12 -0
  77. package/src/types/tokenPack.ts +16 -0
  78. package/src/types/webhook.ts +34 -0
  79. package/src/utils/gracePeriod.test.ts +46 -0
  80. package/src/utils/gracePeriod.ts +19 -0
  81. package/src/utils/orderReference.test.ts +149 -0
  82. package/src/utils/orderReference.ts +57 -0
  83. package/src/utils/proration.test.ts +133 -0
  84. package/src/utils/proration.ts +50 -0
  85. package/src/utils/webhookResponse.test.ts +57 -0
  86. package/src/utils/webhookResponse.ts +14 -0
  87. package/tasks/prd-payment-manager-library-product-requirements-document.md +447 -0
  88. package/tasks/prd.json +336 -0
  89. package/tsconfig.json +15 -0
package/tasks/prd.json ADDED
@@ -0,0 +1,336 @@
1
+ {
2
+ "name": "Payment Manager Library",
3
+ "branchName": "feature/payment-manager-library",
4
+ "userStories": [
5
+ {
6
+ "id": "US-001",
7
+ "title": "Project Scaffolding",
8
+ "description": "As a developer setting up the library, I want a properly configured TypeScript npm package, so that the library can be built, tested, and published.",
9
+ "acceptanceCriteria": [
10
+ "package.json with name payment-manager, proper main/types fields pointing to dist/",
11
+ "tsconfig.json targeting ES2020, module NodeNext, outDir dist/, strict mode enabled",
12
+ "firebase-admin listed as peerDependencies only (>=12.0.0)",
13
+ "@misterhomer1992/wayforpay-api as regular dependency (^3.0.1)",
14
+ "Dev dependencies: typescript, jest, ts-jest, @types/jest",
15
+ "jest.config.ts configured for ts-jest",
16
+ "npm scripts: build, test, prepublishOnly (runs build)",
17
+ ".gitignore excludes node_modules/, dist/"
18
+ ],
19
+ "priority": 1,
20
+ "passes": true,
21
+ "dependsOn": [],
22
+ "labels": [
23
+ "scaffolding",
24
+ "setup"
25
+ ],
26
+ "completionNotes": "Completed by agent"
27
+ },
28
+ {
29
+ "id": "US-002",
30
+ "title": "Type Definitions",
31
+ "description": "As a developer consuming the library, I want complete TypeScript type definitions for all entities and API surfaces, so that I get full type safety and autocompletion.",
32
+ "acceptanceCriteria": [
33
+ "src/types/plan.ts — SubscriptionPlanEntity type",
34
+ "src/types/subscription.ts — SubscriptionEntity type with pendingPlanId? and pendingPlanChangeDate? fields",
35
+ "src/types/payment.ts — PaymentEntity type with paymentLink? and paymentLinkCreatedAt? fields",
36
+ "src/types/tokenPack.ts — TokenPackEntity and UserTokenBalance types",
37
+ "src/types/webhook.ts — WebhookResponse, DeactivationResult, ServiceUrlRequestParams types",
38
+ "src/types/orderReference.ts — OrderReferenceParams and ParsedOrderReference types",
39
+ "src/types/index.ts — re-exports all types",
40
+ "src/types/errors.ts — PaymentManagerError base class plus PlanNotFoundError, PlanInactiveError, SubscriptionAlreadyActiveError, SubscriptionNotFoundError, TokenPackNotFoundError, TokenPackInactiveError, InvalidWebhookSecretError, PaymentProviderError, InvalidOrderReferenceError"
41
+ ],
42
+ "priority": 1,
43
+ "passes": true,
44
+ "dependsOn": [
45
+ "US-001"
46
+ ],
47
+ "labels": [
48
+ "types",
49
+ "typescript"
50
+ ],
51
+ "completionNotes": "Completed by agent"
52
+ },
53
+ {
54
+ "id": "US-003",
55
+ "title": "Config & Initialization",
56
+ "description": "As a developer consuming the library, I want to initialize the library with a config object and receive a typed PaymentManager interface, so that I can use all library functions with my own Firestore instance and Wayforpay credentials.",
57
+ "acceptanceCriteria": [
58
+ "src/config.ts — module-level config singleton with setConfig() and getConfig() functions",
59
+ "PaymentManagerConfig type includes firestore, wayforpay credentials, platform, optional webhookSecret, optional logger",
60
+ "src/index.ts — init(config) function that stores config and returns PaymentManager object",
61
+ "PaymentManager interface exposes plans, subscriptions, tokenPacks, userTokens, webhooks namespaces",
62
+ "All public types re-exported from src/index.ts"
63
+ ],
64
+ "priority": 1,
65
+ "passes": true,
66
+ "dependsOn": [
67
+ "US-001",
68
+ "US-002"
69
+ ],
70
+ "labels": [
71
+ "config",
72
+ "initialization"
73
+ ],
74
+ "completionNotes": "Completed by agent"
75
+ },
76
+ {
77
+ "id": "US-004",
78
+ "title": "Utility Functions",
79
+ "description": "As a developer working on the library internals, I want reliable utility functions for order references, proration, grace periods, and webhook responses, so that core business logic is correct and testable in isolation.",
80
+ "acceptanceCriteria": [
81
+ "src/utils/orderReference.ts — buildOrderReference(params) constructs order reference strings for subscriptions and token packs",
82
+ "src/utils/orderReference.ts — parseOrderReference(ref) extracts version, platform, userId, type, planId/tknId, date; strips _WFPREG-* suffix; throws InvalidOrderReferenceError on malformed input",
83
+ "src/utils/proration.ts — implements upgrade proration formula: calculates remainingRatio, remainingValue, newDailyRate, daysOnNewPlan, newExpiresAt",
84
+ "src/utils/proration.ts — cycleDays helper: daily=1, monthly=30, yearly=365",
85
+ "src/utils/gracePeriod.ts — getGracePeriod(regularMode): daily→6 hours, monthly→1 day, yearly→1 day",
86
+ "src/utils/gracePeriod.ts — getExpirationWithGrace(expiresAt, regularMode) adds grace period to expiration",
87
+ "src/utils/webhookResponse.ts — buildWebhookResponse returns { orderReference, status, time, signature } with HMAC-MD5 signature"
88
+ ],
89
+ "priority": 1,
90
+ "passes": true,
91
+ "dependsOn": [
92
+ "US-002"
93
+ ],
94
+ "labels": [
95
+ "utilities",
96
+ "business-logic"
97
+ ],
98
+ "completionNotes": "Completed by agent"
99
+ },
100
+ {
101
+ "id": "US-005",
102
+ "title": "Firestore Collection Modules",
103
+ "description": "As a developer working on the library internals, I want clean Firestore CRUD abstractions for each entity collection, so that services can read/write data without duplicating Firestore logic.",
104
+ "acceptanceCriteria": [
105
+ "src/collections/plans.ts — getById, getAll, create, update, upsert for subscriptionPlans collection",
106
+ "src/collections/subscriptions.ts — getActiveByUserId, getByUserId, create, update, getAllActiveAndCancelled for subscriptions collection",
107
+ "src/collections/payments.ts — create, update, findPendingByUserAndPlan, findPendingByUserAndTokenPack, findByOrderReference for payments collection",
108
+ "src/collections/tokenPacks.ts — getById, getAll, create, update, remove for tokenPacks collection",
109
+ "src/collections/userTokenBalances.ts — getByUserId (returns zero-balance default if not found), addTokens (atomic increment via FieldValue.increment), update for userTokenBalances collection"
110
+ ],
111
+ "priority": 2,
112
+ "passes": true,
113
+ "dependsOn": [
114
+ "US-002",
115
+ "US-003"
116
+ ],
117
+ "labels": [
118
+ "firestore",
119
+ "data-access"
120
+ ],
121
+ "completionNotes": "Completed by agent"
122
+ },
123
+ {
124
+ "id": "US-006",
125
+ "title": "Plan Service",
126
+ "description": "As a library consumer, I want to manage subscription plans (CRUD + seeding), so that I can define and configure what plans are available to users.",
127
+ "acceptanceCriteria": [
128
+ "seedDefaultPlans() writes 4 default plans (basic-daily, basic-monthly, pro-daily, pro-monthly) to Firestore via upsert",
129
+ "create(plan) creates a new plan with createdAt/updatedAt timestamps",
130
+ "getById(planId) returns plan or null",
131
+ "getAll() returns all plans",
132
+ "update(planId, data) updates plan fields with updatedAt timestamp",
133
+ "deactivate(planId) sets isActive: false",
134
+ "Default plans defined in src/constants/defaultPlans.ts",
135
+ "Logs via optional logger: plan created, plan updated, default plans seeded"
136
+ ],
137
+ "priority": 2,
138
+ "passes": true,
139
+ "dependsOn": [
140
+ "US-004",
141
+ "US-005"
142
+ ],
143
+ "labels": [
144
+ "service",
145
+ "plans"
146
+ ],
147
+ "completionNotes": "Completed by agent"
148
+ },
149
+ {
150
+ "id": "US-007",
151
+ "title": "Payment Service",
152
+ "description": "As a library consumer, I want payment records created and payment URLs cached, so that duplicate Wayforpay API calls are avoided within a 5-minute window.",
153
+ "acceptanceCriteria": [
154
+ "createSubscriptionPayment(userId, planId, currency, productName) — checks for cached pending payment within 5min, builds order reference, creates PaymentEntity, calls generateRegularPurchase, stores paymentLink + paymentLinkCreatedAt, returns { paymentUrl }",
155
+ "createTokenPackPayment(userId, packId, currency, productName, price) — same caching logic, builds token pack order reference, calls generatePurchase (one-time), returns { paymentUrl }",
156
+ "updatePaymentStatus(orderReference, status) updates payment by order reference",
157
+ "Logs via optional logger: payment created, cached URL returned"
158
+ ],
159
+ "priority": 2,
160
+ "passes": true,
161
+ "dependsOn": [
162
+ "US-004",
163
+ "US-005"
164
+ ],
165
+ "labels": [
166
+ "service",
167
+ "payments",
168
+ "wayforpay"
169
+ ],
170
+ "completionNotes": "Completed by agent"
171
+ },
172
+ {
173
+ "id": "US-008",
174
+ "title": "Subscription Service",
175
+ "description": "As a library consumer, I want to manage the full subscription lifecycle, so that users can subscribe, upgrade, downgrade, and cancel.",
176
+ "acceptanceCriteria": [
177
+ "create(params) — cancels existing active subscription if present, validates plan exists and isActive, delegates to payment service for URL, returns { paymentUrl }",
178
+ "expire(userId) — sets subscription status to 'expired'",
179
+ "deactivate(userId) — calls cancelRegularPurchase on Wayforpay, sets status to 'cancelled'",
180
+ "changePlan(userId, newPlanId) — upgrade: calculates prorated newExpiresAt, updates planId and expiresAt immediately, calls editRegularPurchase",
181
+ "changePlan(userId, newPlanId) — downgrade: sets pendingPlanId and pendingPlanChangeDate, calls editRegularPurchase, does NOT change planId immediately",
182
+ "changePlan(userId, newPlanId) — same price: throws error",
183
+ "getActiveByUserId(userId) returns active subscription or null",
184
+ "Logs via optional logger: subscription created, upgraded, downgraded, expired, cancelled"
185
+ ],
186
+ "priority": 2,
187
+ "passes": true,
188
+ "dependsOn": [
189
+ "US-004",
190
+ "US-005",
191
+ "US-006",
192
+ "US-007"
193
+ ],
194
+ "labels": [
195
+ "service",
196
+ "subscriptions"
197
+ ],
198
+ "completionNotes": "Completed by agent"
199
+ },
200
+ {
201
+ "id": "US-009",
202
+ "title": "Token Pack Service",
203
+ "description": "As a library consumer, I want to manage token packs and allow users to buy extra tokens, so that users can purchase additional credits beyond their subscription limits.",
204
+ "acceptanceCriteria": [
205
+ "create(pack) creates new token pack with auto-generated ID",
206
+ "remove(packId) deletes token pack",
207
+ "activate(packId) sets isActive: true",
208
+ "deactivate(packId) sets isActive: false",
209
+ "getAll() returns all token packs",
210
+ "buyExtraTokens(params) validates pack exists and isActive, delegates to payment service for one-time payment URL, returns { paymentUrl }",
211
+ "getBalance(userId) returns UserTokenBalance (zero-balance default if no record)",
212
+ "Logs via optional logger: token pack created, purchase initiated"
213
+ ],
214
+ "priority": 2,
215
+ "passes": true,
216
+ "dependsOn": [
217
+ "US-005",
218
+ "US-007"
219
+ ],
220
+ "labels": [
221
+ "service",
222
+ "tokens"
223
+ ],
224
+ "completionNotes": "Completed by agent"
225
+ },
226
+ {
227
+ "id": "US-010",
228
+ "title": "Payment Check Webhook Handler",
229
+ "description": "As a library consumer, I want to process Wayforpay PAYMENT_CHECK webhooks, so that subscriptions are activated/renewed and token purchases are fulfilled when payments succeed.",
230
+ "acceptanceCriteria": [
231
+ "handlePaymentCheck(reqBody, headers) validates webhook secret from x-secret-key header (config webhookSecret first, falls back to env var NOTIFY_TELEGRAM_USER_SECRET)",
232
+ "Parses request body: JSON.parse(Object.keys(reqBody)[0])",
233
+ "Only reason codes 1100 and 4100 are successful; otherwise returns reject response",
234
+ "Subscription payment (type=sub): creates new SubscriptionEntity on first payment, extends expiresAt on recurrent payment, applies pending downgrade when pendingPlanChangeDate <= now",
235
+ "Token pack payment (type=tkn): looks up TokenPackEntity, atomically increments UserTokenBalance.balance and totalPurchased",
236
+ "Updates PaymentEntity status to 'approved' for successful payments",
237
+ "Always returns signed WebhookResponse: { orderReference, status, time, signature } with HMAC-MD5",
238
+ "Logs via optional logger: webhook received, payment processed, subscription created/renewed, tokens added"
239
+ ],
240
+ "priority": 3,
241
+ "passes": true,
242
+ "dependsOn": [
243
+ "US-004",
244
+ "US-005",
245
+ "US-008",
246
+ "US-009"
247
+ ],
248
+ "labels": [
249
+ "webhook",
250
+ "payments"
251
+ ],
252
+ "completionNotes": "Completed by agent"
253
+ },
254
+ {
255
+ "id": "US-011",
256
+ "title": "Subscription Deactivation Check Handler",
257
+ "description": "As a library consumer, I want a cron-compatible handler that expires overdue subscriptions, so that subscriptions don't remain active after their grace period ends.",
258
+ "acceptanceCriteria": [
259
+ "handleSubscriptionDeactivationCheck(headers) validates webhook secret (same as US-010)",
260
+ "Queries all subscriptions where status IN ['active', 'cancelled']",
261
+ "For each subscription: fetches plan, computes expirationWithGrace, expires subscription if now > expirationWithGrace",
262
+ "Checks pending downgrades: if pendingPlanChangeDate <= now, applies downgrade",
263
+ "Returns DeactivationResult: { expired: string[], downgraded: string[] } (arrays of user IDs)",
264
+ "Logs via optional logger: subscriptions expired, downgrades applied"
265
+ ],
266
+ "priority": 3,
267
+ "passes": true,
268
+ "dependsOn": [
269
+ "US-004",
270
+ "US-005"
271
+ ],
272
+ "labels": [
273
+ "webhook",
274
+ "cron",
275
+ "expiration"
276
+ ],
277
+ "completionNotes": "Completed by agent"
278
+ },
279
+ {
280
+ "id": "US-012",
281
+ "title": "Wire Public API in index.ts",
282
+ "description": "As a library consumer, I want a single entry point that exposes the complete PaymentManager interface, so that I can import and use all functionality from one place.",
283
+ "acceptanceCriteria": [
284
+ "init(config) returns PaymentManager with namespaces: plans, subscriptions, tokenPacks, userTokens, webhooks",
285
+ "All public types exported from package root",
286
+ "Error classes exported from package root",
287
+ "Calling any method before init() throws a clear error"
288
+ ],
289
+ "priority": 3,
290
+ "passes": true,
291
+ "dependsOn": [
292
+ "US-006",
293
+ "US-007",
294
+ "US-008",
295
+ "US-009",
296
+ "US-010",
297
+ "US-011"
298
+ ],
299
+ "labels": [
300
+ "api",
301
+ "entry-point"
302
+ ],
303
+ "completionNotes": "Completed by agent"
304
+ },
305
+ {
306
+ "id": "US-013",
307
+ "title": "Unit Tests",
308
+ "description": "As a developer maintaining the library, I want comprehensive unit tests for core logic, so that business logic is verified and regressions are caught.",
309
+ "acceptanceCriteria": [
310
+ "orderReference.test.ts — builds correct sub/token order references, parses correctly, strips _WFPREG-* suffix, throws on malformed input",
311
+ "proration.test.ts — correct prorated expiration mid-cycle, edge cases for first/last day, works with daily/monthly/yearly modes",
312
+ "gracePeriod.test.ts — returns 6h for daily, 1d for monthly, 1d for yearly, getExpirationWithGrace adds correct duration",
313
+ "webhookResponse.test.ts — correct response structure, valid HMAC-MD5 signature",
314
+ "paymentCheckHandler.test.ts — rejects invalid secret, rejects bad reason codes, creates new subscription, renews existing, applies pending downgrade, adds tokens, returns signed response",
315
+ "deactivationCheckHandler.test.ts — rejects invalid secret, expires subs past grace, keeps subs within grace, applies pending downgrades",
316
+ "All tests pass with npm test",
317
+ "Zero type errors with npx tsc --noEmit"
318
+ ],
319
+ "priority": 3,
320
+ "passes": true,
321
+ "dependsOn": [
322
+ "US-004",
323
+ "US-010",
324
+ "US-011"
325
+ ],
326
+ "labels": [
327
+ "testing",
328
+ "unit-tests"
329
+ ],
330
+ "completionNotes": "Completed by agent"
331
+ }
332
+ ],
333
+ "metadata": {
334
+ "updatedAt": "2026-02-06T14:29:35.896Z"
335
+ }
336
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist/",
7
+ "declaration": true,
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
15
+ }