Home/ Work/ZDGNS Počitnikovanje
Lead developer · 2022 — ongoing · Live

ZDGNS Počitnikovanje

A member-facing holiday-booking portal built for Slovenia's Association of the Deaf and Hard of Hearing — Dynamics 365 CRM backbone, PayPal payments, multi-language UI, and booking rules tuned to 12 distinct membership categories.

Role Lead developer & architect
Duration 2022 → ongoing
Client ZDGNS — Zveza GNS
Platforms web · member portal · admin
Domain Healthcare · Holiday booking
ZDGNS Počitnikovanje screenshot
Member categories
12
distinct booking-rule tiers
Languages
SL + EN
full i18next localisation
Payment flow
PayPal
stateless, CRM-driven
CRM backbone
Dynamics 365
single source of truth

Overview

ZDGNS — Zveza društev gluhih in naglušnih Slovenije (Slovenian Association of the Deaf and Hard of Hearing) runs a holiday and health-rehabilitation programme for its members. Before this portal, reservations were handled manually. I was brought in to build a self-service booking system from scratch that would let members log in, browse holiday properties, check real-time availability, and confirm reservations — all integrated tightly into the organisation’s existing Dynamics 365 CRM.

The portal covers the full lifecycle: member registration (with dual paths for existing card-holders and new registrants), a 3-step reservation wizard, a PayPal payment flow triggered by CRM-generated email links, and a personal dashboard showing upcoming and past stays. Because the user base includes people who are deaf, hard of hearing, deafblind, or have cochlear implants, the UI was designed for clarity and visual-first communication throughout.

The system has been in continuous production use and active development since launch — shipping 22+ versioned releases covering new features, security fixes, and CRM schema updates through 2026.

Architecture

~/zdgns/architecture/system-map.svg
01 — CLIENT SURFACESMember PortalReact 19 · MUI · i18nextPublic PayPal Flowunauthenticated · email linkAdmin (CRM native)Dynamics 365 · staff-facingPHP Proxy LayerOAuth token management · OData v9.2 relay · POST-wrappingsession handling · CORS · PayPal REST proxystateless payment bridge — GUID in PayPal invoice field02 — INTEGRATIONSDynamics 365 CRMmembers · locations · ordersPayPal REST APIcreate · execute · statusEmail (CRM-native)payment links · notificationsAzure AD OAuthCRM token — server-side onlyCRM entities: ad_locations · ad_units · salesorders · contacts · ad_postnastevilkas · opportunitiesClient-side cache: postal codes (24 h) · locations (30 min) · availability (5 min)03 — DATA LAYER

Reading the diagram: The React member portal and the public PayPal flow both speak only to the PHP proxy layer — the CRM access token never reaches the browser. The proxy relays OData v9.2 queries to Dynamics 365, manages session state, and bridges the stateless PayPal checkout by embedding the order GUID inside PayPal’s own invoice field. Azure AD OAuth credentials stay server-side throughout.

03 What I delivered · challenges solved

Six things shipped,
three hard ones solved.

Key contributions

  • Designed the full-stack architecture — React SPA on the front, a PHP proxy layer on the back, Dynamics 365 as the authoritative data source.
  • Built a 3-step reservation wizard: date-range search → availability confirmation → guest-list checkout, with 12 membership-tier booking rules.
  • Integrated PayPal Express Checkout via a stateless PHP proxy — no sessions needed; order GUID embedded in PayPal's invoice field to survive the redirect.
  • Connected the portal bidirectionally to Dynamics 365 CRM: reads locations, units, sales orders, and member records; writes back payment status and reservation data.
  • Implemented two-path registration: existing members register by member-card number with birthdate validation; new contacts created via a dedicated CRM endpoint.
  • Shipped bilingual UI (Slovenian primary, English secondary) using i18next with browser-language detection.
  • Cached postal codes, locations, and unit lists client-side with tiered TTLs (5 min / 30 min / 24 h) to stay within API rate limits.

Challenges solved

  • Dynamics 365 as a backend — every entity (location, unit, sales order, member) lives in CRM; all queries are OData v9.2, requiring careful filter encoding and batch-request tuning.
  • Stateless PayPal flow — CRM sends a payment link by email; the user lands unauthenticated; the GUID must survive PayPal's redirect without server sessions.
  • 12 membership categories with different price tiers and advance-booking windows — deaf and hard-of-hearing members get 3-month priority; others get 1 month.
  • ModSecurity on the shared cPanel host blocked long OData query strings — solved by wrapping all reads in POST bodies through a PHP relay.
The real constraint wasn't React or PHP — it was Dynamics 365 as the only source of truth. Every booking rule, price tier, and member record lives in CRM. Engineering the portal meant learning to think in OData.
Davor Majc, Lead developer / ZDGNS
04 Tech stack

What's under the hood.

ReactMaterial UIVitePHPDynamics 365PayPali18nextReact RouterdayjsaxioscPanel
Let's talk

Pripravljeni popraviti, zgraditi
ali skalirati?

30 minut, z mano osebno. Preberem vaš sistem kot dnevniško datoteko in povem, kaj bi naredil najprej. Brez prezentacij, brez prodajnega lijaka.

Davor Majc, ustanovitelj, Numen

What you get on call
→ enostranska diagnostika
→ 2–3 obliki rešitve, razvrščeni po učinku
→ okvirni strošek + časovnica za vsako
→ da/ne — ali sem prava izbira
+386 40 828 474 · Blejska Dobrava, SI