When order is paid, automatically create Google Meet events for all consulting slots.
The meet_link is saved to consulting_slots table and included in notifications.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Using req.text() first then parsing JSON gives us more control and avoids
stream consumption issues with Deno/Supabase edge functions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Catch TypeError when req.json() is called on consumed stream
- Return success response for duplicate calls (first call handles the actual work)
- This handles React Strict Mode firing onClick twice in parallel
- Only the first call that successfully reads body will process the request
- Use req.clone().json() to handle multiple reads from same request
- This happens when React Strict Mode fires requests twice
- Cloning the request allows reading body multiple times safely
- Add expires_at timestamp to OAuth config
- Cache access_token in database to reuse across requests
- Only refresh token when it expires (after 1 hour)
- Use 60-second buffer to avoid using almost-expired tokens
- Auto-update cached token after refresh
- This fixes the invalid_grant error from excessive refresh calls
- Replace JWT service account authentication with OAuth2 refresh token flow
- Service accounts cannot create Google Meet links for personal Gmail accounts
- Update edge function to use OAuth2 token exchange
- Change database column from google_service_account_json to google_oauth_config
- Add helper tool (get-google-refresh-token.html) to generate OAuth credentials
- Update IntegrasiTab UI to show OAuth config instead of service account
- Add SQL migration file for new google_oauth_config column
OAuth2 Config format:
{
"client_id": "...",
"client_secret": "...",
"refresh_token": "..."
}
This approach works with personal @gmail.com accounts without requiring
Google Workspace or Domain-Wide Delegation.
- Try just 'hangoutsMeet' as type without 'name' field
- This is the most basic format according to some docs
- Combined with full event logging to debug
- Check conferenceData.entryPoints for video meet link
- Keep hangoutLink as fallback for backwards compatibility
- Log full event response to debug missing meet link
- This will show us what Google actually returns
- Remove conferenceSolutionKey entirely - let Google auto-select Meet
- Add console.log to see event data being sent
- Add response status logging for debugging
- Change type from 'hangoutsMeet' to 'event'
- Add name: 'hangoutsMeet' property
- This matches Google Calendar API requirements for creating Meet conferences
- Service accounts require Domain-Wide Delegation to invite attendees
- Removed attendees array, sendUpdates, and guest permissions
- Client email still included in event description
- Google Meet link will still be generated successfully
- Can re-enable attendees later if Domain-Wide Delegation is configured
- Remove external jose library dependency that was causing import errors
- Use native crypto.subtle API available in Deno
- Manual base64url encoding for JWT header, payload, and signature
- Use RSASSA-PKCS1-v1_5 with SHA-256 for RS256 algorithm
- Remove cat heredoc wrapper from file
- Build JWT header and payload manually for exact format control
- Use lower-level importKey/sign from Web Crypto API
- Use RSASSA-PKCS1-v1_5 algorithm directly (RSA+SHA256 = RS256)
- Manual base64url encoding for URL-safe tokens
- Add debug logging to trace JWT generation
- Avoids SignJWT abstraction that was causing algorithm errors
- Use importPKCS8 to convert private key string to CryptoKey
- Pass CryptoKey to SignJWT.sign() instead of string
- Fix 'Key for RS256 algorithm must be CryptoKey' error
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Change from jwt.sign() to SignJWT class
- Use proper jose library SignJWT API for Deno
- Fix 'Cannot read properties of undefined' error
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create new create-google-meet-event edge function
- Use service account authentication (no OAuth needed)
- Add google_service_account_json field to platform_settings
- Add admin UI for service account JSON configuration
- Include test connection button in Integrasi tab
- Add comprehensive setup documentation
- Keep n8n workflows as alternative option
Features:
- Direct Google Calendar API integration
- JWT authentication with service account
- Auto-create Google Meet links
- No external dependencies needed
- Simple configuration via admin panel
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add test/production toggle for n8n webhook URLs in IntegrasiTab
- Update create-meet-link function to use database test_mode setting
- Add send-email-v2 edge function for Mailketing API integration
- Update daily-reminders and send-consultation-reminder to use send-email-v2
- Remove deprecated branding field from BrandingTab
- Update domain references from hub.dwindi.com to with.dwindi.com
- Add environment variables for Coolify deployment
- Add comprehensive edge function test script
- Update payment flow redirect to order detail page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>