121 lines
5.6 KiB
Markdown
121 lines
5.6 KiB
Markdown
# External Integrations
|
|
|
|
**Analysis Date:** 2026-05-05
|
|
|
|
## Google Calendar API v3
|
|
|
|
**Purpose:** Bidirectional sync — bookings push to Google Calendar as events; Google Calendar events pull back as availability blocks.
|
|
|
|
**Implementation:** Native PHP, no SDK. Uses WordPress HTTP API (`wp_remote_post`, `wp_remote_get`, `wp_remote_request`).
|
|
|
|
**Files:**
|
|
- `integrations/google-calendar/class-gcal-service.php` — API calls
|
|
- `integrations/google-calendar/class-oauth-handler.php` — OAuth 2.0 token management
|
|
- `integrations/google-calendar/class-sync-controller.php` — Cron orchestration (Singleton)
|
|
|
|
**Authentication:** OAuth 2.0 with `offline` access (refresh token).
|
|
- Auth endpoint: `https://accounts.google.com/o/oauth2/v2/auth`
|
|
- Token endpoint: `https://oauth2.googleapis.com/token`
|
|
- Scope: `https://www.googleapis.com/auth/calendar`
|
|
- Credentials stored in `wp_options` key `yacht_booking_gcal_credentials`
|
|
- Tokens stored in `wp_options` key `yacht_booking_gcal_tokens`
|
|
- Calendar ID stored in `yacht_booking_gcal_calendar_id` (defaults to `'primary'`)
|
|
- **OAuth redirect URI hardcoded:** `https://jachty.pagedev.pl/wp-admin/admin.php?page=yacht-bookings-settings&tab=google-calendar&gcal_callback=1`
|
|
|
|
**Outbound API calls:**
|
|
| Method | URL | Purpose |
|
|
|--------|-----|---------|
|
|
| POST | `.../calendars/{id}/events` | Create event |
|
|
| PATCH | `.../calendars/{id}/events/{eventId}` | Update event |
|
|
| DELETE | `.../calendars/{id}/events/{eventId}` | Delete event |
|
|
| GET | `.../calendars/{id}/events` | List events (pull sync) |
|
|
| GET | `.../users/me/calendarList` | List calendars |
|
|
| POST | `https://oauth2.googleapis.com/token` | Token exchange/refresh |
|
|
| GET | `https://www.googleapis.com/oauth2/v2/userinfo` | Get connected email |
|
|
|
|
**Sync model:** Pull-only for external → local (hourly WP Cron `yacht_booking_pull_from_gcal`). Push for local → GCal on `yacht_booking_created` / `yacht_booking_status_changed` (scheduled single cron events).
|
|
|
|
**Limitations:**
|
|
- Single shared calendar for all yachts — GCal events apply to every yacht, not per-yacht
|
|
- No incoming webhooks — polling model only
|
|
- No retry logic on API failure
|
|
|
|
## iCal Integration
|
|
|
|
### Export (Feed)
|
|
|
|
**Purpose:** Generate `.ics` file per yacht for subscription by external clients (other booking systems, personal calendars).
|
|
|
|
**File:** `integrations/ical/class-ical-feed.php`
|
|
|
|
**URL pattern:** `{home_url}/yacht-ical/{yacht_id}/{token}.ics`
|
|
- Token stored in `_yacht_ical_token` post meta (24-char alphanumeric, auto-generated)
|
|
- Verified with `hash_equals()` — timing-safe
|
|
- Returns `text/calendar` with all non-cancelled bookings for that yacht
|
|
- Regenerate token: `ICal_Feed::regenerate_token($yacht_id)`
|
|
|
|
### Import (Subscribe)
|
|
|
|
**Purpose:** Subscribe to external iCal URLs per yacht; import events as availability blocks (hourly cron).
|
|
|
|
**File:** `integrations/ical/class-ical-import.php`
|
|
|
|
**Config:** Per-yacht `_yacht_ical_import_url` post meta (set via `ICal_Import::set_import_url()`)
|
|
|
|
**Behavior:** Fetches URL via `wp_remote_get()` (30-second timeout, SSL verification), parses VEVENT components, creates `yacht_booking` posts with `_booking_source = 'ical_import'`, marks dates as blocked via `Availability::mark_as_blocked()`.
|
|
|
|
**Limitation:** Does not check VEVENT `STATUS` property — cancelled events in the feed are imported as blocks.
|
|
|
|
## jsDelivr CDN
|
|
|
|
**Purpose:** FullCalendar library loaded from CDN (no local copy).
|
|
|
|
| Asset | URL |
|
|
|-------|-----|
|
|
| FullCalendar JS | `https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.js` |
|
|
| FullCalendar CSS | `https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.css` |
|
|
| Polish locale | `https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.10/locales/pl.global.min.js` |
|
|
|
|
Enqueued in `Yacht_Booking::enqueue_frontend_assets()`, conditional on page content.
|
|
|
|
## Email
|
|
|
|
**Provider:** WordPress `wp_mail()` only — no Mailgun, SendGrid, or other transactional service.
|
|
|
|
**Templates:** `includes/class-email-templates.php` — templates stored in `wp_options` as tagged strings, compiled with `get_booking_template_data()`.
|
|
|
|
**When sent:**
|
|
- On booking creation: admin notification via `Rest_Controller::send_booking_notification()` (hooked on `yacht_booking_created`)
|
|
- On status change: customer notification via `Admin::send_customer_notification()` (hooked on `yacht_booking_status_changed`)
|
|
- On inquiry create: admin + customer emails via `Inquiry::send_emails()` (built inline, not using Email_Templates)
|
|
|
|
**Headers:** `From:` pulled from `yacht_booking_email_from` option; `Reply-To:` set explicitly.
|
|
|
|
## REST API (Internal)
|
|
|
|
Namespace: `yacht-booking/v1` — base: `/wp-json/yacht-booking/v1/`
|
|
|
|
| Method | Endpoint | Auth | Handler |
|
|
|--------|----------|------|---------|
|
|
| GET | `/yachts` | Public | `get_yachts()` |
|
|
| GET | `/yachts/{id}` | Public | `get_yacht()` |
|
|
| GET | `/availability/{yacht_id}?start=Y-m-d&end=Y-m-d` | Public | `get_availability()` |
|
|
| POST | `/bookings` | Public + `X-WP-Nonce` | `create_booking()` |
|
|
| POST | `/inquiries` | Public + `X-WP-Nonce` | `create_inquiry()` |
|
|
| GET | `/bookings` | Admin cap | `get_bookings()` |
|
|
| PUT | `/bookings/{id}/status` | Admin cap | `update_booking_status()` |
|
|
|
|
All registered in `api/class-rest-controller.php`.
|
|
|
|
## Monitoring & Observability
|
|
|
|
- No error tracking service (no Sentry, Bugsnag)
|
|
- `error_log()` used with prefixes: `[Yacht Booking - GCal]`, `[Yacht Booking - GCal Sync]`, `[Yacht Booking - iCal]`
|
|
- Logging only fires when `WP_DEBUG === true` — silent in production
|
|
|
|
## Deployment
|
|
|
|
- **Method:** FTP via VS Code ftp-kr extension (`.vscode/ftp-kr.json`)
|
|
- **CI/CD:** None — fully manual
|
|
- **Production URL:** `https://jachty.pagedev.pl` (hardcoded in GCal OAuth redirect)
|