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.
- package/.gitlab-ci.yml +20 -0
- package/README.md +65 -0
- package/docs/behind-api-client/easyjob/JobDescriptions/README.md +654 -0
- package/docs/behind-api-client/easyjob/README.md +647 -0
- package/docs/behind-api-client/easyjob/applicants/README.md +494 -0
- package/docs/behind-api-client/easyjob/applications/README.md +754 -0
- package/docs/behind-api-client/easyjob/candidateProfiles/README.md +940 -0
- package/docs/behind-api-client/easyjob/jd-candidate-questions/README.md +372 -0
- package/docs/behind-api-client/payments/payture/README.21.md +901 -0
- package/docs/behind-api-client/payments/payture/README.cards.md +1497 -0
- package/docs/behind-api-client/payments/payture/README.md +1497 -0
- package/docs/behind-api-client/payments/payture/README.rukitchen.md +396 -0
- package/docs/behind-api-client/payments/payture/README.subscriptions.md +1266 -0
- package/docs/behind-api-client/payments/stripe/README.flow.md +254 -0
- package/docs/behind-api-client/rag/storage/README.md +519 -0
- package/example.js +35 -0
- package/index.cjs +14 -0
- package/index.js +15 -0
- package/lib/behind-api-auth-client/BehindApiAuthClient.js +91 -0
- package/lib/behind-api-auth-client/authorisation/AuthorisationApp.js +9 -0
- package/lib/behind-api-auth-client/authorisation/AuthorisationV10.js +9 -0
- package/lib/behind-api-auth-client/authorisation/AuthorisationV10Code.js +30 -0
- package/lib/behind-api-auth-client/example.js +47 -0
- package/lib/behind-api-auth-client/package.json +9 -0
- package/lib/behind-api-client/BehindApiClient.js +137 -0
- package/lib/behind-api-client/chat/ChatApp.js +11 -0
- package/lib/behind-api-client/chat/ChatV10.js +13 -0
- package/lib/behind-api-client/chat/ChatV10Chat.js +87 -0
- package/lib/behind-api-client/chat/ChatV10Chats.js +14 -0
- package/lib/behind-api-client/chat/ChatV10Message.js +57 -0
- package/lib/behind-api-client/chat/ChatV20.js +11 -0
- package/lib/behind-api-client/chat/ChatV20Chat.js +14 -0
- package/lib/behind-api-client/chat/ChatV20Message.js +27 -0
- package/lib/behind-api-client/easyjob/EasyjobApp.js +9 -0
- package/lib/behind-api-client/easyjob/EasyjobV10.js +31 -0
- package/lib/behind-api-client/easyjob/EasyjobV10Answers.js +16 -0
- package/lib/behind-api-client/easyjob/EasyjobV10Applicants.js +29 -0
- package/lib/behind-api-client/easyjob/EasyjobV10Applications.js +39 -0
- package/lib/behind-api-client/easyjob/EasyjobV10CandidateProfileArtifacts.js +31 -0
- package/lib/behind-api-client/easyjob/EasyjobV10CandidateProfiles.js +99 -0
- package/lib/behind-api-client/easyjob/EasyjobV10Companies.js +36 -0
- package/lib/behind-api-client/easyjob/EasyjobV10Cv.js +15 -0
- package/lib/behind-api-client/easyjob/EasyjobV10Departments.js +37 -0
- package/lib/behind-api-client/easyjob/EasyjobV10JobDescriptionArtifacts.js +29 -0
- package/lib/behind-api-client/easyjob/EasyjobV10JobDescriptionCandidateQuestions.js +63 -0
- package/lib/behind-api-client/easyjob/EasyjobV10JobDescriptions.js +93 -0
- package/lib/behind-api-client/easyjob/EasyjobV10Reports.js +35 -0
- package/lib/behind-api-client/example.js +47 -0
- package/lib/behind-api-client/global/GlobalApp.js +9 -0
- package/lib/behind-api-client/global/GlobalV10.js +11 -0
- package/lib/behind-api-client/global/GlobalV10Sockets.js +16 -0
- package/lib/behind-api-client/global/GlobalV10Storage.js +29 -0
- package/lib/behind-api-client/gpt/GptApp.js +15 -0
- package/lib/behind-api-client/gpt/GptV10.js +15 -0
- package/lib/behind-api-client/gpt/GptV10Prompt.js +17 -0
- package/lib/behind-api-client/gpt/GptV10Request.js +16 -0
- package/lib/behind-api-client/gpt/GptV10Storedprompts.js +14 -0
- package/lib/behind-api-client/gpt/GptV10Whisper.js +23 -0
- package/lib/behind-api-client/gpt/GptV20.js +9 -0
- package/lib/behind-api-client/gpt/GptV20Prompt.js +15 -0
- package/lib/behind-api-client/gpt/GptV30.js +13 -0
- package/lib/behind-api-client/gpt/GptV30Chat.js +19 -0
- package/lib/behind-api-client/gpt/GptV30Prompt.js +41 -0
- package/lib/behind-api-client/gpt/GptV30Prompts.js +32 -0
- package/lib/behind-api-client/gpt/GptV40.js +11 -0
- package/lib/behind-api-client/gpt/GptV40Prompt.js +24 -0
- package/lib/behind-api-client/gpt/GptV40Prompts.js +30 -0
- package/lib/behind-api-client/mailer/MailerApp.js +11 -0
- package/lib/behind-api-client/mailer/MailerV10.js +15 -0
- package/lib/behind-api-client/mailer/MailerV10Bulk.js +21 -0
- package/lib/behind-api-client/mailer/MailerV10Message.js +83 -0
- package/lib/behind-api-client/mailer/MailerV10Settings.js +44 -0
- package/lib/behind-api-client/mailer/MailerV10Template.js +23 -0
- package/lib/behind-api-client/mailer/MailerV20.js +9 -0
- package/lib/behind-api-client/mailer/MailerV20Message.js +21 -0
- package/lib/behind-api-client/mastogram/MastogramApp.js +9 -0
- package/lib/behind-api-client/mastogram/MastogramV10.js +13 -0
- package/lib/behind-api-client/mastogram/MastogramV10Bluesky.js +42 -0
- package/lib/behind-api-client/mastogram/MastogramV10Mastodon.js +45 -0
- package/lib/behind-api-client/mastogram/MastogramV10Telegram.js +39 -0
- package/lib/behind-api-client/monitor/MonitorApp.js +9 -0
- package/lib/behind-api-client/monitor/MonitorV10.js +13 -0
- package/lib/behind-api-client/monitor/MonitorV10Finances.js +39 -0
- package/lib/behind-api-client/monitor/MonitorV10Record.js +15 -0
- package/lib/behind-api-client/monitor/MonitorV10Records.js +22 -0
- package/lib/behind-api-client/oauth/OauthApp.js +9 -0
- package/lib/behind-api-client/oauth/OauthV10.js +9 -0
- package/lib/behind-api-client/oauth/OauthV10Authorisation.js +15 -0
- package/lib/behind-api-client/package.json +9 -0
- package/lib/behind-api-client/payments/PaymentsApp.js +13 -0
- package/lib/behind-api-client/payments/PaymentsV10.js +15 -0
- package/lib/behind-api-client/payments/PaymentsV10Gift.js +32 -0
- package/lib/behind-api-client/payments/PaymentsV10Payture.js +30 -0
- package/lib/behind-api-client/payments/PaymentsV10Product.js +15 -0
- package/lib/behind-api-client/payments/PaymentsV10Telegram.js +44 -0
- package/lib/behind-api-client/payments/PaymentsV20.js +9 -0
- package/lib/behind-api-client/payments/PaymentsV20Payture.js +32 -0
- package/lib/behind-api-client/payments/PaymentsV21.js +15 -0
- package/lib/behind-api-client/payments/PaymentsV21Cards.js +14 -0
- package/lib/behind-api-client/payments/PaymentsV21Payture.js +29 -0
- package/lib/behind-api-client/payments/PaymentsV21Stripe.js +28 -0
- package/lib/behind-api-client/payments/PaymentsV21Subscriptions.js +21 -0
- package/lib/behind-api-client/questionnaire/QuestionnaireApp.js +9 -0
- package/lib/behind-api-client/questionnaire/QuestionnaireV10.js +9 -0
- package/lib/behind-api-client/questionnaire/QuestionnaireV10Form.js +22 -0
- package/lib/behind-api-client/raet/RaetApp.js +11 -0
- package/lib/behind-api-client/raet/RaetV10.js +21 -0
- package/lib/behind-api-client/raet/RaetV10Cv.js +87 -0
- package/lib/behind-api-client/raet/RaetV10Individual.js +43 -0
- package/lib/behind-api-client/raet/RaetV10Individuals.js +38 -0
- package/lib/behind-api-client/raet/RaetV10Jd.js +47 -0
- package/lib/behind-api-client/raet/RaetV10Project.js +61 -0
- package/lib/behind-api-client/raet/RaetV10Projects.js +14 -0
- package/lib/behind-api-client/raet/RaetV10Report.js +39 -0
- package/lib/behind-api-client/raet/RaetV20.js +11 -0
- package/lib/behind-api-client/raet/RaetV20Cv.js +31 -0
- package/lib/behind-api-client/raet/RaetV20Individuals.js +25 -0
- package/lib/behind-api-client/rag/RagApp.js +9 -0
- package/lib/behind-api-client/rag/RagV10.js +9 -0
- package/lib/behind-api-client/rag/RagV10Storage.js +27 -0
- package/lib/behind-api-client/ruKitchen/RuKitchenApp.js +9 -0
- package/lib/behind-api-client/ruKitchen/RuKitchenV10.js +11 -0
- package/lib/behind-api-client/ruKitchen/RuKitchenV10Importer.js +29 -0
- package/lib/behind-api-client/ruKitchen/RuKitchenV10SeoArticle.js +14 -0
- package/lib/behind-api-client/sales/SalesApp.js +11 -0
- package/lib/behind-api-client/sales/SalesV10.js +23 -0
- package/lib/behind-api-client/sales/SalesV10Catalogue.js +58 -0
- package/lib/behind-api-client/sales/SalesV10Categories.js +15 -0
- package/lib/behind-api-client/sales/SalesV10Companies.js +55 -0
- package/lib/behind-api-client/sales/SalesV10Company.js +120 -0
- package/lib/behind-api-client/sales/SalesV10Group.js +70 -0
- package/lib/behind-api-client/sales/SalesV10Groups.js +21 -0
- package/lib/behind-api-client/sales/SalesV10Logs.js +14 -0
- package/lib/behind-api-client/sales/SalesV10Notes.js +38 -0
- package/lib/behind-api-client/sales/SalesV20.js +11 -0
- package/lib/behind-api-client/sales/SalesV20Companies.js +26 -0
- package/lib/behind-api-client/sales/SalesV20Notes.js +17 -0
- package/lib/behind-api-client/sip/SipApp.js +9 -0
- package/lib/behind-api-client/sip/SipV10.js +15 -0
- package/lib/behind-api-client/sip/SipV10Call.js +83 -0
- package/lib/behind-api-client/sip/SipV10Phone.js +44 -0
- package/lib/behind-api-client/sip/SipV10Transcript.js +21 -0
- package/lib/behind-api-client/sip/SipV10Transcripts.js +21 -0
- package/lib/behind-api-client/storage/StorageApp.js +9 -0
- package/lib/behind-api-client/storage/StorageV10.js +11 -0
- package/lib/behind-api-client/storage/StorageV10File.js +35 -0
- package/lib/behind-api-client/storage/StorageV10Upload.js +21 -0
- package/lib/behind-api-client/tests/TestsApp.js +9 -0
- package/lib/behind-api-client/tests/TestsV10.js +15 -0
- package/lib/behind-api-client/tests/TestsV10Cases.js +29 -0
- package/lib/behind-api-client/tests/TestsV10CasesExtended.js +14 -0
- package/lib/behind-api-client/tests/TestsV10Core.js +14 -0
- package/lib/behind-api-client/tests/TestsV10Mail.js +14 -0
- package/lib/behind-api-client/tools/ToolsApp.js +9 -0
- package/lib/behind-api-client/tools/ToolsV10.js +9 -0
- package/lib/behind-api-client/tools/ToolsV10Pdf.js +30 -0
- 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
|