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.
- package/.env.staging.example +19 -0
- package/.firebaserc +6 -0
- package/.github/workflows/auto-merge-to-main.yml +28 -0
- package/.github/workflows/ci.yml +27 -0
- package/.github/workflows/npxhub-publish.yml +170 -0
- package/.prettierignore +4 -0
- package/.prettierrc.json +7 -0
- package/README.md +142 -0
- package/SRS.md +448 -0
- package/apphosting.staging.yaml +48 -0
- package/apphosting.yaml +50 -0
- package/bin/evaldoc.cjs +105 -0
- package/cors.json +8 -0
- package/docs/ARCHITECTURE.md +769 -0
- package/docs/ARCHITECTURE_SUMMARY.md +323 -0
- package/docs/DEPLOYMENT.md +86 -0
- package/docs/DEPLOYMENT_STEPS.md +373 -0
- package/docs/DEPLOY_FIRESTORE_RULES.md +239 -0
- package/docs/DEPLOY_STORAGE_RULES.md +82 -0
- package/docs/DOCTOR_TUTORIAL.md +467 -0
- package/docs/FIREBASE_STORAGE_CORS_FIX.md +131 -0
- package/docs/FIXES_APPLIED.md +128 -0
- package/docs/IMPLEMENTATION.md +498 -0
- package/docs/IMPLEMENTATION_COMPLETE.md +317 -0
- package/docs/LLM-SETUP.md +216 -0
- package/docs/MANUAL_TEST_GUIDE.md +327 -0
- package/docs/PATIENT_GUIDE.md +451 -0
- package/docs/PLATFORM_OVERVIEW.md +194 -0
- package/docs/QUICKSTART.md +252 -0
- package/docs/QUICK_REFERENCE.md +222 -0
- package/docs/READY_TO_TEST.md +383 -0
- package/docs/RUN.md +237 -0
- package/docs/START_TESTING.md +130 -0
- package/docs/STRIPE_CONNECT_SETUP.md +454 -0
- package/docs/SYSTEM_READY.md +345 -0
- package/docs/THREADS_IMPLEMENTATION_COMPLETE.md +431 -0
- package/docs/THREAD_IMPLEMENTATION_GUIDE.md +345 -0
- package/docs/firebasefiles.md +21 -0
- package/docs/homepage.md +1208 -0
- package/docs/payments.md +159 -0
- package/eslint.config.js +76 -0
- package/firebase.json +27 -0
- package/firestore.indexes.json +88 -0
- package/firestore.rules +125 -0
- package/functions/package-lock.json +2884 -0
- package/functions/package.json +20 -0
- package/functions/src/analytics.js +101 -0
- package/functions/src/audit.js +28 -0
- package/functions/src/auth.js +19 -0
- package/functions/src/drafts.js +128 -0
- package/functions/src/feedback.js +164 -0
- package/functions/src/firebase-init.js +29 -0
- package/functions/src/i18n.js +134 -0
- package/functions/src/limits.js +27 -0
- package/functions/src/llm.js +271 -0
- package/functions/src/ner.js +162 -0
- package/functions/src/notifications.js +273 -0
- package/functions/src/orgs.js +127 -0
- package/functions/src/patient-api.js +545 -0
- package/functions/src/payment.js +830 -0
- package/functions/src/safety.js +188 -0
- package/functions/src/stripe-config.js +33 -0
- package/functions/src/topics.js +94 -0
- package/functions/src/triage.js +191 -0
- package/functions/src/utils.js +28 -0
- package/functions/src/validate.js +177 -0
- package/functions/test-firebase.js +36 -0
- package/jest.config.js +18 -0
- package/package.json +52 -0
- package/public/404.html +93 -0
- package/public/app/analytics.html +336 -0
- package/public/app/analytics.js +177 -0
- package/public/app/app.css +1400 -0
- package/public/app/app.js +1754 -0
- package/public/app/config.js +14 -0
- package/public/app/inbox-threads.js +843 -0
- package/public/app/inbox.html +598 -0
- package/public/app/inbox.js +397 -0
- package/public/app/index.html +612 -0
- package/public/assets/logo.jpg +0 -0
- package/public/chat.html +137 -0
- package/public/css/chat.css +870 -0
- package/public/css/landing-enhance.css +298 -0
- package/public/css/styles.css +1198 -0
- package/public/favicon.svg +5 -0
- package/public/find-doctor.html +220 -0
- package/public/index.html +399 -0
- package/public/js/chat-threads.js +513 -0
- package/public/js/chat.js +832 -0
- package/public/js/search.js +41 -0
- package/public/privacy.html +101 -0
- package/public/robots.txt +6 -0
- package/public/site.webmanifest +12 -0
- package/public/terms.html +110 -0
- package/scripts/create-test-provider.js +55 -0
- package/scripts/deploy-staging.sh +51 -0
- package/scripts/fix-provider.js +39 -0
- package/scripts/quick-test.sh +37 -0
- package/scripts/test-chat.sh +54 -0
- package/scripts/test-multi-turn.js +137 -0
- package/scripts/test-ner-simple.js +28 -0
- package/scripts/test-ner.js +60 -0
- package/scripts/test-stripe-connect.js +304 -0
- package/scripts/test-webhook-secret.sh +24 -0
- package/server.js +3864 -0
- package/storage.rules +22 -0
- package/test-api.json +1 -0
- package/tests/emulator/README.md +38 -0
- package/tests/emulator/counters.emulator.test.js +81 -0
- package/tests/integration/api-account-selfserve.test.js +312 -0
- package/tests/integration/api-admin-digest.test.js +202 -0
- package/tests/integration/api-chat-satisfaction.test.js +259 -0
- package/tests/integration/api-doctor-drafts.test.js +236 -0
- package/tests/integration/api-doctor-inbox.test.js +401 -0
- package/tests/integration/api-org.test.js +328 -0
- package/tests/integration/api-payment.test.js +664 -0
- package/tests/integration/api-provider-settings.test.js +475 -0
- package/tests/integration/api-qr.test.js +121 -0
- package/tests/integration/api-security.test.js +241 -0
- package/tests/integration/api-stripe-search.test.js +303 -0
- package/tests/integration/api-webhook-deletion.test.js +231 -0
- package/tests/mocks/firebase-admin.js +220 -0
- package/tests/unit/analytics.test.js +151 -0
- package/tests/unit/audit-bugs.test.js +203 -0
- package/tests/unit/chat-ui-logic.test.js +283 -0
- package/tests/unit/drafts.test.js +104 -0
- package/tests/unit/feedback.test.js +148 -0
- package/tests/unit/i18n.test.js +229 -0
- package/tests/unit/llm-retry.test.js +122 -0
- package/tests/unit/notifications.test.js +155 -0
- package/tests/unit/orgs.test.js +328 -0
- package/tests/unit/payment.test.js +889 -0
- package/tests/unit/safety-edge-cases.test.js +509 -0
- package/tests/unit/safety-srs-compliance.test.js +252 -0
- package/tests/unit/safety.test.js +291 -0
- package/tests/unit/server-helpers.test.js +774 -0
- package/tests/unit/topics-limits.test.js +158 -0
- package/tests/unit/triage.test.js +152 -0
- package/tests/unit/validate.test.js +408 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# 🚀 Start Testing Now!
|
|
2
|
+
|
|
3
|
+
## Fixed: JavaScript Error ✅
|
|
4
|
+
|
|
5
|
+
The provider dashboard JavaScript error has been fixed. You can now proceed with testing!
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick Test (2 Minutes)
|
|
10
|
+
|
|
11
|
+
### 1. Open Provider Dashboard
|
|
12
|
+
```
|
|
13
|
+
http://localhost:54112/app
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### 2. Sign In
|
|
17
|
+
Use your existing provider credentials
|
|
18
|
+
|
|
19
|
+
### 3. Open Browser Console
|
|
20
|
+
Press **F12** → Go to **Console** tab
|
|
21
|
+
|
|
22
|
+
### 4. Run This Test Command
|
|
23
|
+
|
|
24
|
+
Copy and paste this into the console:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
// Test 1: Check if you're logged in
|
|
28
|
+
firebase.auth().currentUser.getIdToken().then(token => {
|
|
29
|
+
console.log('✅ Logged in successfully');
|
|
30
|
+
console.log('Token length:', token.length);
|
|
31
|
+
|
|
32
|
+
// Test 2: Create Stripe Connect account
|
|
33
|
+
return fetch('http://localhost:54112/api/stripe/create-connect-account', {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: {
|
|
36
|
+
'Authorization': `Bearer ${token}`,
|
|
37
|
+
'Content-Type': 'application/json'
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
})
|
|
41
|
+
.then(r => r.json())
|
|
42
|
+
.then(data => {
|
|
43
|
+
console.log('✅ Stripe Connect Response:', data);
|
|
44
|
+
|
|
45
|
+
if (data.accountId) {
|
|
46
|
+
console.log('🎉 SUCCESS! Stripe Connect account created!');
|
|
47
|
+
console.log(' Account ID:', data.accountId);
|
|
48
|
+
console.log(' Status:', data.onboardingStatus);
|
|
49
|
+
console.log('\n📋 Next: See READY_TO_TEST.md for full testing flow');
|
|
50
|
+
} else if (data.error) {
|
|
51
|
+
console.error('❌ Error:', data.error);
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
.catch(err => {
|
|
55
|
+
console.error('❌ Test failed:', err);
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Expected Result
|
|
62
|
+
|
|
63
|
+
You should see:
|
|
64
|
+
```
|
|
65
|
+
✅ Logged in successfully
|
|
66
|
+
Token length: 1000+
|
|
67
|
+
✅ Stripe Connect Response: {accountId: "acct_...", onboardingStatus: "PENDING"}
|
|
68
|
+
🎉 SUCCESS! Stripe Connect account created!
|
|
69
|
+
Account ID: acct_...
|
|
70
|
+
Status: PENDING
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## If It Works...
|
|
76
|
+
|
|
77
|
+
🎉 **Continue with full testing!**
|
|
78
|
+
|
|
79
|
+
Open [READY_TO_TEST.md](READY_TO_TEST.md) and follow Steps 2-9 to test the complete payment flow:
|
|
80
|
+
- Get onboarding link
|
|
81
|
+
- Set pricing
|
|
82
|
+
- Create checkout session
|
|
83
|
+
- Make test payment
|
|
84
|
+
- Verify access
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## If You See Errors...
|
|
89
|
+
|
|
90
|
+
### "Unauthorized" or "401"
|
|
91
|
+
- Make sure you're signed in
|
|
92
|
+
- Refresh the page
|
|
93
|
+
- Try signing out and back in
|
|
94
|
+
|
|
95
|
+
### "Provider not found"
|
|
96
|
+
- Your account needs a provider profile
|
|
97
|
+
- Go through the onboarding form first
|
|
98
|
+
|
|
99
|
+
### Other Errors
|
|
100
|
+
- Check the browser console for details
|
|
101
|
+
- Check server logs (terminal where `node server.js` is running)
|
|
102
|
+
- Make sure server is still running on port 54112
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Server Status Check
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Check if server is running
|
|
110
|
+
curl http://localhost:54112/health
|
|
111
|
+
|
|
112
|
+
# Should return:
|
|
113
|
+
# {"ok":true,"timestamp":...}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## What This Tests
|
|
119
|
+
|
|
120
|
+
✅ Authentication working
|
|
121
|
+
✅ API endpoint responding
|
|
122
|
+
✅ Stripe Connect account creation (programmatic)
|
|
123
|
+
✅ Database updates
|
|
124
|
+
✅ Error handling
|
|
125
|
+
|
|
126
|
+
**Once this works, you're ready for the full payment flow!**
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
**Next Steps**: [READY_TO_TEST.md](READY_TO_TEST.md) → Complete payment testing
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
# Stripe Connect Payment System - Complete Setup Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Your EvalDoc platform now has a **full Stripe Connect marketplace** implementation that allows:
|
|
6
|
+
- **Multiple providers** to sign up and receive payments
|
|
7
|
+
- **Automatic Stripe Connect Express account creation** for each provider
|
|
8
|
+
- **25% platform fee** on all transactions (you keep 25%, provider gets 75%)
|
|
9
|
+
- **Subscription-based access control** (daily/weekly/monthly plans)
|
|
10
|
+
- **Webhook-driven access management** (secure, server-side only)
|
|
11
|
+
|
|
12
|
+
## What Has Been Implemented
|
|
13
|
+
|
|
14
|
+
### ✅ Backend (Complete)
|
|
15
|
+
|
|
16
|
+
1. **Stripe Connect Account Management** ([functions/src/payment.js](functions/src/payment.js))
|
|
17
|
+
- `createConnectAccount()` - Auto-creates Express accounts for providers
|
|
18
|
+
- `createAccountLink()` - Generates onboarding links
|
|
19
|
+
- `refreshAccountStatus()` - Checks onboarding completion
|
|
20
|
+
- `setProviderPricing()` - Creates Stripe Products/Prices
|
|
21
|
+
|
|
22
|
+
2. **Checkout & Subscriptions**
|
|
23
|
+
- `createCheckoutSession()` - Stripe Checkout with 25% platform fee
|
|
24
|
+
- Subscription management with access control
|
|
25
|
+
- Payment intent support for one-time payments
|
|
26
|
+
|
|
27
|
+
3. **Webhook Handler**
|
|
28
|
+
- `account.updated` - Tracks onboarding completion
|
|
29
|
+
- `checkout.session.completed` - Subscription initiation
|
|
30
|
+
- `customer.subscription.*` - Subscription lifecycle
|
|
31
|
+
- `invoice.payment_succeeded/failed` - Access control updates
|
|
32
|
+
|
|
33
|
+
4. **API Endpoints** ([server.js](server.js))
|
|
34
|
+
- `POST /api/stripe/create-connect-account` - Create Connect account
|
|
35
|
+
- `POST /api/stripe/create-account-link` - Get onboarding URL
|
|
36
|
+
- `POST /api/stripe/refresh-account-status` - Check status
|
|
37
|
+
- `POST /api/stripe/set-pricing` - Set subscription prices
|
|
38
|
+
- `POST /api/payment/create-checkout-session` - Subscription checkout
|
|
39
|
+
- `POST /api/payment/webhook` - Stripe webhook handler
|
|
40
|
+
|
|
41
|
+
### 🔨 Frontend (Partially Complete)
|
|
42
|
+
|
|
43
|
+
1. **Provider Dashboard** ([public/app/index.html](public/app/index.html))
|
|
44
|
+
- Revenue analytics display
|
|
45
|
+
- Pricing configuration UI
|
|
46
|
+
- Transaction history viewer
|
|
47
|
+
- **Still needed**: Stripe Connect onboarding flow
|
|
48
|
+
|
|
49
|
+
2. **Patient Payment** ([public/chat.html](public/chat.html))
|
|
50
|
+
- Payment modal added
|
|
51
|
+
- CSS styling complete
|
|
52
|
+
- **Still needed**: Connect checkout integration
|
|
53
|
+
|
|
54
|
+
## Setup Instructions
|
|
55
|
+
|
|
56
|
+
### Step 1: Stripe Account Setup
|
|
57
|
+
|
|
58
|
+
1. **Sign up for Stripe** at https://stripe.com
|
|
59
|
+
2. **Enable Stripe Connect**:
|
|
60
|
+
- Go to https://dashboard.stripe.com/connect/accounts/overview
|
|
61
|
+
- Click "Get Started" for Connect
|
|
62
|
+
- Select "Platform or Marketplace"
|
|
63
|
+
|
|
64
|
+
3. **Get your API keys**:
|
|
65
|
+
- Go to https://dashboard.stripe.com/test/apikeys
|
|
66
|
+
- Copy:
|
|
67
|
+
- `STRIPE_PUBLISHABLE_KEY` (starts with `pk_test_` or `pk_live_`)
|
|
68
|
+
- `STRIPE_SECRET_KEY` (starts with `sk_test_` or `sk_live_`)
|
|
69
|
+
|
|
70
|
+
4. **Already added to `.env`** ✅:
|
|
71
|
+
```
|
|
72
|
+
STRIPE_PUBLISHABLE_KEY=pk_live_51Pc6tMJ5RPsKfJwQ...
|
|
73
|
+
STRIPE_SECRET_KEY=sk_live_51Pc6tMJ5RPsKfJwQ...
|
|
74
|
+
STRIPE_WEBHOOK_SECRET=(see Step 2)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Step 2: Set Up Webhooks
|
|
78
|
+
|
|
79
|
+
1. Go to https://dashboard.stripe.com/test/webhooks
|
|
80
|
+
2. Click "+ Add endpoint"
|
|
81
|
+
3. **Endpoint URL**: `https://your-domain.com/api/payment/webhook`
|
|
82
|
+
- For local testing: Use [Stripe CLI](https://stripe.com/docs/stripe-cli) to forward webhooks
|
|
83
|
+
4. **Events to listen for**:
|
|
84
|
+
```
|
|
85
|
+
account.updated
|
|
86
|
+
checkout.session.completed
|
|
87
|
+
customer.subscription.created
|
|
88
|
+
customer.subscription.updated
|
|
89
|
+
customer.subscription.deleted
|
|
90
|
+
invoice.payment_succeeded
|
|
91
|
+
invoice.payment_failed
|
|
92
|
+
```
|
|
93
|
+
5. **Copy the signing secret** (starts with `whsec_...`)
|
|
94
|
+
6. **Add to `.env`**:
|
|
95
|
+
```
|
|
96
|
+
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Step 3: Configure apphosting.yaml (Already Done ✅)
|
|
100
|
+
|
|
101
|
+
Your `apphosting.yaml` is already configured with all necessary Stripe environment variables:
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
# Stripe API Keys (lines 26-40)
|
|
105
|
+
- variable: STRIPE_PUBLISHABLE_KEY
|
|
106
|
+
secret: STRIPE_PUBLISHABLE_KEY
|
|
107
|
+
availability:
|
|
108
|
+
- RUNTIME
|
|
109
|
+
|
|
110
|
+
- variable: STRIPE_SECRET_KEY
|
|
111
|
+
secret: STRIPE_SECRET_KEY
|
|
112
|
+
availability:
|
|
113
|
+
- RUNTIME
|
|
114
|
+
|
|
115
|
+
- variable: STRIPE_WEBHOOK_SECRET
|
|
116
|
+
secret: STRIPE_WEBHOOK_SECRET
|
|
117
|
+
availability:
|
|
118
|
+
- RUNTIME
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
✅ **Status**: Configuration complete. Variables reference Firebase secrets (not hardcoded values).
|
|
122
|
+
|
|
123
|
+
### Step 4: Set Firebase Secrets for Production
|
|
124
|
+
|
|
125
|
+
When deploying to production, set these secrets in Firebase:
|
|
126
|
+
|
|
127
|
+
**Option A: Using Firebase CLI**
|
|
128
|
+
```bash
|
|
129
|
+
# Set Stripe Publishable Key
|
|
130
|
+
firebase apphosting:secrets:set STRIPE_PUBLISHABLE_KEY
|
|
131
|
+
# When prompted, paste: pk_live_51Pc6tMJ5RPsKfJwQ...
|
|
132
|
+
|
|
133
|
+
# Set Stripe Secret Key
|
|
134
|
+
firebase apphosting:secrets:set STRIPE_SECRET_KEY
|
|
135
|
+
# When prompted, paste: sk_live_51Pc6tMJ5RPsKfJwQ...
|
|
136
|
+
|
|
137
|
+
# Set Stripe Webhook Secret
|
|
138
|
+
firebase apphosting:secrets:set STRIPE_WEBHOOK_SECRET
|
|
139
|
+
# When prompted, paste: whsec_zo955CM5K9ZFxlx8...
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Option B: Using gcloud**
|
|
143
|
+
```bash
|
|
144
|
+
# Create secrets with values
|
|
145
|
+
echo "pk_live_51Pc6tMJ5RPsKfJwQ..." | gcloud secrets create STRIPE_PUBLISHABLE_KEY --data-file=-
|
|
146
|
+
echo "sk_live_51Pc6tMJ5RPsKfJwQ..." | gcloud secrets create STRIPE_SECRET_KEY --data-file=-
|
|
147
|
+
echo "whsec_zo955CM5K9ZFxlx8..." | gcloud secrets create STRIPE_WEBHOOK_SECRET --data-file=-
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Note**: For local development, secrets are loaded from `.env` file (already configured ✅).
|
|
151
|
+
|
|
152
|
+
### Step 5: Add Platform URL
|
|
153
|
+
|
|
154
|
+
Add this to your `.env` (used for redirect URLs):
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
PLATFORM_URL=https://your-domain.com
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
For local development:
|
|
161
|
+
```bash
|
|
162
|
+
PLATFORM_URL=http://localhost:8080
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Database Schema
|
|
166
|
+
|
|
167
|
+
### Firestore Collections
|
|
168
|
+
|
|
169
|
+
Your platform now uses these collections:
|
|
170
|
+
|
|
171
|
+
#### `providers/{providerId}`
|
|
172
|
+
```javascript
|
|
173
|
+
{
|
|
174
|
+
// Existing fields...
|
|
175
|
+
displayName: "Dr. Jane Smith",
|
|
176
|
+
email: "jane@example.com",
|
|
177
|
+
handle: "dr-smith",
|
|
178
|
+
status: "published",
|
|
179
|
+
|
|
180
|
+
// NEW: Stripe Connect fields
|
|
181
|
+
stripeAccountId: "acct_...", // Stripe Connect account ID
|
|
182
|
+
stripeOnboardingStatus: "COMPLETE", // NOT_STARTED | PENDING | COMPLETE
|
|
183
|
+
chargesEnabled: true, // Can accept payments
|
|
184
|
+
payoutsEnabled: true, // Can receive payouts
|
|
185
|
+
stripeProductId: "prod_...", // Stripe Product ID
|
|
186
|
+
stripePriceIds: { // Stripe Price IDs for each interval
|
|
187
|
+
day: "price_...",
|
|
188
|
+
week: "price_...",
|
|
189
|
+
month: "price_..."
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
// Pricing configuration
|
|
193
|
+
pricingModel: "subscription", // free | subscription | per-chat | hybrid
|
|
194
|
+
subscriptionPrice: 29.99, // Monthly price (if using legacy)
|
|
195
|
+
paymentEnabled: true,
|
|
196
|
+
currency: "usd"
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### `patients/{sessionId}`
|
|
201
|
+
```javascript
|
|
202
|
+
{
|
|
203
|
+
stripeCustomerId: "cus_...", // Stripe Customer ID
|
|
204
|
+
createdAt: Timestamp,
|
|
205
|
+
updatedAt: Timestamp
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### `subscriptions/{subId}` (auto-generated ID)
|
|
210
|
+
```javascript
|
|
211
|
+
{
|
|
212
|
+
providerId: "user123",
|
|
213
|
+
patientId: "sess_abc123",
|
|
214
|
+
stripeSubscriptionId: "sub_...",
|
|
215
|
+
stripeCustomerId: "cus_...",
|
|
216
|
+
stripePriceId: "price_...",
|
|
217
|
+
status: "active", // active | past_due | canceled | incomplete
|
|
218
|
+
currentPeriodStart: Timestamp,
|
|
219
|
+
currentPeriodEnd: Timestamp,
|
|
220
|
+
cancelAtPeriodEnd: false,
|
|
221
|
+
createdAt: Timestamp,
|
|
222
|
+
updatedAt: Timestamp
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### `access/{providerId}_{patientId}` (composite key)
|
|
227
|
+
```javascript
|
|
228
|
+
{
|
|
229
|
+
providerId: "user123",
|
|
230
|
+
patientId: "sess_abc123",
|
|
231
|
+
status: "ACTIVE", // ACTIVE | INACTIVE
|
|
232
|
+
stripeSubscriptionId: "sub_...",
|
|
233
|
+
currentPeriodEnd: Timestamp,
|
|
234
|
+
updatedAt: Timestamp
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### `transactions/{txId}` (for per-chat payments)
|
|
239
|
+
```javascript
|
|
240
|
+
{
|
|
241
|
+
providerId: "user123",
|
|
242
|
+
patientId: "sess_abc123",
|
|
243
|
+
conversationId: "conv_...", // optional
|
|
244
|
+
amount: 500, // in cents
|
|
245
|
+
currency: "usd",
|
|
246
|
+
status: "succeeded", // pending | succeeded | failed | refunded
|
|
247
|
+
paymentMethod: "card",
|
|
248
|
+
stripePaymentIntentId: "pi_...",
|
|
249
|
+
metadata: {},
|
|
250
|
+
createdAt: Timestamp,
|
|
251
|
+
updatedAt: Timestamp
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Provider Workflow
|
|
256
|
+
|
|
257
|
+
### 1. Provider Signs Up
|
|
258
|
+
```javascript
|
|
259
|
+
// Automatic - handled by your existing /api/provider/create endpoint
|
|
260
|
+
// Provider document is created in Firestore
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### 2. Provider Enables Payments
|
|
264
|
+
```javascript
|
|
265
|
+
// Step 1: Create Connect account (programmatic)
|
|
266
|
+
POST /api/stripe/create-connect-account
|
|
267
|
+
Headers: Authorization: Bearer {firebase-id-token}
|
|
268
|
+
|
|
269
|
+
Response:
|
|
270
|
+
{
|
|
271
|
+
"accountId": "acct_...",
|
|
272
|
+
"onboardingStatus": "PENDING"
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Step 2: Get onboarding link
|
|
276
|
+
POST /api/stripe/create-account-link
|
|
277
|
+
Headers: Authorization: Bearer {firebase-id-token}
|
|
278
|
+
|
|
279
|
+
Response:
|
|
280
|
+
{
|
|
281
|
+
"url": "https://connect.stripe.com/setup/...",
|
|
282
|
+
"expiresAt": 1234567890
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Provider clicks URL and completes Stripe onboarding
|
|
286
|
+
// Webhook automatically updates provider.chargesEnabled = true
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 3. Provider Sets Pricing
|
|
290
|
+
```javascript
|
|
291
|
+
POST /api/stripe/set-pricing
|
|
292
|
+
Headers: Authorization: Bearer {firebase-id-token}
|
|
293
|
+
Body:
|
|
294
|
+
{
|
|
295
|
+
"currency": "usd",
|
|
296
|
+
"prices": {
|
|
297
|
+
"day": 5.00, // Optional: $5/day
|
|
298
|
+
"week": 20.00, // Optional: $20/week
|
|
299
|
+
"month": 50.00 // Optional: $50/month
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
Response:
|
|
304
|
+
{
|
|
305
|
+
"productId": "prod_...",
|
|
306
|
+
"priceIds": {
|
|
307
|
+
"day": "price_...",
|
|
308
|
+
"week": "price_...",
|
|
309
|
+
"month": "price_..."
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Patient Workflow
|
|
315
|
+
|
|
316
|
+
### 1. Patient Wants to Chat
|
|
317
|
+
```javascript
|
|
318
|
+
// Check if payment is required
|
|
319
|
+
POST /api/payment/check-access
|
|
320
|
+
Body:
|
|
321
|
+
{
|
|
322
|
+
"handle": "dr-smith",
|
|
323
|
+
"sessionId": "sess_abc123",
|
|
324
|
+
"conversationId": "conv_123" // optional
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
Response (no access):
|
|
328
|
+
{
|
|
329
|
+
"hasAccess": false,
|
|
330
|
+
"type": null
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 2. Patient Subscribes
|
|
335
|
+
```javascript
|
|
336
|
+
// Create checkout session
|
|
337
|
+
POST /api/payment/create-checkout-session
|
|
338
|
+
Body:
|
|
339
|
+
{
|
|
340
|
+
"handle": "dr-smith",
|
|
341
|
+
"sessionId": "sess_abc123",
|
|
342
|
+
"interval": "month" // or "week" or "day"
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
Response:
|
|
346
|
+
{
|
|
347
|
+
"sessionId": "cs_...",
|
|
348
|
+
"url": "https://checkout.stripe.com/c/pay/..."
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Redirect patient to URL
|
|
352
|
+
// After payment, Stripe redirects to /payment-success?session_id=...
|
|
353
|
+
// Webhook grants access via `access` collection
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### 3. Patient Chats
|
|
357
|
+
```javascript
|
|
358
|
+
// Before each chat request, verify access
|
|
359
|
+
POST /api/payment/check-access
|
|
360
|
+
Body: { "handle": "dr-smith", "sessionId": "sess_abc123" }
|
|
361
|
+
|
|
362
|
+
Response (has access):
|
|
363
|
+
{
|
|
364
|
+
"hasAccess": true,
|
|
365
|
+
"type": "subscription"
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// If hasAccess === true, allow chat
|
|
369
|
+
// Otherwise, show payment modal
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Testing
|
|
373
|
+
|
|
374
|
+
### Test Mode (Recommended First)
|
|
375
|
+
|
|
376
|
+
1. Use test API keys (`pk_test_...` and `sk_test_...`)
|
|
377
|
+
2. Use test card numbers:
|
|
378
|
+
- **Success**: `4242 4242 4242 4242`
|
|
379
|
+
- **Decline**: `4000 0000 0000 0002`
|
|
380
|
+
- Any future expiry date (e.g., `12/34`)
|
|
381
|
+
- Any 3-digit CVC (e.g., `123`)
|
|
382
|
+
|
|
383
|
+
3. Test webhook locally with Stripe CLI:
|
|
384
|
+
```bash
|
|
385
|
+
# Install Stripe CLI
|
|
386
|
+
brew install stripe/stripe-cli/stripe
|
|
387
|
+
|
|
388
|
+
# Login
|
|
389
|
+
stripe login
|
|
390
|
+
|
|
391
|
+
# Forward webhooks to localhost
|
|
392
|
+
stripe listen --forward-to localhost:8080/api/payment/webhook
|
|
393
|
+
|
|
394
|
+
# Copy the webhook signing secret (whsec_...) to .env
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Live Mode
|
|
398
|
+
|
|
399
|
+
1. Switch to live API keys (`pk_live_...` and `sk_live_...`)
|
|
400
|
+
2. Complete Stripe account verification
|
|
401
|
+
3. Set up production webhooks at your live domain
|
|
402
|
+
4. Update `PLATFORM_URL` to production URL
|
|
403
|
+
|
|
404
|
+
## Revenue Flow
|
|
405
|
+
|
|
406
|
+
**Example**: Patient pays $100/month
|
|
407
|
+
|
|
408
|
+
1. **Stripe Checkout** collects $100
|
|
409
|
+
2. **Platform fee** (25%): $25 goes to your platform's Stripe balance
|
|
410
|
+
3. **Provider receives** (75%): $75 goes to provider's Connect account
|
|
411
|
+
4. **Stripe payout**: Provider receives $75 in their bank account (automatic)
|
|
412
|
+
|
|
413
|
+
## Next Steps to Complete
|
|
414
|
+
|
|
415
|
+
1. **Add Connect onboarding UI** to provider dashboard:
|
|
416
|
+
- Button to start onboarding
|
|
417
|
+
- Show onboarding status
|
|
418
|
+
- Link to Stripe Express Dashboard
|
|
419
|
+
|
|
420
|
+
2. **Add patient checkout flow**:
|
|
421
|
+
- Detect payment requirement
|
|
422
|
+
- Show subscription options (daily/weekly/monthly)
|
|
423
|
+
- Redirect to Stripe Checkout
|
|
424
|
+
- Handle success/cancel redirects
|
|
425
|
+
|
|
426
|
+
3. **Test end-to-end** flow:
|
|
427
|
+
- Provider onboards
|
|
428
|
+
- Provider sets pricing
|
|
429
|
+
- Patient subscribes
|
|
430
|
+
- Patient gains access
|
|
431
|
+
- Verify revenue split
|
|
432
|
+
|
|
433
|
+
## Security Notes
|
|
434
|
+
|
|
435
|
+
- ✅ All access control is webhook-driven (not client-controlled)
|
|
436
|
+
- ✅ Payments route through Connect with automatic fee splitting
|
|
437
|
+
- ✅ Providers receive payouts directly from Stripe
|
|
438
|
+
- ✅ Platform never handles provider funds directly
|
|
439
|
+
- ✅ All sensitive operations require Firebase Auth
|
|
440
|
+
|
|
441
|
+
## Support & Documentation
|
|
442
|
+
|
|
443
|
+
- **Stripe Connect Docs**: https://stripe.com/docs/connect
|
|
444
|
+
- **Stripe Checkout Docs**: https://stripe.com/docs/payments/checkout
|
|
445
|
+
- **Webhooks Guide**: https://stripe.com/docs/webhooks
|
|
446
|
+
- **Testing Guide**: https://stripe.com/docs/testing
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
**Status**: Backend complete ✅ | Frontend 70% complete ⏳
|
|
451
|
+
|
|
452
|
+
**Ready to deploy**: Yes (after adding webhook secret)
|
|
453
|
+
|
|
454
|
+
**Ready for testing**: Yes (use test mode)
|