PageSourceSearch

https://smartclinician.ai/checkout.html?plan=student&billing=monthly

html smartclinician.ai collected 2026-10-05 15:05:37 UTC 24,873 bytes, 683 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>Checkout - Smart Clinician</title>
7    <style>
8        * {
9            margin: 0;
10            padding: 0;
11            box-sizing: border-box;
12            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
13        }
14
15        body {
16            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17            min-height: 100vh;
18            padding: 40px 20px;
19        }
20
21        .container {
22            max-width: 900px;
23            margin: 0 auto;
24            background: white;
25            border-radius: 20px;
26            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
27            overflow: hidden;
28        }
29
30        .header {
31            background: linear-gradient(135deg, #1a73e8 0%, #4285f4 100%);
32            color: white;
33            padding: 30px 40px;
34            text-align: center;
35        }
36
37        .header h1 {
38            font-size: 32px;
39            margin-bottom: 10px;
40        }
41
42        .header p {
43            opacity: 0.9;
44            font-size: 16px;
45        }
46
47        .content {
48            display: grid;
49            grid-template-columns: 1fr 1fr;
50            gap: 40px;
51            padding: 40px;
52        }
53
54        .plan-summary {
55            border-right: 2px solid #e8eaed;
56            padding-right: 40px;
57        }
58
59        .plan-summary h2 {
60            font-size: 24px;
61            margin-bottom: 20px;
62            color: #202124;
63        }
64
65        .plan-card {
66            background: #f8f9fa;
67            border-radius: 12px;
68            padding: 25px;
69            margin-bottom: 20px;
70        }
71
72        .plan-name {
73            font-size: 28px;
74            font-weight: 700;
75            color: #1a73e8;
76            margin-bottom: 10px;
77        }
78
79        .plan-price {
80            font-size: 42px;
81            font-weight: 700;
82            color: #202124;
83            margin-bottom: 5px;
84        }
85
86        .plan-period {
87            color: #5f6368;
88            margin-bottom: 20px;
89        }
90
91        .features-list {
92            list-style: none;
93            margin-top: 20px;
94        }
95
96        .features-list li {
97            padding: 10px 0;
98            border-bottom: 1px solid #e8eaed;
99            display: flex;
100            align-items: center;
101            gap: 10px;
102        }
103
104        .features-list li:last-child {
105            border-bottom: none;
106        }
107
108        .check-icon {
109            color: #34a853;
110            font-weight: bold;
111        }
112
113        .payment-section h2 {
114            font-size: 24px;
115            margin-bottom: 20px;
116            color: #202124;
117        }
118
119        .discount-section {
120            background: #e8f0fe;
121            border-radius: 12px;
122            padding: 20px;
123            margin-bottom: 25px;
124        }
125
126        .discount-section h3 {
127            font-size: 16px;
128            margin-bottom: 15px;
129            color: #1a73e8;
130        }
131
132        .discount-input-group {
133            display: flex;
134            gap: 10px;
135        }
136
137        .discount-input-group input {
138            flex: 1;
139            padding: 12px;
140            border: 1px solid #dadce0;
141            border-radius: 8px;
142            font-size: 14px;
143            text-transform: uppercase;
144        }
145
146        .discount-input-group button {
147            padding: 12px 24px;
148            background: #1a73e8;
149            color: white;
150            border: none;
151            border-radius: 8px;
152            font-weight: 600;
153            cursor: pointer;
154            transition: all 0.3s;
155        }
156
157        .discount-input-group button:hover {
158            background: #0d47a1;
159        }
160
161        .discount-applied {
162            background: #e6f4ea;
163            border: 1px solid #34a853;
164            border-radius: 8px;
165            padding: 15px;
166            margin-top: 15px;
167            display: none;
168        }
169
170        .discount-applied.show {
171            display: block;
172        }
173
174        .price-breakdown {
175            background: #f8f9fa;
176            border-radius: 12px;
177            padding: 20px;
178            margin-bottom: 25px;
179        }
180
181        .price-row {
182            display: flex;
183            justify-content: space-between;
184            padding: 10px 0;
185            font-size: 16px;
186        }
187
188        .price-row.total {
189            border-top: 2px solid #dadce0;
190            margin-top: 10px;
191            padding-top: 15px;
192            font-size: 24px;
193            font-weight: 700;
194            color: #1a73e8;
195        }
196
197        .discount-row {
198            color: #34a853;
199            font-weight: 600;
200        }
201
202        #paypal-button-container {
203            margin: 30px 0;
204        }
205
206        .security-note {
207            text-align: center;
208            color: #5f6368;
209            font-size: 13px;
210            margin-top: 20px;
211        }
212
213        .loading {
214            text-align: center;
215            padding: 40px;
216            color: #5f6368;
217        }
218
219        .spinner {
220            border: 3px solid #f3f3f3;
221            border-top: 3px solid #1a73e8;
222            border-radius: 50%;
223            width: 40px;
224            height: 40px;
225            animation: spin 1s linear infinite;
226            margin: 0 auto 20px;
227        }
228
229        @keyframes spin {
230            0% { transform: rotate(0deg); }
231            100% { transform: rotate(360deg); }
232        }
233
234        .alert {
235            padding: 15px;
236            border-radius: 8px;
237            margin-bottom: 20px;
238        }
239
240        .alert-error {
241            background: #fce8e6;
242            color: #d93025;
243            border: 1px solid #d93025;
244        }
245
246        .alert-success {
247            background: #e6f4ea;
248            color: #1e8e3e;
249            border: 1px solid #1e8e3e;
250        }
251
252        @media (max-width: 768px) {
253            .content {
254                grid-template-columns: 1fr;
255                gap: 30px;
256            }
257
258            .plan-summary {
259                border-right: none;
260                border-bottom: 2px solid #e8eaed;
261                padding-right: 0;
262                padding-bottom: 30px;
263            }
264        }
265    </style>
266</head>
267<body>
268    <div class="container">
269        <div class="header">
270            <h1>🩺 Smart Clinician</h1>
271            <p>Complete your subscription</p>
272        </div>
273
274        <div class="content">
275            <!-- Plan Summary -->
276            <div class="plan-summary">
277                <h2>Plan Summary</h2>
278                <div id="plan-info" class="loading">
279                    <div class="spinner"></div>
280                    <p>Loading plan details...</p>
281                </div>
282            </div>
283
284            <!-- Payment Section -->
285            <div class="payment-section">
286                <h2>Payment Details</h2>
287
288                <div id="alert"></div>
289
290                <!-- Discount Code -->
291                <div class="discount-section">
292                    <h3>🎁 Have a discount code?</h3>
293                    <div class="discount-input-group">
294                        <input type="text" id="discount-code" placeholder="Enter code (e.g., LAUNCH50)" maxlength="50">
295                        <button onclick="applyDiscount()">Apply</button>
296                    </div>
297                    <div id="discount-applied" class="discount-applied">
298                        <strong>✓ Discount applied:</strong> <span id="discount-details"></span>
299                    </div>
300                </div>
301
302                <!-- Price Breakdown -->
303                <div class="price-breakdown">
304                    <div class="price-row">
305                        <span>Subtotal:</span>
306                        <span id="subtotal">$0.00</span>
307                    </div>
308                    <div class="price-row discount-row" id="discount-row" style="display: none;">
309                        <span>Discount:</span>
310                        <span id="discount-amount">-$0.00</span>
311                    </div>
312                    <div class="price-row total">
313                        <span>Total:</span>
314                        <span id="total-price">$0.00</span>
315                    </div>
316                </div>
317
318                <!-- PayPal Button -->
319                <div id="paypal-button-container"></div>
320
321                <div class="security-note">
322                    🔒 Secure payment powered by PayPal<br>
323                    Your payment information is encrypted and secure
324                </div>
325            </div>
326        </div>
327    </div>
328
329    <!-- PayPal SDK will be loaded dynamically -->
330    
330<script>
331        // Use global API_URL if available, otherwise set it
332        if (typeof window.API_URL === 'undefined') {
333            window.API_URL = 'https://smartclinician.ai/api';
334        }
335        let currentPlan = null;
336        let currentBilling = 'monthly';
337        let appliedDiscount = null;
338        let paypalClientId = null;
339
340        // Get plan from URL
341        const urlParams = new URLSearchParams(window.location.search);
342        const selectedTier = urlParams.get('plan') || 'intermediate';
343        const selectedBilling = urlParams.get('billing') || 'monthly';
344
345        // Check authentication on page load
346        document.addEventListener('DOMContentLoaded', async () => {
347            // Check if user is logged in
348            const token = localStorage.getItem('token');
349            if (!token) {
350                // Save current URL to return after login
351                sessionStorage.setItem('redirectAfterLogin', window.location.href);
352                window.location.href = 'login.html?redirect=checkout';
353                return;
354            }
355            
356            // Load PayPal configuration first
357            await loadPayPalConfig();
358            await loadPlanDetails();
359        });
360
361        async function loadPayPalConfig() {
362            try {
363                const response = await fetch(`${API_URL}/payment/config`);
364                const data = await response.json();
365                
366                if (!data.success || !data.clientId) {
367                    throw new Error('PayPal not configured');
368                }
369                
370                paypalClientId = data.clientId;
371                
372                // Load PayPal SDK dynamically
373                const script = document.createElement('script');
374                script.src = `https://www.paypal.com/sdk/js?client-id=${data.clientId}&vault=true&intent=subscription`;
375                script.async = true;
376                script.onload = () => {
377                    console.log('PayPal SDK loaded');
378                    if (currentPlan) {
379                        updatePricing();
380                    }
381                };
382                script.onerror = () => {
383                    console.error('Failed to load PayPal SDK');
384                    showAlert('Failed to load PayPal. Please refresh the page.', 'error');
385                };
386                document.head.appendChild(script);
387                
388            } catch (error) {
389                console.error('Load PayPal config error:', error);
390                showAlert('Payment system not available. Please contact support.', 'error');
391            }
392        }
393
394        async function loadPlanDetails() {
395            try {
396                const response = await fetch(`${API_URL}/payment/pricing`);
397                const data = await response.json();
398
399                if (!data.success) {
400                    throw new Error('Failed to load pricing');
401                }
402
403                console.log('Available tiers:', data.tiers.map(t => t.tier));
404                console.log('Looking for tier:', selectedTier);
405
406                // Find selected tier
407                currentPlan = data.tiers.find(t => t.tier === selectedTier);
408                currentBilling = selectedBilling;
409
410                if (!currentPlan) {
411                    showAlert(`Invalid plan selected: "${selectedTier}". Available plans: ${data.tiers.map(t => t.tier).join(', ')}`, 'error');
412                    // Default to professional if invalid
413                    currentPlan = data.tiers.find(t => t.tier === 'professional') || data.tiers[0];
414                    if (!currentPlan) {
415                        throw new Error('No pricing plans available');
416                    }
417                }
418
419                // Display plan info
420                displayPlanInfo();
421                updatePricing();
422
423            } catch (error) {
424                console.error('Load plan error:', error);
425                showAlert('Failed to load plan details', 'error');
426            }
427        }
428
429        function isEnterprisePlan() {
430            return currentPlan && String(currentPlan.tier).toLowerCase() === 'enterprise';
431        }
432
433        function displayPlanInfo() {
434            const price = currentBilling === 'annual' ? currentPlan.annual_price : currentPlan.monthly_price;
435            const period = currentBilling === 'annual' ? 'year' : 'month';
436            const savings = currentBilling === 'annual'
437                ? ` (Save $${(parseFloat(currentPlan.monthly_price) * 12 - parseFloat(currentPlan.annual_price)).toFixed(2)}/year)`
438                : '';
439
440            const credits = Number(currentPlan.cases_per_month) || 0;
441            const creditsLabel = credits >= 999999 ? 'Unlimited credits' : `${credits.toLocaleString()} credits/month`;
442            const heavyCaseEstimate = credits >= 999999 ? 'Unlimited intensive cases' : `~${Math.max(1, Math.round(credits / 64))} intensive cases`;
443
444            const creditUsageGuide = `
445                <div style="margin-top: 20px; padding: 16px; border: 1px solid #e0e0e0; border-radius: 12px; background: #f8f9fa;">
446                    <h4 style="margin-top: 0; margin-bottom: 12px;">Credit Usage Guide</h4>
447                    <ul style="list-style: none; padding: 0; margin: 0; font-size: 14px;">
448                        <li style="margin-bottom: 8px;"><strong>🟢 Light Case:</strong> Diagnosis + Knowledge Base ≈ 10–25 credits</li>
449                        <li style="margin-bottom: 8px;"><strong>🟡 Standard Case:</strong> Diagnosis + Treatment + Research ≈ 25–45 credits</li>
450                        <li><strong>🔴 Intensive Case:</strong> All agents + refinements ≈ 45–65 credits (worst-case ~64 credits)</li>
451                    </ul>
452                </div>
453            `;
454
455            const priceLine = isEnterprisePlan()
456                ? `Contact Us`
457                : `$${price}<span style="font-size: 18px; font-weight: 400;">/${period}</span>`;
458            const periodLine = isEnterprisePlan()
459                ? `Custom pricing & SLAs`
460                : `Billed ${currentBilling}${savings}`;
461
462            const html = `
463                <div class="plan-card">
464                    <div class="plan-name">${currentPlan.display_name}</div>
465                    <div class="plan-price">${priceLine}</div>
466                    <div class="plan-period">${periodLine}</div>
467                    <ul class="features-list">
468                        <li><span class="check-icon">✓</span> ${creditsLabel} (${heavyCaseEstimate})</li>
469                        ${currentPlan.features.map(f => `<li><span class="check-icon">✓</span> ${f}</li>`).join('')}
470                    </ul>
471                    ${creditUsageGuide}
472                </div>
473            `;
474
475            document.getElementById('plan-info').innerHTML = html;
476        }
477
478        function updatePricing() {
479            if (isEnterprisePlan()) {
480                document.getElementById('subtotal').textContent = 'Contact Us';
481                document.getElementById('discount-row').style.display = 'none';
482                document.getElementById('total-price').textContent = 'Contact Us';
483                document.getElementById('paypal-button-container').innerHTML = `
484                    <a href="contact.html?interest=Enterprise" class="btn btn-outline" style="width: 100%; text-align: center; display: inline-block; margin-top: 10px;">Contact Us</a>
485                    <a href="contact.html?interest=Enterprise#request-form" class="btn btn-outline" style="width: 100%; text-align: center; display: inline-block; margin-top: 10px;">Request Form</a>
486                `;
487                return;
488            }
489            const basePrice = parseFloat(currentBilling === 'annual' ? currentPlan.annual_price : currentPlan.monthly_price);
490            let discountAmount = 0;
491            let finalPrice = basePrice;
492
493            if (appliedDiscount) {
494                if (appliedDiscount.type === 'percentage') {
495                    discountAmount = (basePrice * appliedDiscount.discount_value) / 100;
496                } else {
497                    discountAmount = appliedDiscount.discount_value;
498                }
499                finalPrice = Math.max(0, basePrice - discountAmount);
500            }
501
502            document.getElementById('subtotal').textContent = `$${basePrice.toFixed(2)}`;
503            document.getElementById('discount-amount').textContent = `-$${discountAmount.toFixed(2)}`;
504            document.getElementById('total-price').textContent = `$${finalPrice.toFixed(2)}`;
505
506            if (discountAmount > 0) {
507                document.getElementById('discount-row').style.display = 'flex';
508            } else {
509                document.getElementById('discount-row').style.display = 'none';
510            }
511
512            // Update PayPal button
513            renderPayPalButton(finalPrice);
514        }
515
516        async function applyDiscount() {
517            const code = document.getElementById('discount-code').value.trim().toUpperCase();
518            
519            if (!code) {
520                showAlert('Please enter a discount code', 'error');
521                return;
522            }
523
524            try {
525                // Validate discount code with backend
526                const response = await fetch(`${API_URL}
526/payment/validate-discount`, {
527                    method: 'POST',
528                    headers: {
529                        'Content-Type': 'application/json'
530                    },
531                    body: JSON.stringify({
532                        code: code,
533                        tier: selectedTier,
534                        billing: currentBilling
535                    })
536                });
537
538                const data = await response.json();
539
540                if (data.success && data.valid) {
541                    appliedDiscount = data.discount;
542                    document.getElementById('discount-details').textContent = 
543                        `${code} - ${data.discount.discount_value}% off`;
544                    document.getElementById('discount-applied').classList.add('show');
545                    showAlert(`Discount code "${code}" applied successfully!`, 'success');
546                    updatePricing();
547                } else {
548                    showAlert(data.message || 'Invalid discount code', 'error');
549                }
550            } catch (error) {
551                console.error('Apply discount error:', error);
552                showAlert('Failed to apply discount code', 'error');
553            }
554        }
555
556        function renderPayPalButton(amount) {
557            if (isEnterprisePlan()) {
558                return;
559            }
560            // Clear existing button
561            document.getElementById('paypal-button-container').innerHTML = '';
562
563            // Check if PayPal SDK is loaded
564            if (typeof paypal === 'undefined' || !paypalClientId) {
565                document.getElementById('paypal-button-container').innerHTML = `
566                    <div style="background: #f8f9fa; border-radius: 12px; padding: 30px; text-align: center;">
567                        <div class="spinner"></div>
568                        <p style="color: #5f6368; margin-top: 15px;">Loading PayPal...</p>
569                        <button onclick="proceedToPayment()" style="margin-top: 20px; padding: 15px 40px; background: #0070ba; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer;">
570                            Continue to PayPal
571                        </button>
572                    </div>
573                `;
574                return;
575            }
576
577            // Render PayPal subscription button
578            try {
579                paypal.Buttons({
580                    style: {
581                        layout: 'vertical',
582                        color: 'blue',
583                        shape: 'rect',
584                        label: 'subscribe'
585                    },
586                    createSubscription: function(data, actions) {
587                        return fetch(`${API_URL}/payment/create-subscription`, {
588                            method: 'POST',
589                            headers: {
590                                'Authorization': `Bearer ${localStorage.getItem('token')}`,
591                                'Content-Type': 'application/json'
592                            },
593                            body: JSON.stringify({
594                                tier: selectedTier,
595                                billing: currentBilling,
596                                discountCode: appliedDiscount?.code || null
597                            })
598                        })
599                        .then(res => res.json())
600                        .then(data => {
601                            if (data.success && data.subscriptionId) {
602                                return data.subscriptionId;
603                            } else {
604                                throw new Error(data.error || 'Failed to create subscription');
605                            }
606                        });
607                    },
608                    onApprove: function(data, actions) {
609                        // Redirect to success page
610                        window.location.href = `payment-success.html?subscription=${data.subscriptionID}&tier=${selectedTier}&billing=${currentBilling}`;
611                    },
612                    onCancel: function(data) {
613                        // Redirect to cancel page
614                        window.location.href = `payment-cancel.html?tier=${selectedTier}`;
615                    },
616                    onError: function(err) {
617                        console.error('PayPal error:', err);
618                        showAlert('Payment failed. Please try again.', 'error');
619                        // Fallback to manual redirect
620                        setTimeout(() => {
621                            proceedToPayment();
622                        }, 2000);
623                    }
624                }).render('#paypal-button-container');
625            } catch (error) {
626                console.error('PayPal button render error:', error);
627                // Fallback button
628                document.getElementById('paypal-button-container').innerHTML = `
629                    <button onclick="proceedToPayment()" style="width: 100%; padding: 15px 40px; background: #0070ba; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer;">
630                        Pay $${amount.toFixed(2)} with PayPal
631                    </button>
632                `;
633            }
634        }
635
636        async function proceedToPayment() {
637            showAlert('Connecting to PayPal...', 'success');
638            
639            try {
640                const token = localStorage.getItem('token');
641                if (!token) {
642                    window.location.href = 'login.html';
643                    return;
644                }
645
646                const response = await fetch(`${API_URL}
646/payment/create-subscription`, {
647                    method: 'POST',
648                    headers: {
649                        'Authorization': `Bearer ${token}`,
650                        'Content-Type': 'application/json'
651                    },
652                    body: JSON.stringify({
653                        tier: selectedTier,
654                        billing: currentBilling,
655                        discountCode: appliedDiscount?.code || null
656                    })
657                });
658
659                const data = await response.json();
660
661                if (data.success && data.approvalUrl) {
662                    // Redirect to PayPal
663                    window.location.href = data.approvalUrl;
664                } else {
665                    showAlert(data.error || 'Failed to create subscription', 'error');
666                }
667            } catch (error) {
668                console.error('Payment error:', error);
669                showAlert('Failed to process payment', 'error');
670            }
671        }
672
673        function showAlert(message, type) {
674            const alertDiv = document.getElementById('alert');
675            alertDiv.innerHTML = `<div class="alert alert-${type}">${message}</div>`;
676            setTimeout(() => {
677                alertDiv.innerHTML = '';
678            }, 5000);
679        }
680    </script>
680
681</body>
682</html>
683

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.