evaldoc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.env.staging.example +19 -0
  2. package/.firebaserc +6 -0
  3. package/.github/workflows/auto-merge-to-main.yml +28 -0
  4. package/.github/workflows/ci.yml +27 -0
  5. package/.github/workflows/npxhub-publish.yml +170 -0
  6. package/.prettierignore +4 -0
  7. package/.prettierrc.json +7 -0
  8. package/README.md +142 -0
  9. package/SRS.md +448 -0
  10. package/apphosting.staging.yaml +48 -0
  11. package/apphosting.yaml +50 -0
  12. package/bin/evaldoc.cjs +105 -0
  13. package/cors.json +8 -0
  14. package/docs/ARCHITECTURE.md +769 -0
  15. package/docs/ARCHITECTURE_SUMMARY.md +323 -0
  16. package/docs/DEPLOYMENT.md +86 -0
  17. package/docs/DEPLOYMENT_STEPS.md +373 -0
  18. package/docs/DEPLOY_FIRESTORE_RULES.md +239 -0
  19. package/docs/DEPLOY_STORAGE_RULES.md +82 -0
  20. package/docs/DOCTOR_TUTORIAL.md +467 -0
  21. package/docs/FIREBASE_STORAGE_CORS_FIX.md +131 -0
  22. package/docs/FIXES_APPLIED.md +128 -0
  23. package/docs/IMPLEMENTATION.md +498 -0
  24. package/docs/IMPLEMENTATION_COMPLETE.md +317 -0
  25. package/docs/LLM-SETUP.md +216 -0
  26. package/docs/MANUAL_TEST_GUIDE.md +327 -0
  27. package/docs/PATIENT_GUIDE.md +451 -0
  28. package/docs/PLATFORM_OVERVIEW.md +194 -0
  29. package/docs/QUICKSTART.md +252 -0
  30. package/docs/QUICK_REFERENCE.md +222 -0
  31. package/docs/READY_TO_TEST.md +383 -0
  32. package/docs/RUN.md +237 -0
  33. package/docs/START_TESTING.md +130 -0
  34. package/docs/STRIPE_CONNECT_SETUP.md +454 -0
  35. package/docs/SYSTEM_READY.md +345 -0
  36. package/docs/THREADS_IMPLEMENTATION_COMPLETE.md +431 -0
  37. package/docs/THREAD_IMPLEMENTATION_GUIDE.md +345 -0
  38. package/docs/firebasefiles.md +21 -0
  39. package/docs/homepage.md +1208 -0
  40. package/docs/payments.md +159 -0
  41. package/eslint.config.js +76 -0
  42. package/firebase.json +27 -0
  43. package/firestore.indexes.json +88 -0
  44. package/firestore.rules +125 -0
  45. package/functions/package-lock.json +2884 -0
  46. package/functions/package.json +20 -0
  47. package/functions/src/analytics.js +101 -0
  48. package/functions/src/audit.js +28 -0
  49. package/functions/src/auth.js +19 -0
  50. package/functions/src/drafts.js +128 -0
  51. package/functions/src/feedback.js +164 -0
  52. package/functions/src/firebase-init.js +29 -0
  53. package/functions/src/i18n.js +134 -0
  54. package/functions/src/limits.js +27 -0
  55. package/functions/src/llm.js +271 -0
  56. package/functions/src/ner.js +162 -0
  57. package/functions/src/notifications.js +273 -0
  58. package/functions/src/orgs.js +127 -0
  59. package/functions/src/patient-api.js +545 -0
  60. package/functions/src/payment.js +830 -0
  61. package/functions/src/safety.js +188 -0
  62. package/functions/src/stripe-config.js +33 -0
  63. package/functions/src/topics.js +94 -0
  64. package/functions/src/triage.js +191 -0
  65. package/functions/src/utils.js +28 -0
  66. package/functions/src/validate.js +177 -0
  67. package/functions/test-firebase.js +36 -0
  68. package/jest.config.js +18 -0
  69. package/package.json +52 -0
  70. package/public/404.html +93 -0
  71. package/public/app/analytics.html +336 -0
  72. package/public/app/analytics.js +177 -0
  73. package/public/app/app.css +1400 -0
  74. package/public/app/app.js +1754 -0
  75. package/public/app/config.js +14 -0
  76. package/public/app/inbox-threads.js +843 -0
  77. package/public/app/inbox.html +598 -0
  78. package/public/app/inbox.js +397 -0
  79. package/public/app/index.html +612 -0
  80. package/public/assets/logo.jpg +0 -0
  81. package/public/chat.html +137 -0
  82. package/public/css/chat.css +870 -0
  83. package/public/css/landing-enhance.css +298 -0
  84. package/public/css/styles.css +1198 -0
  85. package/public/favicon.svg +5 -0
  86. package/public/find-doctor.html +220 -0
  87. package/public/index.html +399 -0
  88. package/public/js/chat-threads.js +513 -0
  89. package/public/js/chat.js +832 -0
  90. package/public/js/search.js +41 -0
  91. package/public/privacy.html +101 -0
  92. package/public/robots.txt +6 -0
  93. package/public/site.webmanifest +12 -0
  94. package/public/terms.html +110 -0
  95. package/scripts/create-test-provider.js +55 -0
  96. package/scripts/deploy-staging.sh +51 -0
  97. package/scripts/fix-provider.js +39 -0
  98. package/scripts/quick-test.sh +37 -0
  99. package/scripts/test-chat.sh +54 -0
  100. package/scripts/test-multi-turn.js +137 -0
  101. package/scripts/test-ner-simple.js +28 -0
  102. package/scripts/test-ner.js +60 -0
  103. package/scripts/test-stripe-connect.js +304 -0
  104. package/scripts/test-webhook-secret.sh +24 -0
  105. package/server.js +3864 -0
  106. package/storage.rules +22 -0
  107. package/test-api.json +1 -0
  108. package/tests/emulator/README.md +38 -0
  109. package/tests/emulator/counters.emulator.test.js +81 -0
  110. package/tests/integration/api-account-selfserve.test.js +312 -0
  111. package/tests/integration/api-admin-digest.test.js +202 -0
  112. package/tests/integration/api-chat-satisfaction.test.js +259 -0
  113. package/tests/integration/api-doctor-drafts.test.js +236 -0
  114. package/tests/integration/api-doctor-inbox.test.js +401 -0
  115. package/tests/integration/api-org.test.js +328 -0
  116. package/tests/integration/api-payment.test.js +664 -0
  117. package/tests/integration/api-provider-settings.test.js +475 -0
  118. package/tests/integration/api-qr.test.js +121 -0
  119. package/tests/integration/api-security.test.js +241 -0
  120. package/tests/integration/api-stripe-search.test.js +303 -0
  121. package/tests/integration/api-webhook-deletion.test.js +231 -0
  122. package/tests/mocks/firebase-admin.js +220 -0
  123. package/tests/unit/analytics.test.js +151 -0
  124. package/tests/unit/audit-bugs.test.js +203 -0
  125. package/tests/unit/chat-ui-logic.test.js +283 -0
  126. package/tests/unit/drafts.test.js +104 -0
  127. package/tests/unit/feedback.test.js +148 -0
  128. package/tests/unit/i18n.test.js +229 -0
  129. package/tests/unit/llm-retry.test.js +122 -0
  130. package/tests/unit/notifications.test.js +155 -0
  131. package/tests/unit/orgs.test.js +328 -0
  132. package/tests/unit/payment.test.js +889 -0
  133. package/tests/unit/safety-edge-cases.test.js +509 -0
  134. package/tests/unit/safety-srs-compliance.test.js +252 -0
  135. package/tests/unit/safety.test.js +291 -0
  136. package/tests/unit/server-helpers.test.js +774 -0
  137. package/tests/unit/topics-limits.test.js +158 -0
  138. package/tests/unit/triage.test.js +152 -0
  139. package/tests/unit/validate.test.js +408 -0
@@ -0,0 +1,28 @@
1
+ // Simple test to verify NER is working with standard entities
2
+ require('dotenv').config();
3
+ const { HfInference } = require('@huggingface/inference');
4
+
5
+ async function quickTest() {
6
+ const client = new HfInference(process.env.HUGGINGFACE_API_KEY);
7
+
8
+ const testText = "John Smith from New York has diabetes and takes insulin.";
9
+
10
+ console.log('Testing:', testText);
11
+ console.log('\n');
12
+
13
+ try {
14
+ const result = await client.tokenClassification({
15
+ model: 'dslim/bert-base-NER',
16
+ inputs: testText
17
+ });
18
+
19
+ console.log('✓ NER Working! Detected entities:');
20
+ result.forEach(entity => {
21
+ console.log(` - "${entity.word}" [${entity.entity_group || entity.entity}] (${(entity.score * 100).toFixed(1)}%)`);
22
+ });
23
+ } catch (error) {
24
+ console.error('Error:', error.message);
25
+ }
26
+ }
27
+
28
+ quickTest();
@@ -0,0 +1,60 @@
1
+ // Test script for NER integration
2
+ require('dotenv').config();
3
+ const { analyzeMedicalSafety } = require('./functions/src/ner');
4
+
5
+ async function testNER() {
6
+ console.log('🧪 Testing Medical NER Integration\n');
7
+
8
+ const testCases = [
9
+ {
10
+ name: 'Urgent Symptoms',
11
+ text: 'I have severe chest pain and difficulty breathing'
12
+ },
13
+ {
14
+ name: 'Disease Mention',
15
+ text: 'I was diagnosed with diabetes and hypertension'
16
+ },
17
+ {
18
+ name: 'Medication Query',
19
+ text: 'I am taking metformin and lisinopril for my conditions'
20
+ },
21
+ {
22
+ name: 'General Question',
23
+ text: 'What are the symptoms of the flu?'
24
+ }
25
+ ];
26
+
27
+ for (const testCase of testCases) {
28
+ console.log(`\n📋 Test: ${testCase.name}`);
29
+ console.log(` Input: "${testCase.text}"`);
30
+
31
+ try {
32
+ const result = await analyzeMedicalSafety(testCase.text);
33
+
34
+ console.log(` Entities Found: ${result.entities.length}`);
35
+ if (result.entities.length > 0) {
36
+ result.entities.forEach(entity => {
37
+ console.log(` - "${entity.text}" [${entity.type}] (confidence: ${(entity.confidence * 100).toFixed(1)}%)`);
38
+ });
39
+ }
40
+
41
+ console.log(` Analysis:`);
42
+ console.log(` Has Symptoms: ${result.analysis.hasSymptoms}`);
43
+ console.log(` Has Diseases: ${result.analysis.hasDiseases}`);
44
+ console.log(` Has Medications: ${result.analysis.hasMedications}`);
45
+ console.log(` Should Escalate: ${result.shouldEscalate}`);
46
+ console.log(` Requires Disclaimer: ${result.requiresDisclaimer}`);
47
+
48
+ if (result.analysis.urgentSymptoms.length > 0) {
49
+ console.log(` ⚠️ URGENT: ${result.analysis.urgentSymptoms.join(', ')}`);
50
+ }
51
+
52
+ } catch (error) {
53
+ console.error(` ❌ Error: ${error.message}`);
54
+ }
55
+ }
56
+
57
+ console.log('\n✅ NER Testing Complete\n');
58
+ }
59
+
60
+ testNER().catch(console.error);
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test Script for Stripe Connect Payment Flow
5
+ * Tests: Provider creation → Connect setup → Pricing → Patient subscription
6
+ */
7
+
8
+ const admin = require('firebase-admin');
9
+ const fetch = require('node-fetch');
10
+
11
+ // Load environment variables
12
+ require('dotenv').config();
13
+
14
+ const BASE_URL = 'http://localhost:54112';
15
+
16
+ // Initialize Firebase Admin
17
+ if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
18
+ admin.initializeApp({
19
+ credential: admin.credential.cert(require(process.env.GOOGLE_APPLICATION_CREDENTIALS)),
20
+ projectId: process.env.PROJECT_ID || 'evaldocplatform'
21
+ });
22
+ }
23
+
24
+ const db = admin.firestore();
25
+
26
+ // ANSI color codes for pretty output
27
+ const colors = {
28
+ reset: '\x1b[0m',
29
+ green: '\x1b[32m',
30
+ red: '\x1b[31m',
31
+ yellow: '\x1b[33m',
32
+ blue: '\x1b[34m',
33
+ cyan: '\x1b[36m'
34
+ };
35
+
36
+ function log(message, color = 'reset') {
37
+ console.log(`${colors[color]}${message}${colors.reset}`);
38
+ }
39
+
40
+ function step(number, title) {
41
+ console.log('\n' + '='.repeat(60));
42
+ log(`STEP ${number}: ${title}`, 'cyan');
43
+ console.log('='.repeat(60));
44
+ }
45
+
46
+ async function testFlow() {
47
+ try {
48
+ log('\n🧪 Starting Stripe Connect Payment Flow Test\n', 'blue');
49
+
50
+ // Step 1: Create test user in Firebase Auth
51
+ step(1, 'Create Test Provider User');
52
+
53
+ const testEmail = `test-provider-${Date.now()}@evaldoc.test`;
54
+ const testPassword = 'TestPassword123!';
55
+
56
+ log(`Creating user: ${testEmail}`, 'yellow');
57
+
58
+ let userRecord;
59
+ try {
60
+ userRecord = await admin.auth().createUser({
61
+ email: testEmail,
62
+ password: testPassword,
63
+ displayName: 'Dr. Test Provider'
64
+ });
65
+ log(`✓ User created: ${userRecord.uid}`, 'green');
66
+ } catch (error) {
67
+ log(`✗ Error creating user: ${error.message}`, 'red');
68
+ throw error;
69
+ }
70
+
71
+ // Step 2: Create provider document
72
+ step(2, 'Create Provider Document');
73
+
74
+ const providerData = {
75
+ handle: `test-provider-${Date.now()}`,
76
+ displayName: 'Dr. Test Provider',
77
+ specialty: 'Test Medicine',
78
+ country: 'US',
79
+ email: testEmail,
80
+ status: 'published',
81
+ welcomeMessage: 'Welcome to my test practice!',
82
+ topics: ['medication_info_safety', 'general_health_info'],
83
+ brandPrimaryColor: '#0e1a14',
84
+ brandSecondaryColor: '#4fb3a4',
85
+ currency: 'usd',
86
+ planMonthlyLimit: 1000,
87
+ createdAt: admin.firestore.FieldValue.serverTimestamp(),
88
+ updatedAt: admin.firestore.FieldValue.serverTimestamp()
89
+ };
90
+
91
+ log(`Creating provider: ${providerData.handle}`, 'yellow');
92
+
93
+ await db.collection('providers').doc(userRecord.uid).set(providerData);
94
+ log(`✓ Provider created with handle: ${providerData.handle}`, 'green');
95
+
96
+ // Step 3: Get Firebase ID token (simulate login)
97
+ step(3, 'Get Authentication Token');
98
+
99
+ log('Getting custom token...', 'yellow');
100
+ const customToken = await admin.auth().createCustomToken(userRecord.uid);
101
+ log(`✓ Custom token generated`, 'green');
102
+
103
+ // Step 4: Create Stripe Connect Account
104
+ step(4, 'Create Stripe Connect Account (Programmatic)');
105
+
106
+ log('Calling /api/stripe/create-connect-account...', 'yellow');
107
+
108
+ const connectResponse = await fetch(`${BASE_URL}/api/stripe/create-connect-account`, {
109
+ method: 'POST',
110
+ headers: {
111
+ 'Authorization': `Bearer ${customToken}`,
112
+ 'Content-Type': 'application/json'
113
+ }
114
+ });
115
+
116
+ if (!connectResponse.ok) {
117
+ const error = await connectResponse.text();
118
+ log(`✗ Error: ${error}`, 'red');
119
+ throw new Error(`Connect account creation failed: ${error}`);
120
+ }
121
+
122
+ const connectResult = await connectResponse.json();
123
+ log(`✓ Connect account created: ${connectResult.accountId}`, 'green');
124
+ log(` Status: ${connectResult.onboardingStatus}`, 'yellow');
125
+
126
+ // Step 5: Get onboarding link
127
+ step(5, 'Generate Onboarding Link');
128
+
129
+ log('Calling /api/stripe/create-account-link...', 'yellow');
130
+
131
+ const linkResponse = await fetch(`${BASE_URL}/api/stripe/create-account-link`, {
132
+ method: 'POST',
133
+ headers: {
134
+ 'Authorization': `Bearer ${customToken}`,
135
+ 'Content-Type': 'application/json'
136
+ }
137
+ });
138
+
139
+ if (!linkResponse.ok) {
140
+ const error = await linkResponse.text();
141
+ log(`✗ Error: ${error}`, 'red');
142
+ throw new Error(`Account link creation failed: ${error}`);
143
+ }
144
+
145
+ const linkResult = await linkResponse.json();
146
+ log(`✓ Onboarding link generated`, 'green');
147
+ log(` URL: ${linkResult.url}`, 'cyan');
148
+ log(` Expires: ${new Date(linkResult.expiresAt * 1000).toLocaleString()}`, 'yellow');
149
+
150
+ log('\n⚠️ MANUAL STEP REQUIRED:', 'yellow');
151
+ log(' The provider would now visit this URL to complete Stripe onboarding.', 'yellow');
152
+ log(' In test mode, you can use test business details.', 'yellow');
153
+ log(' For this automated test, we\'ll simulate the account being approved.', 'yellow');
154
+
155
+ // Step 6: Simulate account approval (in real flow, this comes from Stripe webhook)
156
+ step(6, 'Simulate Stripe Account Approval');
157
+
158
+ log('Updating provider to simulate approved account...', 'yellow');
159
+ await db.collection('providers').doc(userRecord.uid).update({
160
+ chargesEnabled: true,
161
+ payoutsEnabled: true,
162
+ stripeOnboardingStatus: 'COMPLETE'
163
+ });
164
+ log(`✓ Provider account marked as approved`, 'green');
165
+
166
+ // Step 7: Set pricing
167
+ step(7, 'Set Subscription Pricing');
168
+
169
+ const pricingData = {
170
+ currency: 'usd',
171
+ prices: {
172
+ day: 5.00,
173
+ week: 20.00,
174
+ month: 50.00
175
+ }
176
+ };
177
+
178
+ log(`Setting pricing: Day=$${pricingData.prices.day}, Week=$${pricingData.prices.week}, Month=$${pricingData.prices.month}`, 'yellow');
179
+
180
+ const pricingResponse = await fetch(`${BASE_URL}/api/stripe/set-pricing`, {
181
+ method: 'POST',
182
+ headers: {
183
+ 'Authorization': `Bearer ${customToken}`,
184
+ 'Content-Type': 'application/json'
185
+ },
186
+ body: JSON.stringify(pricingData)
187
+ });
188
+
189
+ if (!pricingResponse.ok) {
190
+ const error = await pricingResponse.text();
191
+ log(`✗ Error: ${error}`, 'red');
192
+ throw new Error(`Pricing setup failed: ${error}`);
193
+ }
194
+
195
+ const pricingResult = await pricingResponse.json();
196
+ log(`✓ Pricing configured`, 'green');
197
+ log(` Product ID: ${pricingResult.productId}`, 'yellow');
198
+ log(` Price IDs:`, 'yellow');
199
+ Object.entries(pricingResult.priceIds).forEach(([interval, priceId]) => {
200
+ log(` ${interval}: ${priceId}`, 'cyan');
201
+ });
202
+
203
+ // Step 8: Patient creates checkout session
204
+ step(8, 'Patient Creates Checkout Session');
205
+
206
+ const patientSessionId = `test-patient-${Date.now()}`;
207
+
208
+ log(`Patient session: ${patientSessionId}`, 'yellow');
209
+ log(`Subscribing to: ${providerData.handle} (monthly plan)`, 'yellow');
210
+
211
+ const checkoutResponse = await fetch(`${BASE_URL}/api/payment/create-checkout-session`, {
212
+ method: 'POST',
213
+ headers: {
214
+ 'Content-Type': 'application/json'
215
+ },
216
+ body: JSON.stringify({
217
+ handle: providerData.handle,
218
+ sessionId: patientSessionId,
219
+ interval: 'month'
220
+ })
221
+ });
222
+
223
+ if (!checkoutResponse.ok) {
224
+ const error = await checkoutResponse.text();
225
+ log(`✗ Error: ${error}`, 'red');
226
+ throw new Error(`Checkout session creation failed: ${error}`);
227
+ }
228
+
229
+ const checkoutResult = await checkoutResponse.json();
230
+ log(`✓ Checkout session created`, 'green');
231
+ log(` Session ID: ${checkoutResult.sessionId}`, 'yellow');
232
+ log(` Checkout URL: ${checkoutResult.url}`, 'cyan');
233
+
234
+ log('\n⚠️ MANUAL STEP REQUIRED:', 'yellow');
235
+ log(' The patient would now visit this URL to complete payment.', 'yellow');
236
+ log(' In test mode, use card: 4242 4242 4242 4242', 'yellow');
237
+ log(' After payment, Stripe webhooks would grant access.', 'yellow');
238
+
239
+ // Step 9: Check access (before payment)
240
+ step(9, 'Check Patient Access (Before Payment)');
241
+
242
+ const accessCheckResponse = await fetch(`${BASE_URL}/api/payment/check-access`, {
243
+ method: 'POST',
244
+ headers: {
245
+ 'Content-Type': 'application/json'
246
+ },
247
+ body: JSON.stringify({
248
+ handle: providerData.handle,
249
+ sessionId: patientSessionId
250
+ })
251
+ });
252
+
253
+ const accessResult = await accessCheckResponse.json();
254
+ log(`Access status: ${accessResult.hasAccess ? 'GRANTED' : 'DENIED'}`, accessResult.hasAccess ? 'green' : 'yellow');
255
+ log(`Access type: ${accessResult.type || 'none'}`, 'yellow');
256
+
257
+ // Summary
258
+ step('✓', 'Test Summary');
259
+
260
+ log('\n✅ All backend steps completed successfully!\n', 'green');
261
+
262
+ log('What was tested:', 'cyan');
263
+ log(' ✓ Provider user creation', 'green');
264
+ log(' ✓ Provider document creation', 'green');
265
+ log(' ✓ Stripe Connect account creation (programmatic)', 'green');
266
+ log(' ✓ Onboarding link generation', 'green');
267
+ log(' ✓ Account approval simulation', 'green');
268
+ log(' ✓ Subscription pricing setup', 'green');
269
+ log(' ✓ Checkout session creation', 'green');
270
+ log(' ✓ Access control check', 'green');
271
+
272
+ log('\nManual steps needed to complete flow:', 'yellow');
273
+ log(' 1. Visit onboarding URL to complete Stripe Connect setup', 'yellow');
274
+ log(' 2. Visit checkout URL and pay with test card (4242 4242 4242 4242)', 'yellow');
275
+ log(' 3. Set up webhook endpoint to receive Stripe events', 'yellow');
276
+
277
+ log('\nTest data created:', 'cyan');
278
+ log(` Provider UID: ${userRecord.uid}`, 'cyan');
279
+ log(` Provider Handle: ${providerData.handle}`, 'cyan');
280
+ log(` Provider Email: ${testEmail}`, 'cyan');
281
+ log(` Stripe Account: ${connectResult.accountId}`, 'cyan');
282
+ log(` Patient Session: ${patientSessionId}`, 'cyan');
283
+
284
+ log('\nCleanup (if needed):', 'yellow');
285
+ log(` firebase auth:users:delete ${userRecord.uid}`, 'yellow');
286
+ log(` Delete provider document: providers/${userRecord.uid}`, 'yellow');
287
+
288
+ } catch (error) {
289
+ log(`\n✗ Test failed: ${error.message}`, 'red');
290
+ console.error(error);
291
+ process.exit(1);
292
+ }
293
+ }
294
+
295
+ // Run the test
296
+ testFlow()
297
+ .then(() => {
298
+ log('\n✓ Test completed successfully!\n', 'green');
299
+ process.exit(0);
300
+ })
301
+ .catch(error => {
302
+ log(`\n✗ Test failed: ${error.message}\n`, 'red');
303
+ process.exit(1);
304
+ });
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+
3
+ # Test webhook secret is loaded
4
+ echo "🔍 Checking Stripe Webhook Secret..."
5
+ echo ""
6
+
7
+ if grep -q "STRIPE_WEBHOOK_SECRET=whsec_" .env; then
8
+ echo "✅ Webhook secret found in .env"
9
+ SECRET=$(grep "STRIPE_WEBHOOK_SECRET" .env | cut -d'=' -f2)
10
+ if [ ! -z "$SECRET" ] && [ "$SECRET" != "whsec_" ]; then
11
+ echo "✅ Secret is configured: ${SECRET:0:20}..."
12
+ echo ""
13
+ echo "🎉 Ready to receive webhooks!"
14
+ echo ""
15
+ echo "Next steps:"
16
+ echo " 1. Go to provider dashboard: http://localhost:54112/app"
17
+ echo " 2. Test payment flow using browser console"
18
+ echo " 3. See MANUAL_TEST_GUIDE.md for commands"
19
+ else
20
+ echo "⚠️ Secret is empty"
21
+ fi
22
+ else
23
+ echo "❌ Webhook secret not found"
24
+ fi