PageSourceSearch

https://www.keyray.ru/_instatic/assets/iEiWYpZGgbRipGN2yw-v0/classic/017-cancel_subscription.js

js keyray.ru collected 2026-10-06 07:02:05 UTC 8,639 bytes, 228 lines download raw bytes

1/**
2 * PuntoPunto - Subscription Cancellation Script for Webflow
3 * 
4 * Требует: env_redirect_rotation.js
5 * Использует PuntoEnv для конфигурации и логирования
6 * 
7 * Usage:
8 * 1. Add this script to Webflow page: Settings → Custom Code → Before </body> tag
9 * 2. Add custom attribute to cancel button: data-cancel-action="subscription"
10 * 3. Token will be extracted from URL query parameter: ?token=...
11 * 4. On success: redirect to /success
12 * 5. On error: redirect to /error
13 * 6. Messages will be inserted into elements with finish-message attributes
14 */
15
16(function() {
17    'use strict';
18    
19    // Проверка наличия PuntoEnv (должен быть загружен из env_redirect_rotation.js)
20    if (!window.PuntoEnv) {
21        console.error('❌ PuntoEnv not found! Make sure env_redirect_rotation.js is loaded first.');
22        return;
23    }
24    
25    // ============================================
26    // ИМПОРТ КОНФИГУРАЦИИ ИЗ ЕДИНОГО ИСТОЧНИКА
27    // ============================================
28    
29    const { API_BASE, SITE_BASE, log, warn, error } = window.PuntoEnv;
30    
31    log('🔄 Cancel Subscription Script initializing...');
32    log('🔗 Using API Base:', API_BASE);
33    log('🏠 Using Site Base:', SITE_BASE);
34    
35    // Fallback messages (only if server doesn't provide them)
36    const FALLBACK_SUCCESS_MESSAGES = {
37        headline: 'Подписка отменена',
38        text: 'Данные вашей карты удалены для последующих списаний. Ваша лицензия будет аннулирована после окончания текущего оплаченного периода.'
39    };
40    
41    const FALLBACK_ERROR_MESSAGES = {
42        headline: 'Ошибка при отмене подписки',
43        text: 'Во время отмены подписки произошла ошибка. Попробуйте снова открыть приложение и повторить процедуру.'
44    };
45    
46    /**
47     * Extract token from URL query parameters
48     */
49    function getTokenFromURL() {
50        const urlParams = new URLSearchParams(window.location.search);
51        return urlParams.get('token');
52    }
53    
54    /**
55     * Update finish messages on success/error page
56     */
57    function updateFinishMessages(messages) {
58        // Update page title
59        if (messages.headline) {
60            document.title = messages.headline;
61            log(`📄 Page title updated: ${messages.headline}`);
62        }
63        
64        // Update headline
65        const headlineElement = document.querySelector('[finish-message="headline"]');
66        if (headlineElement && messages.headline) {
67            headlineElement.textContent = messages.headline;
68        }
69        
70        // Update text
71        const textElement = document.querySelector('[finish-message="text"]');
72        if (textElement && messages.text) {
73            textElement.textContent = messages.text;
74        }
75    }
76    
77    /**
78     * Redirect to success/error page with messages from server
79     * 
80     * @param {boolean} isSuccess - Whether to redirect to success or error page
81     * @param {Object} serverMessages - Messages from server {headline, text}
82     */
83    function redirectWithMessage(isSuccess, serverMessages = {}) {
84        const targetPath = isSuccess ? '/success' : '/error';
85        
86        // Use server messages or fallback to defaults
87        const fallbackMessages = isSuccess ? FALLBACK_SUCCESS_MESSAGES : FALLBACK_ERROR_MESSAGES;
88        const messages = {
89            headline: serverMessages.headline || fallbackMessages.headline,
90            text: serverMessages.text || fallbackMessages.text
91        };
92        
93        // Store messages in sessionStorage for the next page
94        sessionStorage.setItem('finishMessages', JSON.stringify(messages));
95        
96        log(`🔄 Redirecting to: ${SITE_BASE}${targetPath}`);
97        
98        // Redirect (SITE_BASE будет автоматически правильным в зависимости от окружения)
99        window.location.href = `${SITE_BASE}${targetPath}`;
100    }
101    
102    /**
103     * Cancel subscription via API
104     */
105    async function cancelSubscription(token, button) {
106        // Disable button and show loading
107        button.disabled = true;
108        const originalText = button.textContent;
109        button.textContent = 'Отменяем подписку...';
110        
111        try {
112            const response = await fetch(`${API_BASE}/api/subscriptions/cancel`, {
113                method: 'POST',
114                headers: {
115                    'Content-Type': 'application/json'
116                },
117                body: JSON.stringify({ token })
118            });
119            
120            const data = await response.json();
121            
122            if (response.ok && data.success) {
123                // Success - use messages from server
124                const serverMessages = {
125                    headline: data.headline,
126                    text: data.text
127                };
128                
129                log('✅ Subscription cancelled successfully');
130                redirectWithMessage(true, serverMessages);
131            } else {
132                // API returned error - use error messages from server
133                const serverMessages = {
134                    headline: data.headline,
135                    text: data.text || data.error // Fallback to error field
136                };
137                
138                error('❌ Subscription cancellation failed:', data.error);
139                redirectWithMessage(false, serverMessages);
140            }
141            
142        } catch (err) {
143            // Network error - use fallback messages
144            error('❌ Network error during subscription cancellation:', err);
145            redirectWithMessage(false, {
146                text: 'Ошибка сети. Проверьте подключение к интернету и попробуйте снова.'
147            });
148        } finally {
149            // Restore button state (in case redirect fails)
150            button.disabled = false;
151            button.textContent = originalText;
152        }
153    }
154    
155    /**
156     * Initialize cancellation button
157     */
158    function initCancelButton() {
159        const cancelButton = document.querySelector('[data-cancel-action="subscription"]');
160        
161        if (!cancelButton) {
162            log('ℹ️ Cancel subscription button not found on this page');
163            return;
164        }
165        
166        // Extract token from URL
167        const token = getTokenFromURL();
168        
169        if (!token) {
170            error('❌ No token found in URL. User must open cancellation link from app.');
171            // Disable button if no token
172            cancelButton.disabled = true;
173            cancelButton.textContent = 'Неверная ссылка';
174            return;
175        }
176        
177        log('✅ Cancel button initialized with token');
178        
179        // Add click handler
180        cancelButton.addEventListener('click', async function(event) {
181            event.preventDefault();
182            
183            // Optional: Show confirmation dialog
184            const confirmed = confirm(
185                'Вы уверены, что хотите отменить подписку?\n\n' +
186                'Доступ к Pro функциям сохранится до конца оплаченного периода.'
187            );
188            
189            if (confirmed) {
190                await cancelSubscription(token, cancelButton);
191            }
192        });
193    }
194    
195    /**
196     * Apply finish messages on success/error pages
197     */
198    function applyFinishMessages() {
199        const messages = sessionStorage.getItem('finishMessages');
200        
201        if (messages) {
202            try {
203                const parsedMessages = JSON.parse(messages);
204                updateFinishMessages(parsedMessages);
205                
206                // Clear after applying
207                sessionStorage.removeItem('finishMessages');
208            } catch (err) {
209                error('❌ Failed to parse finish messages:', err);
210            }
211        }
212    }
213    
214    /**
215     * Initialize on DOM ready
216     */
217    if (document.readyState === 'loading') {
218        document.addEventListener('DOMContentLoaded', function() {
219            initCancelButton();
220            applyFinishMessages();
221        });
222    } else {
223        initCancelButton();
224        applyFinishMessages();
225    }
226    
227})();
228

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.