node-behind-api-client 2.0.48

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 (157) hide show
  1. package/.gitlab-ci.yml +20 -0
  2. package/README.md +65 -0
  3. package/docs/behind-api-client/easyjob/JobDescriptions/README.md +654 -0
  4. package/docs/behind-api-client/easyjob/README.md +647 -0
  5. package/docs/behind-api-client/easyjob/applicants/README.md +494 -0
  6. package/docs/behind-api-client/easyjob/applications/README.md +754 -0
  7. package/docs/behind-api-client/easyjob/candidateProfiles/README.md +940 -0
  8. package/docs/behind-api-client/easyjob/jd-candidate-questions/README.md +372 -0
  9. package/docs/behind-api-client/payments/payture/README.21.md +901 -0
  10. package/docs/behind-api-client/payments/payture/README.cards.md +1497 -0
  11. package/docs/behind-api-client/payments/payture/README.md +1497 -0
  12. package/docs/behind-api-client/payments/payture/README.rukitchen.md +396 -0
  13. package/docs/behind-api-client/payments/payture/README.subscriptions.md +1266 -0
  14. package/docs/behind-api-client/payments/stripe/README.flow.md +254 -0
  15. package/docs/behind-api-client/rag/storage/README.md +519 -0
  16. package/example.js +35 -0
  17. package/index.cjs +14 -0
  18. package/index.js +15 -0
  19. package/lib/behind-api-auth-client/BehindApiAuthClient.js +91 -0
  20. package/lib/behind-api-auth-client/authorisation/AuthorisationApp.js +9 -0
  21. package/lib/behind-api-auth-client/authorisation/AuthorisationV10.js +9 -0
  22. package/lib/behind-api-auth-client/authorisation/AuthorisationV10Code.js +30 -0
  23. package/lib/behind-api-auth-client/example.js +47 -0
  24. package/lib/behind-api-auth-client/package.json +9 -0
  25. package/lib/behind-api-client/BehindApiClient.js +137 -0
  26. package/lib/behind-api-client/chat/ChatApp.js +11 -0
  27. package/lib/behind-api-client/chat/ChatV10.js +13 -0
  28. package/lib/behind-api-client/chat/ChatV10Chat.js +87 -0
  29. package/lib/behind-api-client/chat/ChatV10Chats.js +14 -0
  30. package/lib/behind-api-client/chat/ChatV10Message.js +57 -0
  31. package/lib/behind-api-client/chat/ChatV20.js +11 -0
  32. package/lib/behind-api-client/chat/ChatV20Chat.js +14 -0
  33. package/lib/behind-api-client/chat/ChatV20Message.js +27 -0
  34. package/lib/behind-api-client/easyjob/EasyjobApp.js +9 -0
  35. package/lib/behind-api-client/easyjob/EasyjobV10.js +31 -0
  36. package/lib/behind-api-client/easyjob/EasyjobV10Answers.js +16 -0
  37. package/lib/behind-api-client/easyjob/EasyjobV10Applicants.js +29 -0
  38. package/lib/behind-api-client/easyjob/EasyjobV10Applications.js +39 -0
  39. package/lib/behind-api-client/easyjob/EasyjobV10CandidateProfileArtifacts.js +31 -0
  40. package/lib/behind-api-client/easyjob/EasyjobV10CandidateProfiles.js +99 -0
  41. package/lib/behind-api-client/easyjob/EasyjobV10Companies.js +36 -0
  42. package/lib/behind-api-client/easyjob/EasyjobV10Cv.js +15 -0
  43. package/lib/behind-api-client/easyjob/EasyjobV10Departments.js +37 -0
  44. package/lib/behind-api-client/easyjob/EasyjobV10JobDescriptionArtifacts.js +29 -0
  45. package/lib/behind-api-client/easyjob/EasyjobV10JobDescriptionCandidateQuestions.js +63 -0
  46. package/lib/behind-api-client/easyjob/EasyjobV10JobDescriptions.js +93 -0
  47. package/lib/behind-api-client/easyjob/EasyjobV10Reports.js +35 -0
  48. package/lib/behind-api-client/example.js +47 -0
  49. package/lib/behind-api-client/global/GlobalApp.js +9 -0
  50. package/lib/behind-api-client/global/GlobalV10.js +11 -0
  51. package/lib/behind-api-client/global/GlobalV10Sockets.js +16 -0
  52. package/lib/behind-api-client/global/GlobalV10Storage.js +29 -0
  53. package/lib/behind-api-client/gpt/GptApp.js +15 -0
  54. package/lib/behind-api-client/gpt/GptV10.js +15 -0
  55. package/lib/behind-api-client/gpt/GptV10Prompt.js +17 -0
  56. package/lib/behind-api-client/gpt/GptV10Request.js +16 -0
  57. package/lib/behind-api-client/gpt/GptV10Storedprompts.js +14 -0
  58. package/lib/behind-api-client/gpt/GptV10Whisper.js +23 -0
  59. package/lib/behind-api-client/gpt/GptV20.js +9 -0
  60. package/lib/behind-api-client/gpt/GptV20Prompt.js +15 -0
  61. package/lib/behind-api-client/gpt/GptV30.js +13 -0
  62. package/lib/behind-api-client/gpt/GptV30Chat.js +19 -0
  63. package/lib/behind-api-client/gpt/GptV30Prompt.js +41 -0
  64. package/lib/behind-api-client/gpt/GptV30Prompts.js +32 -0
  65. package/lib/behind-api-client/gpt/GptV40.js +11 -0
  66. package/lib/behind-api-client/gpt/GptV40Prompt.js +24 -0
  67. package/lib/behind-api-client/gpt/GptV40Prompts.js +30 -0
  68. package/lib/behind-api-client/mailer/MailerApp.js +11 -0
  69. package/lib/behind-api-client/mailer/MailerV10.js +15 -0
  70. package/lib/behind-api-client/mailer/MailerV10Bulk.js +21 -0
  71. package/lib/behind-api-client/mailer/MailerV10Message.js +83 -0
  72. package/lib/behind-api-client/mailer/MailerV10Settings.js +44 -0
  73. package/lib/behind-api-client/mailer/MailerV10Template.js +23 -0
  74. package/lib/behind-api-client/mailer/MailerV20.js +9 -0
  75. package/lib/behind-api-client/mailer/MailerV20Message.js +21 -0
  76. package/lib/behind-api-client/mastogram/MastogramApp.js +9 -0
  77. package/lib/behind-api-client/mastogram/MastogramV10.js +13 -0
  78. package/lib/behind-api-client/mastogram/MastogramV10Bluesky.js +42 -0
  79. package/lib/behind-api-client/mastogram/MastogramV10Mastodon.js +45 -0
  80. package/lib/behind-api-client/mastogram/MastogramV10Telegram.js +39 -0
  81. package/lib/behind-api-client/monitor/MonitorApp.js +9 -0
  82. package/lib/behind-api-client/monitor/MonitorV10.js +13 -0
  83. package/lib/behind-api-client/monitor/MonitorV10Finances.js +39 -0
  84. package/lib/behind-api-client/monitor/MonitorV10Record.js +15 -0
  85. package/lib/behind-api-client/monitor/MonitorV10Records.js +22 -0
  86. package/lib/behind-api-client/oauth/OauthApp.js +9 -0
  87. package/lib/behind-api-client/oauth/OauthV10.js +9 -0
  88. package/lib/behind-api-client/oauth/OauthV10Authorisation.js +15 -0
  89. package/lib/behind-api-client/package.json +9 -0
  90. package/lib/behind-api-client/payments/PaymentsApp.js +13 -0
  91. package/lib/behind-api-client/payments/PaymentsV10.js +15 -0
  92. package/lib/behind-api-client/payments/PaymentsV10Gift.js +32 -0
  93. package/lib/behind-api-client/payments/PaymentsV10Payture.js +30 -0
  94. package/lib/behind-api-client/payments/PaymentsV10Product.js +15 -0
  95. package/lib/behind-api-client/payments/PaymentsV10Telegram.js +44 -0
  96. package/lib/behind-api-client/payments/PaymentsV20.js +9 -0
  97. package/lib/behind-api-client/payments/PaymentsV20Payture.js +32 -0
  98. package/lib/behind-api-client/payments/PaymentsV21.js +15 -0
  99. package/lib/behind-api-client/payments/PaymentsV21Cards.js +14 -0
  100. package/lib/behind-api-client/payments/PaymentsV21Payture.js +29 -0
  101. package/lib/behind-api-client/payments/PaymentsV21Stripe.js +28 -0
  102. package/lib/behind-api-client/payments/PaymentsV21Subscriptions.js +21 -0
  103. package/lib/behind-api-client/questionnaire/QuestionnaireApp.js +9 -0
  104. package/lib/behind-api-client/questionnaire/QuestionnaireV10.js +9 -0
  105. package/lib/behind-api-client/questionnaire/QuestionnaireV10Form.js +22 -0
  106. package/lib/behind-api-client/raet/RaetApp.js +11 -0
  107. package/lib/behind-api-client/raet/RaetV10.js +21 -0
  108. package/lib/behind-api-client/raet/RaetV10Cv.js +87 -0
  109. package/lib/behind-api-client/raet/RaetV10Individual.js +43 -0
  110. package/lib/behind-api-client/raet/RaetV10Individuals.js +38 -0
  111. package/lib/behind-api-client/raet/RaetV10Jd.js +47 -0
  112. package/lib/behind-api-client/raet/RaetV10Project.js +61 -0
  113. package/lib/behind-api-client/raet/RaetV10Projects.js +14 -0
  114. package/lib/behind-api-client/raet/RaetV10Report.js +39 -0
  115. package/lib/behind-api-client/raet/RaetV20.js +11 -0
  116. package/lib/behind-api-client/raet/RaetV20Cv.js +31 -0
  117. package/lib/behind-api-client/raet/RaetV20Individuals.js +25 -0
  118. package/lib/behind-api-client/rag/RagApp.js +9 -0
  119. package/lib/behind-api-client/rag/RagV10.js +9 -0
  120. package/lib/behind-api-client/rag/RagV10Storage.js +27 -0
  121. package/lib/behind-api-client/ruKitchen/RuKitchenApp.js +9 -0
  122. package/lib/behind-api-client/ruKitchen/RuKitchenV10.js +11 -0
  123. package/lib/behind-api-client/ruKitchen/RuKitchenV10Importer.js +29 -0
  124. package/lib/behind-api-client/ruKitchen/RuKitchenV10SeoArticle.js +14 -0
  125. package/lib/behind-api-client/sales/SalesApp.js +11 -0
  126. package/lib/behind-api-client/sales/SalesV10.js +23 -0
  127. package/lib/behind-api-client/sales/SalesV10Catalogue.js +58 -0
  128. package/lib/behind-api-client/sales/SalesV10Categories.js +15 -0
  129. package/lib/behind-api-client/sales/SalesV10Companies.js +55 -0
  130. package/lib/behind-api-client/sales/SalesV10Company.js +120 -0
  131. package/lib/behind-api-client/sales/SalesV10Group.js +70 -0
  132. package/lib/behind-api-client/sales/SalesV10Groups.js +21 -0
  133. package/lib/behind-api-client/sales/SalesV10Logs.js +14 -0
  134. package/lib/behind-api-client/sales/SalesV10Notes.js +38 -0
  135. package/lib/behind-api-client/sales/SalesV20.js +11 -0
  136. package/lib/behind-api-client/sales/SalesV20Companies.js +26 -0
  137. package/lib/behind-api-client/sales/SalesV20Notes.js +17 -0
  138. package/lib/behind-api-client/sip/SipApp.js +9 -0
  139. package/lib/behind-api-client/sip/SipV10.js +15 -0
  140. package/lib/behind-api-client/sip/SipV10Call.js +83 -0
  141. package/lib/behind-api-client/sip/SipV10Phone.js +44 -0
  142. package/lib/behind-api-client/sip/SipV10Transcript.js +21 -0
  143. package/lib/behind-api-client/sip/SipV10Transcripts.js +21 -0
  144. package/lib/behind-api-client/storage/StorageApp.js +9 -0
  145. package/lib/behind-api-client/storage/StorageV10.js +11 -0
  146. package/lib/behind-api-client/storage/StorageV10File.js +35 -0
  147. package/lib/behind-api-client/storage/StorageV10Upload.js +21 -0
  148. package/lib/behind-api-client/tests/TestsApp.js +9 -0
  149. package/lib/behind-api-client/tests/TestsV10.js +15 -0
  150. package/lib/behind-api-client/tests/TestsV10Cases.js +29 -0
  151. package/lib/behind-api-client/tests/TestsV10CasesExtended.js +14 -0
  152. package/lib/behind-api-client/tests/TestsV10Core.js +14 -0
  153. package/lib/behind-api-client/tests/TestsV10Mail.js +14 -0
  154. package/lib/behind-api-client/tools/ToolsApp.js +9 -0
  155. package/lib/behind-api-client/tools/ToolsV10.js +9 -0
  156. package/lib/behind-api-client/tools/ToolsV10Pdf.js +30 -0
  157. package/package.json +25 -0
@@ -0,0 +1,1266 @@
1
+ # Payments API V21 - Subscriptions Endpoint
2
+
3
+ This document provides detailed information about the Subscriptions management methods for the V21 Payments API.
4
+
5
+ ## Overview
6
+
7
+ The Subscriptions Payments API V21 provides methods for retrieving and managing user subscription instances. This includes fetching active subscriptions with their payment methods, billing details, status information, and scheduling data.
8
+
9
+ ---
10
+
11
+ ## Payments API V21 - Subscriptions Methods
12
+
13
+ ### behindAPI.V21.payments.subscriptions.getList()
14
+ Retrieves all subscription instances for the authenticated user, including payment methods, amounts, currencies, intervals, and status information.
15
+
16
+ **Parameters:**
17
+ - None (uses authenticated user session)
18
+
19
+ **Authorization:**
20
+ - Required: Yes (uses `req.userSession.UserId`)
21
+
22
+ **Usage:**
23
+ ```javascript
24
+ const result = await behindAPI.V21.payments.subscriptions.getList();
25
+
26
+ // Success Response (with subscriptions):
27
+ {
28
+ success: true,
29
+ data: [
30
+ {
31
+ subscription_instance_id: "cc0e8400-e29b-41d4-a716-446655440009",
32
+ subscription_key: "premium-monthly",
33
+ payment_method: "payture",
34
+ subscription_amount: 3200,
35
+ subscription_currency: "RUB",
36
+ subscription_interval: 2592000,
37
+ instance_last_attempt_time: "2024-11-24T09:24:39.880Z",
38
+ instance_enabled: true,
39
+ instance_active: true,
40
+ instance_created_at: "2024-11-01T10:00:00.000Z",
41
+ next_billing_date: "2024-12-01T10:00:00.000Z"
42
+ }
43
+ ]
44
+ }
45
+
46
+ // Success Response (no subscriptions):
47
+ {
48
+ success: true,
49
+ data: []
50
+ }
51
+
52
+ // Error Response:
53
+ {
54
+ success: false,
55
+ message: "Can't finish request",
56
+ e: { /* error details */ }
57
+ }
58
+ ```
59
+
60
+ ### behindAPI.V21.payments.subscriptions.subscribe(subscriptionId)
61
+ Creates a new subscription instance for the authenticated user, linking them to a specific subscription plan and establishing recurring billing.
62
+
63
+ **Parameters:**
64
+ - `subscriptionId` (string, required) - The subscription plan ID/key to create instance for
65
+
66
+ **Authorization:**
67
+ - Required: Yes (uses `req.userSession.UserId`)
68
+
69
+ **Usage:**
70
+ ```javascript
71
+ const result = await behindAPI.V21.payments.subscriptions.subscribe(
72
+ "premium-monthly"
73
+ );
74
+
75
+ // Success Response:
76
+ {
77
+ success: true,
78
+ data: {
79
+ subscription_instance_id: "sub_inst_1234567890abcdef"
80
+ }
81
+ }
82
+
83
+ // Error Response (failed to create):
84
+ {
85
+ success: false,
86
+ message: "Failed to create subscription instance"
87
+ }
88
+
89
+ // Error Response (general error):
90
+ {
91
+ success: false,
92
+ message: "Can't finish request",
93
+ e: { /* error details */ }
94
+ }
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Data Structure Reference
100
+
101
+ ### Subscription Instance Object
102
+ Each subscription instance in the response contains:
103
+
104
+ **Identification:**
105
+ - `subscription_instance_id` (UUID string) - Unique identifier for this subscription instance
106
+ - `subscription_key` (string) - The subscription plan identifier (e.g., "premium-monthly", "basic-annual")
107
+
108
+ **Payment Information:**
109
+ - `payment_method` (string) - Payment gateway type ("payture", "stripe", "yookassa")
110
+ - `subscription_amount` (number) - Subscription cost in minor currency units (kopecks/cents)
111
+ - `subscription_currency` (string) - ISO currency code (e.g., "RUB", "USD", "EUR")
112
+
113
+ **Billing Schedule:**
114
+ - `subscription_interval` (number) - Billing interval in seconds
115
+ - 2,592,000 = 30 days (monthly)
116
+ - 7,776,000 = 90 days (quarterly)
117
+ - 15,552,000 = 180 days (semi-annual)
118
+ - 31,536,000 = 365 days (annual)
119
+
120
+ **Status Information:**
121
+ - `instance_enabled` (boolean) - Whether subscription is enabled for billing
122
+ - `instance_active` (boolean) - Whether subscription is currently active
123
+ - `instance_last_attempt_time` (ISO datetime string) - Last payment attempt timestamp
124
+ - `instance_created_at` (ISO datetime string) - When subscription was created
125
+ - `next_billing_date` (ISO datetime string) - Calculated next billing date
126
+
127
+ **Conversion Helpers:**
128
+ ```javascript
129
+ // Amount conversion
130
+ const rubles = subscription_amount / 100; // 3200 → 32.00 RUB
131
+
132
+ // Interval conversion
133
+ const days = subscription_interval / 86400; // 2592000 → 30 days
134
+ const months = days / 30; // Approximate months
135
+ ```
136
+
137
+ ### Subscription Creation Response
138
+ The `subscribe()` method returns:
139
+
140
+ **Success Response:**
141
+ - `success` (boolean) - Always `true` for successful requests
142
+ - `data` (object) - Container for response data
143
+ - `subscription_instance_id` (UUID string) - Created subscription instance unique identifier
144
+
145
+ **Error Response:**
146
+ - `success` (boolean) - Always `false` for failed requests
147
+ - `message` (string) - Error description
148
+ - "Failed to create subscription instance" - Database returned null/false
149
+ - "Can't finish request" - General processing error
150
+ - `e` (object, optional) - Error details
151
+
152
+ ---
153
+
154
+ ## Complete Usage Examples
155
+
156
+ ### Basic Subscription List Retrieval
157
+
158
+ ```javascript
159
+ // Example: Get and display user's subscriptions
160
+ async function getUserSubscriptions() {
161
+ try {
162
+ console.log("Fetching user's subscriptions...");
163
+
164
+ const result = await behindAPI.V21.payments.subscriptions.getList();
165
+
166
+ if (result.success) {
167
+ if (result.data.length === 0) {
168
+ console.log("No active subscriptions found");
169
+ return [];
170
+ }
171
+
172
+ console.log(`✓ Found ${result.data.length} subscription(s)`);
173
+
174
+ result.data.forEach((sub, index) => {
175
+ console.log(`\nSubscription ${index + 1}:`);
176
+ console.log(` Plan: ${sub.subscription_key}`);
177
+ console.log(` Amount: ${sub.subscription_amount / 100} ${sub.subscription_currency}`);
178
+ console.log(` Status: ${sub.instance_active ? 'Active' : 'Inactive'}`);
179
+ console.log(` Next billing: ${new Date(sub.next_billing_date).toLocaleDateString()}`);
180
+ });
181
+
182
+ return result.data;
183
+ } else {
184
+ console.error(`✗ Failed to fetch subscriptions: ${result.message}`);
185
+ return [];
186
+ }
187
+ } catch (error) {
188
+ console.error("Error fetching subscriptions:", error);
189
+ return [];
190
+ }
191
+ }
192
+
193
+ // Usage
194
+ const subscriptions = await getUserSubscriptions();
195
+ ```
196
+
197
+ ### Create a New Subscription
198
+
199
+ ```javascript
200
+ // Example: Create a new subscription for the authenticated user
201
+ async function createSubscription(planKey) {
202
+ try {
203
+ console.log(`Creating subscription for plan: ${planKey}`);
204
+
205
+ const result = await behindAPI.V21.payments.subscriptions.subscribe(planKey);
206
+
207
+ if (result.success) {
208
+ console.log("✓ Subscription created successfully!");
209
+ console.log(`Subscription Instance ID: ${result.data.subscription_instance_id}`);
210
+
211
+ return result.data.subscription_instance_id;
212
+ } else {
213
+ console.error(`✗ Failed to create subscription: ${result.message}`);
214
+ return null;
215
+ }
216
+ } catch (error) {
217
+ console.error("Error creating subscription:", error);
218
+ return null;
219
+ }
220
+ }
221
+
222
+ // Usage
223
+ const subscriptionId = await createSubscription("premium-monthly");
224
+ ```
225
+
226
+ ### Complete Subscription Flow (Card Registration + Creation)
227
+
228
+ ```javascript
229
+ // Example: Full flow from card registration to subscription creation
230
+ async function completeSubscriptionFlow(planKey) {
231
+ try {
232
+ console.log("Starting subscription flow...");
233
+
234
+ // Step 1: Check if user has a payment method
235
+ const cardsResult = await behindAPI.V21.payments.cards.getList();
236
+
237
+ if (!cardsResult.success || cardsResult.data.length === 0) {
238
+ console.log("No payment method found. Initiating card registration...");
239
+
240
+ // Initialize card registration
241
+ const initResult = await behindAPI.V21.payments.payture.init();
242
+
243
+ if (initResult.success) {
244
+ // Store pending subscription for after card registration
245
+ sessionStorage.setItem('pending_subscription', planKey);
246
+ sessionStorage.setItem('card_registration_pending', 'true');
247
+
248
+ // Redirect to Payture
249
+ window.location.href = initResult.data.RedirectUrl;
250
+ return;
251
+ } else {
252
+ console.error("Failed to initialize card registration");
253
+ return null;
254
+ }
255
+ }
256
+
257
+ console.log("Payment method found. Creating subscription...");
258
+
259
+ // Step 2: Create subscription
260
+ const subscriptionResult = await behindAPI.V21.payments.subscriptions.subscribe(planKey);
261
+
262
+ if (subscriptionResult.success) {
263
+ console.log("✓ Subscription created successfully!");
264
+ console.log(`Subscription ID: ${subscriptionResult.data.subscription_instance_id}`);
265
+
266
+ return subscriptionResult.data.subscription_instance_id;
267
+ } else {
268
+ console.error(`Failed to create subscription: ${subscriptionResult.message}`);
269
+ return null;
270
+ }
271
+
272
+ } catch (error) {
273
+ console.error("Error in subscription flow:", error);
274
+ return null;
275
+ }
276
+ }
277
+
278
+ // Usage
279
+ const subscriptionId = await completeSubscriptionFlow("premium-monthly");
280
+
281
+ // In callback page after card registration:
282
+ async function handleCardRegistrationCallback() {
283
+ const params = new URLSearchParams(window.location.search);
284
+ const success = params.get('result');
285
+
286
+ if (success === 'True') {
287
+ // Finalize card registration
288
+ const finalizeResult = await behindAPI.V21.payments.payture.finalise();
289
+
290
+ if (finalizeResult.success) {
291
+ console.log("Card registered successfully!");
292
+
293
+ // Create pending subscription
294
+ const planKey = sessionStorage.getItem('pending_subscription');
295
+
296
+ if (planKey) {
297
+ const subscriptionResult = await behindAPI.V21.payments.subscriptions.subscribe(planKey);
298
+
299
+ if (subscriptionResult.success) {
300
+ console.log("Subscription created!");
301
+ sessionStorage.removeItem('pending_subscription');
302
+ sessionStorage.removeItem('card_registration_pending');
303
+ window.location.href = '/subscription-success';
304
+ } else {
305
+ console.error("Failed to create subscription");
306
+ window.location.href = '/error';
307
+ }
308
+ }
309
+ }
310
+ }
311
+ }
312
+ ```
313
+
314
+ ### Display Subscriptions Dashboard
315
+
316
+ ```javascript
317
+ // Example: Create a subscription management dashboard
318
+ class SubscriptionDashboard {
319
+ constructor(containerId) {
320
+ this.container = document.getElementById(containerId);
321
+ this.subscriptions = [];
322
+ }
323
+
324
+ async loadSubscriptions() {
325
+ try {
326
+ const result = await behindAPI.V21.payments.subscriptions.getList();
327
+
328
+ if (!result.success) {
329
+ this.showError("Failed to load subscriptions");
330
+ return;
331
+ }
332
+
333
+ this.subscriptions = result.data;
334
+
335
+ if (this.subscriptions.length === 0) {
336
+ this.showNoSubscriptions();
337
+ return;
338
+ }
339
+
340
+ this.renderDashboard();
341
+
342
+ } catch (error) {
343
+ console.error("Error loading subscriptions:", error);
344
+ this.showError("An error occurred while loading subscriptions");
345
+ }
346
+ }
347
+
348
+ renderDashboard() {
349
+ const html = `
350
+ <div class="subscription-dashboard">
351
+ <h2>Your Subscriptions</h2>
352
+ ${this.renderSummary()}
353
+ ${this.renderSubscriptionList()}
354
+ </div>
355
+ `;
356
+
357
+ this.container.innerHTML = html;
358
+ }
359
+
360
+ renderSummary() {
361
+ const stats = this.calculateStats();
362
+
363
+ return `
364
+ <div class="subscription-summary">
365
+ <div class="stat">
366
+ <label>Active Subscriptions</label>
367
+ <value>${stats.active}</value>
368
+ </div>
369
+ <div class="stat">
370
+ <label>Monthly Cost</label>
371
+ <value>${stats.monthlyCost} ${stats.currency}</value>
372
+ </div>
373
+ <div class="stat">
374
+ <label>Next Payment</label>
375
+ <value>${stats.nextPayment}</value>
376
+ </div>
377
+ </div>
378
+ `;
379
+ }
380
+
381
+ renderSubscriptionList() {
382
+ return `
383
+ <div class="subscription-list">
384
+ ${this.subscriptions.map(sub => this.renderSubscription(sub)).join('')}
385
+ </div>
386
+ `;
387
+ }
388
+
389
+ renderSubscription(sub) {
390
+ const amount = (sub.subscription_amount / 100).toFixed(2);
391
+ const interval = this.formatInterval(sub.subscription_interval);
392
+ const nextBilling = new Date(sub.next_billing_date).toLocaleDateString();
393
+ const isActive = sub.instance_active && sub.instance_enabled;
394
+
395
+ return `
396
+ <div class="subscription-card ${isActive ? 'active' : 'inactive'}">
397
+ <div class="subscription-header">
398
+ <h3>${this.formatSubscriptionName(sub.subscription_key)}</h3>
399
+ <span class="badge ${isActive ? 'badge-success' : 'badge-inactive'}">
400
+ ${isActive ? 'Active' : 'Inactive'}
401
+ </span>
402
+ </div>
403
+ <div class="subscription-details">
404
+ <div class="detail">
405
+ <span class="label">Amount:</span>
406
+ <span class="value">${amount} ${sub.subscription_currency}</span>
407
+ </div>
408
+ <div class="detail">
409
+ <span class="label">Billing:</span>
410
+ <span class="value">${interval}</span>
411
+ </div>
412
+ <div class="detail">
413
+ <span class="label">Payment Method:</span>
414
+ <span class="value">${this.formatPaymentMethod(sub.payment_method)}</span>
415
+ </div>
416
+ <div class="detail">
417
+ <span class="label">Next Billing:</span>
418
+ <span class="value">${nextBilling}</span>
419
+ </div>
420
+ </div>
421
+ <div class="subscription-actions">
422
+ ${isActive ?
423
+ `<button onclick="dashboard.cancelSubscription('${sub.subscription_instance_id}')">
424
+ Cancel Subscription
425
+ </button>` :
426
+ `<button onclick="dashboard.renewSubscription('${sub.subscription_instance_id}')">
427
+ Renew Subscription
428
+ </button>`
429
+ }
430
+ </div>
431
+ </div>
432
+ `;
433
+ }
434
+
435
+ calculateStats() {
436
+ const active = this.subscriptions.filter(s => s.instance_active).length;
437
+
438
+ // Calculate monthly equivalent cost
439
+ let monthlyCost = 0;
440
+ let currency = 'RUB';
441
+
442
+ this.subscriptions.forEach(sub => {
443
+ if (sub.instance_active) {
444
+ const amount = sub.subscription_amount / 100;
445
+ const months = sub.subscription_interval / 2592000; // Convert to months
446
+ monthlyCost += amount / months;
447
+ currency = sub.subscription_currency;
448
+ }
449
+ });
450
+
451
+ // Find next payment date
452
+ const nextPaymentDates = this.subscriptions
453
+ .filter(s => s.instance_active)
454
+ .map(s => new Date(s.next_billing_date));
455
+
456
+ const nextPayment = nextPaymentDates.length > 0
457
+ ? new Date(Math.min(...nextPaymentDates)).toLocaleDateString()
458
+ : 'N/A';
459
+
460
+ return {
461
+ active,
462
+ monthlyCost: monthlyCost.toFixed(2),
463
+ currency,
464
+ nextPayment
465
+ };
466
+ }
467
+
468
+ formatInterval(seconds) {
469
+ const days = seconds / 86400;
470
+
471
+ if (days === 30) return 'Monthly';
472
+ if (days === 90) return 'Quarterly';
473
+ if (days === 180) return 'Semi-Annual';
474
+ if (days === 365) return 'Annual';
475
+
476
+ return `Every ${Math.round(days)} days`;
477
+ }
478
+
479
+ formatSubscriptionName(key) {
480
+ return key
481
+ .split('-')
482
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
483
+ .join(' ');
484
+ }
485
+
486
+ formatPaymentMethod(method) {
487
+ const methods = {
488
+ payture: 'Payture Card',
489
+ stripe: 'Stripe Card',
490
+ yookassa: 'YooKassa'
491
+ };
492
+ return methods[method] || method;
493
+ }
494
+
495
+ showNoSubscriptions() {
496
+ this.container.innerHTML = `
497
+ <div class="no-subscriptions">
498
+ <h2>No Active Subscriptions</h2>
499
+ <p>You don't have any active subscriptions yet.</p>
500
+ <button onclick="window.location.href='/subscribe'">
501
+ Browse Plans
502
+ </button>
503
+ </div>
504
+ `;
505
+ }
506
+
507
+ showError(message) {
508
+ this.container.innerHTML = `
509
+ <div class="error-message">
510
+ <p>⚠️ ${message}</p>
511
+ <button onclick="dashboard.loadSubscriptions()">Retry</button>
512
+ </div>
513
+ `;
514
+ }
515
+
516
+ async cancelSubscription(subscriptionId) {
517
+ // Implement cancellation logic
518
+ console.log(`Canceling subscription: ${subscriptionId}`);
519
+ }
520
+
521
+ async renewSubscription(subscriptionId) {
522
+ // Implement renewal logic
523
+ console.log(`Renewing subscription: ${subscriptionId}`);
524
+ }
525
+ }
526
+
527
+ // Initialize dashboard
528
+ const dashboard = new SubscriptionDashboard('subscription-container');
529
+ dashboard.loadSubscriptions();
530
+ ```
531
+
532
+ ### Filter and Analyze Subscriptions
533
+
534
+ ```javascript
535
+ // Example: Analyze user's subscription data
536
+ class SubscriptionAnalyzer {
537
+ constructor(subscriptions) {
538
+ this.subscriptions = subscriptions;
539
+ }
540
+
541
+ getActiveSubscriptions() {
542
+ return this.subscriptions.filter(sub =>
543
+ sub.instance_active && sub.instance_enabled
544
+ );
545
+ }
546
+
547
+ getInactiveSubscriptions() {
548
+ return this.subscriptions.filter(sub =>
549
+ !sub.instance_active || !sub.instance_enabled
550
+ );
551
+ }
552
+
553
+ getSubscriptionsByPaymentMethod(method) {
554
+ return this.subscriptions.filter(sub =>
555
+ sub.payment_method === method
556
+ );
557
+ }
558
+
559
+ getTotalMonthlySpend() {
560
+ let total = 0;
561
+
562
+ this.getActiveSubscriptions().forEach(sub => {
563
+ const amount = sub.subscription_amount / 100;
564
+ const intervalMonths = sub.subscription_interval / 2592000;
565
+ total += amount / intervalMonths;
566
+ });
567
+
568
+ return total;
569
+ }
570
+
571
+ getAnnualSpend() {
572
+ return this.getTotalMonthlySpend() * 12;
573
+ }
574
+
575
+ getUpcomingPayments(days = 30) {
576
+ const now = Date.now();
577
+ const futureDate = now + (days * 24 * 60 * 60 * 1000);
578
+
579
+ return this.getActiveSubscriptions()
580
+ .filter(sub => {
581
+ const nextBilling = new Date(sub.next_billing_date).getTime();
582
+ return nextBilling >= now && nextBilling <= futureDate;
583
+ })
584
+ .sort((a, b) =>
585
+ new Date(a.next_billing_date) - new Date(b.next_billing_date)
586
+ );
587
+ }
588
+
589
+ getSubscriptionHealth() {
590
+ const active = this.getActiveSubscriptions().length;
591
+ const total = this.subscriptions.length;
592
+ const inactive = total - active;
593
+
594
+ return {
595
+ total,
596
+ active,
597
+ inactive,
598
+ healthScore: total > 0 ? (active / total * 100).toFixed(0) : 0
599
+ };
600
+ }
601
+
602
+ generateReport() {
603
+ const health = this.getSubscriptionHealth();
604
+ const monthlySpend = this.getTotalMonthlySpend();
605
+ const annualSpend = this.getAnnualSpend();
606
+ const upcoming = this.getUpcomingPayments();
607
+
608
+ console.log("\n=== SUBSCRIPTION ANALYSIS REPORT ===");
609
+ console.log(`\nHealth Score: ${health.healthScore}%`);
610
+ console.log(`Total Subscriptions: ${health.total}`);
611
+ console.log(` Active: ${health.active}`);
612
+ console.log(` Inactive: ${health.inactive}`);
613
+
614
+ console.log(`\nSpending:`);
615
+ console.log(` Monthly: ${monthlySpend.toFixed(2)} RUB`);
616
+ console.log(` Annual: ${annualSpend.toFixed(2)} RUB`);
617
+
618
+ console.log(`\nUpcoming Payments (next 30 days):`);
619
+ if (upcoming.length === 0) {
620
+ console.log(` None`);
621
+ } else {
622
+ upcoming.forEach(sub => {
623
+ const date = new Date(sub.next_billing_date).toLocaleDateString();
624
+ const amount = (sub.subscription_amount / 100).toFixed(2);
625
+ console.log(` ${date}: ${sub.subscription_key} - ${amount} ${sub.subscription_currency}`);
626
+ });
627
+ }
628
+
629
+ console.log("\n" + "=".repeat(40));
630
+
631
+ return {
632
+ health,
633
+ spending: { monthly: monthlySpend, annual: annualSpend },
634
+ upcomingPayments: upcoming
635
+ };
636
+ }
637
+ }
638
+
639
+ // Usage
640
+ async function analyzeSubscriptions() {
641
+ const result = await behindAPI.V21.payments.subscriptions.getList();
642
+
643
+ if (result.success && result.data.length > 0) {
644
+ const analyzer = new SubscriptionAnalyzer(result.data);
645
+ return analyzer.generateReport();
646
+ }
647
+
648
+ console.log("No subscriptions to analyze");
649
+ return null;
650
+ }
651
+
652
+ const report = await analyzeSubscriptions();
653
+ ```
654
+
655
+ ### Subscription Status Monitor
656
+
657
+ ```javascript
658
+ // Example: Monitor subscription status and send alerts
659
+ class SubscriptionMonitor {
660
+ constructor(checkIntervalMinutes = 60) {
661
+ this.checkInterval = checkIntervalMinutes * 60 * 1000;
662
+ this.lastCheck = null;
663
+ this.alertCallbacks = [];
664
+ }
665
+
666
+ async startMonitoring() {
667
+ console.log("Starting subscription monitoring...");
668
+
669
+ await this.checkSubscriptions();
670
+
671
+ setInterval(async () => {
672
+ await this.checkSubscriptions();
673
+ }, this.checkInterval);
674
+ }
675
+
676
+ async checkSubscriptions() {
677
+ try {
678
+ const result = await behindAPI.V21.payments.subscriptions.getList();
679
+
680
+ if (!result.success) {
681
+ console.error("Failed to check subscriptions:", result.message);
682
+ return;
683
+ }
684
+
685
+ this.lastCheck = new Date();
686
+
687
+ // Check for issues
688
+ result.data.forEach(sub => {
689
+ this.checkPaymentDue(sub);
690
+ this.checkInactiveSubscription(sub);
691
+ this.checkFailedPayment(sub);
692
+ });
693
+
694
+ } catch (error) {
695
+ console.error("Monitoring error:", error);
696
+ }
697
+ }
698
+
699
+ checkPaymentDue(subscription) {
700
+ const nextBilling = new Date(subscription.next_billing_date);
701
+ const now = new Date();
702
+ const daysUntilPayment = (nextBilling - now) / (1000 * 60 * 60 * 24);
703
+
704
+ if (daysUntilPayment <= 3 && daysUntilPayment > 0) {
705
+ this.alert({
706
+ type: 'payment_due',
707
+ severity: 'info',
708
+ subscription: subscription,
709
+ message: `Payment due in ${Math.ceil(daysUntilPayment)} day(s) for ${subscription.subscription_key}`,
710
+ daysRemaining: Math.ceil(daysUntilPayment)
711
+ });
712
+ }
713
+ }
714
+
715
+ checkInactiveSubscription(subscription) {
716
+ if (!subscription.instance_active && subscription.instance_enabled) {
717
+ this.alert({
718
+ type: 'inactive_subscription',
719
+ severity: 'warning',
720
+ subscription: subscription,
721
+ message: `Subscription ${subscription.subscription_key} is enabled but inactive`
722
+ });
723
+ }
724
+ }
725
+
726
+ checkFailedPayment(subscription) {
727
+ const lastAttempt = new Date(subscription.instance_last_attempt_time);
728
+ const nextBilling = new Date(subscription.next_billing_date);
729
+
730
+ // If last attempt was after scheduled billing date, payment may have failed
731
+ if (lastAttempt > nextBilling && !subscription.instance_active) {
732
+ this.alert({
733
+ type: 'payment_failed',
734
+ severity: 'error',
735
+ subscription: subscription,
736
+ message: `Payment may have failed for ${subscription.subscription_key}`,
737
+ lastAttempt: lastAttempt.toISOString()
738
+ });
739
+ }
740
+ }
741
+
742
+ alert(alertData) {
743
+ console.log(`[${alertData.severity.toUpperCase()}] ${alertData.message}`);
744
+
745
+ // Call registered callbacks
746
+ this.alertCallbacks.forEach(callback => callback(alertData));
747
+ }
748
+
749
+ onAlert(callback) {
750
+ this.alertCallbacks.push(callback);
751
+ }
752
+
753
+ stopMonitoring() {
754
+ console.log("Stopping subscription monitoring");
755
+ clearInterval(this.checkInterval);
756
+ }
757
+ }
758
+
759
+ // Usage
760
+ const monitor = new SubscriptionMonitor(60); // Check every hour
761
+
762
+ monitor.onAlert(alert => {
763
+ if (alert.severity === 'error') {
764
+ // Show notification to user
765
+ showNotification(alert.message, 'error');
766
+ } else if (alert.severity === 'warning') {
767
+ console.warn(alert.message);
768
+ } else {
769
+ console.info(alert.message);
770
+ }
771
+ });
772
+
773
+ monitor.startMonitoring();
774
+ ```
775
+
776
+ ### Subscription Comparison Tool
777
+
778
+ ```javascript
779
+ // Example: Compare available plans with user's current subscriptions
780
+ async function compareSubscriptionPlans() {
781
+ const result = await behindAPI.V21.payments.subscriptions.getList();
782
+
783
+ if (!result.success) {
784
+ console.error("Failed to fetch subscriptions");
785
+ return;
786
+ }
787
+
788
+ const currentSubscriptions = result.data;
789
+
790
+ // Available plans (would typically come from API)
791
+ const availablePlans = [
792
+ {
793
+ key: 'basic-monthly',
794
+ name: 'Basic Monthly',
795
+ amount: 1900,
796
+ currency: 'RUB',
797
+ interval: 2592000,
798
+ features: ['Feature 1', 'Feature 2']
799
+ },
800
+ {
801
+ key: 'premium-monthly',
802
+ name: 'Premium Monthly',
803
+ amount: 3200,
804
+ currency: 'RUB',
805
+ interval: 2592000,
806
+ features: ['Feature 1', 'Feature 2', 'Feature 3', 'Feature 4']
807
+ },
808
+ {
809
+ key: 'premium-annual',
810
+ name: 'Premium Annual',
811
+ amount: 32000,
812
+ currency: 'RUB',
813
+ interval: 31536000,
814
+ features: ['Feature 1', 'Feature 2', 'Feature 3', 'Feature 4', 'Annual Discount']
815
+ }
816
+ ];
817
+
818
+ console.log("\n=== SUBSCRIPTION COMPARISON ===\n");
819
+
820
+ availablePlans.forEach(plan => {
821
+ const isSubscribed = currentSubscriptions.some(sub =>
822
+ sub.subscription_key === plan.key && sub.instance_active
823
+ );
824
+
825
+ const monthlyEquivalent = plan.amount / 100 / (plan.interval / 2592000);
826
+
827
+ console.log(`${plan.name}:`);
828
+ console.log(` Status: ${isSubscribed ? '✅ SUBSCRIBED' : '❌ Not Subscribed'}`);
829
+ console.log(` Price: ${plan.amount / 100} ${plan.currency}`);
830
+ console.log(` Monthly Equivalent: ${monthlyEquivalent.toFixed(2)} ${plan.currency}`);
831
+ console.log(` Features: ${plan.features.join(', ')}`);
832
+ console.log();
833
+ });
834
+
835
+ // Calculate savings for annual plans
836
+ const monthlyPremium = availablePlans.find(p => p.key === 'premium-monthly');
837
+ const annualPremium = availablePlans.find(p => p.key === 'premium-annual');
838
+
839
+ if (monthlyPremium && annualPremium) {
840
+ const monthlyCost = (monthlyPremium.amount / 100) * 12;
841
+ const annualCost = annualPremium.amount / 100;
842
+ const savings = monthlyCost - annualCost;
843
+ const savingsPercent = (savings / monthlyCost * 100).toFixed(0);
844
+
845
+ console.log(`💡 Savings Tip:`);
846
+ console.log(` Switch to annual: Save ${savings.toFixed(2)} ${monthlyPremium.currency} (${savingsPercent}%)`);
847
+ }
848
+
849
+ console.log("\n" + "=".repeat(35));
850
+ }
851
+
852
+ // Usage
853
+ await compareSubscriptionPlans();
854
+ ```
855
+
856
+ ---
857
+
858
+ ## Database Integration
859
+
860
+ ### Database Procedure Called
861
+ The `getList()` method calls:
862
+ ```sql
863
+ subscriptions.subscriptions_get(i_user_id)
864
+ ```
865
+
866
+ **Parameters:**
867
+ - `i_user_id` (UUID) - The authenticated user's ID from session
868
+
869
+ **Returns:**
870
+ Array of subscription instance records with fields:
871
+ - subscription_instance_id
872
+ - subscription_key
873
+ - payment_method
874
+ - subscription_amount
875
+ - subscription_currency
876
+ - subscription_interval
877
+ - instance_last_attempt_time
878
+ - instance_enabled
879
+ - instance_active
880
+ - instance_created_at
881
+ - next_billing_date (calculated)
882
+
883
+ ---
884
+
885
+ ## Testing
886
+
887
+ ### Test Subscription List Retrieval
888
+
889
+ ```javascript
890
+ // Test script for subscriptions list functionality
891
+ async function testSubscriptionsList() {
892
+ console.log("=== TESTING SUBSCRIPTIONS LIST API ===\n");
893
+
894
+ // Test 1: Fetch subscriptions
895
+ console.log("Test 1: Fetching user's subscriptions...");
896
+ const result = await behindAPI.V21.payments.subscriptions.getList();
897
+
898
+ console.log(result.success ? "✓ PASS" : "✗ FAIL");
899
+
900
+ if (result.success) {
901
+ console.log(`Found ${result.data.length} subscription(s)`);
902
+
903
+ if (result.data.length > 0) {
904
+ console.log("\nFirst subscription details:");
905
+ const first = result.data[0];
906
+ console.log(` ID: ${first.subscription_instance_id}`);
907
+ console.log(` Plan: ${first.subscription_key}`);
908
+ console.log(` Amount: ${first.subscription_amount / 100} ${first.subscription_currency}`);
909
+ console.log(` Status: ${first.instance_active ? 'Active' : 'Inactive'}`);
910
+ console.log(` Payment Method: ${first.payment_method}`);
911
+ console.log(` Next Billing: ${first.next_billing_date}`);
912
+ }
913
+ } else {
914
+ console.log(`Error: ${result.message}`);
915
+ }
916
+
917
+ console.log("\n=== TEST COMPLETE ===");
918
+ }
919
+
920
+ // Run test
921
+ testSubscriptionsList();
922
+ ```
923
+
924
+ ### Mock Subscription Data
925
+
926
+ ```javascript
927
+ // Example mock data for testing UI components
928
+ const mockSubscriptionData = {
929
+ success: true,
930
+ data: [
931
+ {
932
+ subscription_instance_id: "cc0e8400-e29b-41d4-a716-446655440009",
933
+ subscription_key: "premium-monthly",
934
+ payment_method: "payture",
935
+ subscription_amount: 3200,
936
+ subscription_currency: "RUB",
937
+ subscription_interval: 2592000,
938
+ instance_last_attempt_time: "2024-11-24T09:24:39.880Z",
939
+ instance_enabled: true,
940
+ instance_active: true,
941
+ instance_created_at: "2024-11-01T10:00:00.000Z",
942
+ next_billing_date: "2024-12-01T10:00:00.000Z"
943
+ },
944
+ {
945
+ subscription_instance_id: "dd0e8400-e29b-41d4-a716-446655440010",
946
+ subscription_key: "basic-monthly",
947
+ payment_method: "payture",
948
+ subscription_amount: 1900,
949
+ subscription_currency: "RUB",
950
+ subscription_interval: 2592000,
951
+ instance_last_attempt_time: "2024-10-15T08:00:00.000Z",
952
+ instance_enabled: false,
953
+ instance_active: false,
954
+ instance_created_at: "2024-09-15T08:00:00.000Z",
955
+ next_billing_date: "2024-11-15T08:00:00.000Z"
956
+ }
957
+ ]
958
+ };
959
+
960
+ // Use mock data in development
961
+ async function getSubscriptionsWithMock(useMock = false) {
962
+ if (useMock) {
963
+ return mockSubscriptionData;
964
+ }
965
+ return await behindAPI.V21.payments.subscriptions.getList();
966
+ }
967
+ ```
968
+
969
+ ---
970
+
971
+ ## Troubleshooting
972
+
973
+ ### Common Issues and Solutions
974
+
975
+ **Issue: Subscription shows as active but user doesn't have access**
976
+ ```javascript
977
+ // Solution: Verify both instance_active and instance_enabled
978
+ function isSubscriptionFullyActive(subscription) {
979
+ return subscription.instance_active &&
980
+ subscription.instance_enabled;
981
+ }
982
+
983
+ async function verifyUserAccess() {
984
+ const result = await behindAPI.V21.payments.subscriptions.getList();
985
+
986
+ if (!result.success) {
987
+ return false;
988
+ }
989
+
990
+ const activeSubscriptions = result.data.filter(isSubscriptionFullyActive);
991
+
992
+ console.log(`Active subscriptions: ${activeSubscriptions.length}`);
993
+
994
+ return activeSubscriptions.length > 0;
995
+ }
996
+ ```
997
+
998
+ **Issue: Next billing date seems incorrect**
999
+ ```javascript
1000
+ // Solution: Calculate from last attempt + interval
1001
+ function calculateNextBillingDate(subscription) {
1002
+ const lastAttempt = new Date(subscription.instance_last_attempt_time);
1003
+ const intervalMs = subscription.subscription_interval * 1000;
1004
+
1005
+ return new Date(lastAttempt.getTime() + intervalMs);
1006
+ }
1007
+
1008
+ // Verify calculated date
1009
+ async function verifyBillingDates() {
1010
+ const result = await behindAPI.V21.payments.subscriptions.getList();
1011
+
1012
+ if (result.success) {
1013
+ result.data.forEach(sub => {
1014
+ const calculated = calculateNextBillingDate(sub);
1015
+ const provided = new Date(sub.next_billing_date);
1016
+
1017
+ console.log(`${sub.subscription_key}:`);
1018
+ console.log(` Provided: ${provided.toISOString()}`);
1019
+ console.log(` Calculated: ${calculated.toISOString()}`);
1020
+ console.log(` Match: ${calculated.getTime() === provided.getTime()}`);
1021
+ });
1022
+ }
1023
+ }
1024
+ ```
1025
+
1026
+ **Issue: Empty subscription list for paying customers**
1027
+ ```javascript
1028
+ // Solution: Debug authentication and database
1029
+ async function debugSubscriptionList() {
1030
+ console.log("Debugging subscription list...");
1031
+
1032
+ // Verify authentication
1033
+ const isAuth = !!sessionStorage.getItem('auth_token');
1034
+ console.log(`Authenticated: ${isAuth}`);
1035
+
1036
+ if (!isAuth) {
1037
+ console.error("User not authenticated");
1038
+ return;
1039
+ }
1040
+
1041
+ // Fetch with detailed logging
1042
+ try {
1043
+ const result = await behindAPI.V21.payments.subscriptions.getList();
1044
+
1045
+ console.log("API Response:", JSON.stringify(result, null, 2));
1046
+ console.log("Success:", result.success);
1047
+ console.log("Subscription count:", result.data?.length || 0);
1048
+
1049
+ if (result.success && result.data.length > 0) {
1050
+ console.log("Subscription details:");
1051
+ result.data.forEach(sub => {
1052
+ console.log(` - ${sub.subscription_key}: ${sub.instance_active ? 'Active' : 'Inactive'}`);
1053
+ });
1054
+ }
1055
+
1056
+ } catch (error) {
1057
+ console.error("Error:", error);
1058
+ }
1059
+ }
1060
+ ```
1061
+
1062
+ **Issue: Subscription not updating after payment**
1063
+ ```javascript
1064
+ // Solution: Implement cache invalidation and refresh
1065
+ class SubscriptionCache {
1066
+ constructor() {
1067
+ this.subscriptions = [];
1068
+ this.lastFetch = null;
1069
+ this.cacheTimeout = 60000; // 1 minute
1070
+ }
1071
+
1072
+ async getSubscriptions(forceRefresh = false) {
1073
+ const now = Date.now();
1074
+
1075
+ if (!forceRefresh &&
1076
+ this.lastFetch &&
1077
+ (now - this.lastFetch) < this.cacheTimeout) {
1078
+ console.log("Returning cached subscriptions");
1079
+ return this.subscriptions;
1080
+ }
1081
+
1082
+ console.log("Fetching fresh subscriptions...");
1083
+ const result = await behindAPI.V21.payments.subscriptions.getList();
1084
+
1085
+ if (result.success) {
1086
+ this.subscriptions = result.data;
1087
+ this.lastFetch = now;
1088
+ }
1089
+
1090
+ return this.subscriptions;
1091
+ }
1092
+
1093
+ invalidate() {
1094
+ this.lastFetch = null;
1095
+ console.log("Subscription cache invalidated");
1096
+ }
1097
+ }
1098
+
1099
+ // Usage
1100
+ const cache = new SubscriptionCache();
1101
+
1102
+ // After payment or subscription change
1103
+ cache.invalidate();
1104
+ const subscriptions = await cache.getSubscriptions(true);
1105
+ ```
1106
+
1107
+ ---
1108
+
1109
+ ## Security Considerations
1110
+
1111
+ 1. **Authentication Required**: This endpoint requires valid user session
1112
+ 2. **User Isolation**: Users can only see their own subscriptions
1113
+ 3. **Payment Method Security**: Sensitive payment details not included
1114
+ 4. **HTTPS Only**: All API calls must use HTTPS in production
1115
+ 5. **Session Validation**: Validate session tokens on every request
1116
+ 6. **Rate Limiting**: Implement appropriate rate limits
1117
+ 7. **Data Integrity**: Verify subscription data integrity
1118
+
1119
+ ```javascript
1120
+ // Example: Secure subscription access
1121
+ async function getSubscriptionsSecurely() {
1122
+ // Verify session is valid
1123
+ if (!sessionStorage.getItem('auth_token')) {
1124
+ throw new Error("Not authenticated");
1125
+ }
1126
+
1127
+ // Fetch subscriptions
1128
+ const result = await behindAPI.V21.payments.subscriptions.getList();
1129
+
1130
+ if (!result.success) {
1131
+ throw new Error("Failed to fetch subscriptions");
1132
+ }
1133
+
1134
+ // Log access (send to audit service)
1135
+ auditLog({
1136
+ action: 'subscriptions_viewed',
1137
+ timestamp: new Date().toISOString(),
1138
+ count: result.data.length
1139
+ });
1140
+
1141
+ return result.data;
1142
+ }
1143
+ ```
1144
+
1145
+ ---
1146
+
1147
+ ## Integration Examples
1148
+
1149
+ ### Complete Subscription Lifecycle
1150
+
1151
+ ```javascript
1152
+ // Example: Full subscription management flow
1153
+ class SubscriptionManager {
1154
+ async createSubscription(planKey) {
1155
+ // Step 1: Check if user has valid payment method
1156
+ const hasCard = await this.checkPaymentMethod();
1157
+
1158
+ if (!hasCard) {
1159
+ console.log("No payment method found. Redirecting to setup...");
1160
+ await this.setupPaymentMethod(planKey);
1161
+ return null;
1162
+ }
1163
+
1164
+ // Step 2: Create subscription instance
1165
+ const result = await behindAPI.V21.payments.subscriptions.subscribe(planKey);
1166
+
1167
+ if (result.success) {
1168
+ console.log("✅ Subscription created successfully!");
1169
+ return result.data.subscription_instance_id;
1170
+ } else {
1171
+ console.error("❌ Failed to create subscription:", result.message);
1172
+ return null;
1173
+ }
1174
+ }
1175
+
1176
+ async checkPaymentMethod() {
1177
+ const cards = await behindAPI.V21.payments.cards.getList();
1178
+
1179
+ if (!cards.success || cards.data.length === 0) {
1180
+ return false;
1181
+ }
1182
+
1183
+ // Check for valid active cards
1184
+ const validCards = cards.data.filter(card =>
1185
+ card.is_active &&
1186
+ card.response.Status === "IsActive" &&
1187
+ card.response.Expired === "false"
1188
+ );
1189
+
1190
+ return validCards.length > 0;
1191
+ }
1192
+
1193
+ async setupPaymentMethod(planKey) {
1194
+ const init = await behindAPI.V21.payments.payture.init();
1195
+
1196
+ if (init.success) {
1197
+ // Store pending subscription
1198
+ sessionStorage.setItem('pending_subscription', planKey);
1199
+ sessionStorage.setItem('card_registration_pending', 'true');
1200
+
1201
+ // Redirect to card registration
1202
+ window.location.href = init.data.RedirectUrl;
1203
+ } else {
1204
+ console.error("Failed to initialize payment setup");
1205
+ }
1206
+ }
1207
+
1208
+ async listSubscriptions() {
1209
+ const result = await behindAPI.V21.payments.subscriptions.getList();
1210
+
1211
+ if (result.success) {
1212
+ return result.data;
1213
+ }
1214
+
1215
+ return [];
1216
+ }
1217
+
1218
+ async getActiveSubscriptions() {
1219
+ const all = await this.listSubscriptions();
1220
+ return all.filter(sub => sub.instance_active && sub.instance_enabled);
1221
+ }
1222
+
1223
+ async hasActiveSubscription(planKey) {
1224
+ const active = await this.getActiveSubscriptions();
1225
+ return active.some(sub => sub.subscription_key === planKey);
1226
+ }
1227
+ }
1228
+
1229
+ // Usage
1230
+ const manager = new SubscriptionManager();
1231
+
1232
+ // Create new subscription
1233
+ const subscriptionId = await manager.createSubscription("premium-monthly");
1234
+
1235
+ // List all subscriptions
1236
+ const subscriptions = await manager.listSubscriptions();
1237
+
1238
+ // Check if user has specific subscription
1239
+ const hasPremium = await manager.hasActiveSubscription("premium-monthly");
1240
+ ```
1241
+
1242
+ ---
1243
+
1244
+ ## API Reference Summary
1245
+
1246
+ | Method | Authorization Required | Parameters | Returns |
1247
+ |--------|----------------------|------------|---------|
1248
+ | `getList` | Yes | None | Array of subscription instances |
1249
+ | `subscribe` | Yes | subscriptionId | subscription_instance_id |
1250
+
1251
+ ---
1252
+
1253
+ ## Support and Resources
1254
+
1255
+ For additional help:
1256
+ - **API Documentation**: Refer to the main Payments API V21 documentation
1257
+ - **Database Schema**: Check subscription_instances table structure
1258
+ - **Related Endpoints**:
1259
+ - `/payments/V21/payture/init` - Initialize card registration
1260
+ - `/payments/V21/payture/finalise` - Finalize card after registration
1261
+ - `/payments/V21/cards/getList` - Manage payment methods
1262
+ - `/payments/V21/subscriptions/subscribe` - Create new subscriptions
1263
+ - **Database Procedures**:
1264
+ - `subscriptions.subscriptions_get` - Retrieve subscriptions
1265
+ - `subscriptions.subscription_instance_create` - Create instances
1266
+ - **Support**: Contact your technical support team for integration assistance