Heavenly Insight — Build Report & Case Study
A layered case study: executive summary for course audiences, technical deep-dive for developers
Positive Vibrations Web Design · August 2026
Executive Summary
Heavenly Insight is a mobile-first natal chart calculator and astrology toolkit — a production-grade web app that calculates precise planetary positions at the moment of birth, then layers on 30+ divination tools, a premium subscription, an admin analytics dashboard, and a daily horoscope email system. It was built on the Base44 platform (backend-as-a-service) using React, Tailwind CSS, and the astronomia astronomical calculation library.
What was built
- •40+ pages covering natal charts, compatibility/synastry, tarot, runes, numerology, and over a dozen cultural astrology systems (Egyptian, Hebrew, Druid, Mayan, Tibetan, Vedic nakshatras, I Ching, Ifá, and more)
- •6 supported languages (English, French, German, Spanish, Portuguese, Italian)
- •Premium subscription via Stripe with a 3-day free trial, monthly and annual billing, and 45+ currencies
- •Daily horoscope email system powered by a scheduled workflow and AI-generated personalized readings
- •Admin dashboard with user management, analytics, premium-gating controls, and feedback review
- •Cosmic Rose premium theme and three cosmic mini-games (match-3, snake, memory)
The headline lesson
The app is functionally complete and visually polished, but it was built feature-first — each tool was added one at a time without a compliance and store-readiness checkpoint. This meant that when it came time to prepare for App Store and Google Play submission, several foundational issues surfaced late: the payment model violates store guidelines, the email system lacked legally required opt-out mechanisms, and consent was implicit rather than explicit. These were all fixable, but they cost rework that could have been avoided with a compliance-first checklist from day one.
📊 Q: When should compliance issues be addressed? — A: At design phase, not pre-submission
- Should have been addressed
- When it was actually addressed
📊 Q: How much does late discovery cost? — A: 3-5× more effort than fixing at design time
- Cost if done at design phase
- Cost of late fix
Architecture & Stack
Technology stack
| Layer | Technology | Notes |
|---|---|---|
| Frontend framework | React 18 + Vite | Lazy-loaded route chunks for fast initial load |
| Styling | Tailwind CSS + shadcn/ui | Token-based design system in index.css |
| Backend | Base44 BaaS | Entities, functions, workflows, auth, integrations |
| Astronomical engine | astronomia (npm) | High-precision planetary calculations |
| Charts/visualizations | Custom SVG natal chart wheel | Hand-built, not a charting library |
| Payments | Stripe Checkout + webhooks | Subscription mode with 3-day trial |
| AI | InvokeLLM integration | Gemini 3 Flash for horoscopes, Claude for complex readings |
| Maps | react-leaflet | Birth location selection |
| PDF export | jsPDF + html2canvas | Reading downloads |
| 3D/games | three.js, framer-motion | Cosmic Crush, Zodiac Snake, Astro Memory |
Application structure
The app follows a mobile-first single-page architecture with a bottom navigation bar (Dashboard, Tools, Saved, Settings) that adapts to a desktop sidebar at the lg breakpoint. Every page is a lazy-loaded route chunk, so the initial bundle contains only the shell — auth, layout, and router.
Key architectural decisions
- •Centralized astronomical logic — All planetary calculations live in src/lib/astroCalculations.js. This is the single source of truth for chart computation, used by every page that needs positions. This was a good call: it meant a bug fix in one place propagated everywhere.
- •Tools registry as source of truth — The Tools page exports a categories array that defines every tool (icon, title, description, route, premium status). The Layout component imports this to derive which routes belong to the "Tools" tab for navigation state. This prevents drift between the tools list and the nav.
- •Entity-driven data model — Six entities store all persistent data: SavedChart, ChartNote, ReadingHistory, UserFeedback, VisitLog, and AppSettings. The built-in User entity stores auth, roles, and premium status.
- •Backend functions for external integration — Five functions handle everything that touches external services: createStripeCheckout, stripeWebhook, sendDailyHoroscopeEmail, unsubscribeEmail, and logVisit.
- •Workflow for scheduled automation — A single scheduled workflow (Daily Horoscope Email) fires at 8am Europe/Lisbon time and invokes the email function.
📊 Q: How is the app architected? — A: Four layers: Client → SDK → Backend → External Services
📊 Q: How is data stored and who can access it? — A: 6 entities, each with RLS rules
The premium system
Premium access is determined by a single utility function, checkIsPremium(user) in src/lib/premium.js. Premium = active subscription (not expired). Testers and admins always get full access (for QA). This is checked by a PremiumGate component that wraps premium content. The Admin Console can dynamically override which tools are free vs. premium per-tool, and the PremiumGate caches these overrides in a shared singleton.
📊 Q: How is premium status determined? — A: Two checks: tester/admin role → active subscription
What could have been done better
- •The translations file grew to 1,788 lines. All 6 languages lived in a single translations.js. This exceeded maintainability and should have been split into per-language files from the start. When 16 new tools were added, 5 of 6 languages were missing translations — a symptom of the monolithic structure making it easy to miss. (Now fixed: split into 6 per-language modules.)
- •No component storybook or visual regression. With 40+ pages and dozens of premium-gated components, there is no automated visual test. The Testing Agent helps, but a systematic screenshot baseline would catch regressions.
- •The astronomical calculation library choice. astronomia provides precision but has a steep API. A thin wrapper module documenting the most-used calls would have reduced onboarding friction for future maintainers.
Security & Row-Level Security (RLS)
Authentication
The platform owns the auth backend — tokens, sessions, email verification. The app uses the built-in AuthProvider and ProtectedRoute components. Social login includes Google and Apple (Apple is required by App Store guideline 4.8 when other social login is offered).
Row-Level Security — the data isolation model
Every entity that stores user data has RLS rules ensuring users can only see and modify their own records, while admins can access everything. The pattern is consistent: create allows any authenticated user; read, update, and delete allow own records (created_by_id == user.id) OR admin.
This applies to SavedChart, ChartNote, and ReadingHistory. The VisitLog entity is more restrictive — only admins can read analytics; regular users can create visit logs (for tracking) but never read them.
What this got right
RLS was applied from the beginning on user-content entities. One user cannot see another user's saved charts, notes, or reading history.
What was missed initially
The AppSettings entity (which stores admin-configured premium overrides) allows both user and admin roles to read settings. This is intentional (the frontend needs to read overrides to gate content), but it means any authenticated user can read all app settings via the API. Since settings only contain tool-free/premium flags (not secrets), this is acceptable — but it is worth noting that secrets must never live in entities; they belong in the platform's secrets store, which they do (Stripe keys, webhook secret).
Backend function security
- •createStripeCheckout — requires an authenticated user (base44.auth.me()), uses the user's ID and email for the Stripe session
- •sendDailyHoroscopeEmail — requires admin role; runs as service role to read all Self-tagged charts
- •unsubscribeEmail — public (no auth), but verifies a signed HMAC token before unsubscribing — so only the email recipient can unsubscribe themselves
- •stripeWebhook — verifies the Stripe signature on every request; no user auth needed
- •logVisit — public (creates a visit log for analytics)
The email consent gap (fixed)
The original problem: Saving a chart tagged "Self" silently enrolled the user in daily emails. There was no explicit consent prompt, no unsubscribe link in the email, and the Privacy Policy claimed it was a "Premium feature" when it actually went to all users.
The fix: (1) Added an email_consent boolean field to the User entity. (2) Added an explicit opt-in toggle in Settings — users must actively enable daily emails. (3) The email function now skips any user without email_consent === true. (4) Every email includes a signed unsubscribe link to a new unsubscribeEmail function. (5) The unsubscribe function verifies an HMAC signature (signed with the webhook secret) and returns a styled HTML confirmation page — no login required.
Testing confirmed: After the fix, the email function sent 0 emails (was 4) until users opt in.
📊 Q: What changed to make email compliant? — A: Explicit opt-in + signed unsubscribe link
📊 Q: How does the daily horoscope email workflow work? — A: Scheduled trigger → function → LLM → email
What could have been done better
- •Consent should have been designed before the first email was sent. Building the email feature without consent/unsubscribe was a compliance debt that accumulated interest. A "privacy by design" checklist at feature inception would have caught this.
- •The webhook signature verification in stripeWebhook has dead code — a verifySignature helper function is defined but the actual verification is done inline with a different implementation. This should be consolidated into one clean function.
- •No rate limiting on the logVisit function. A malicious client could flood visit logs. The RLS prevents reading, but the write is open to any authenticated user.
App Store & Google Play Compliance
This is where the build faced its most significant challenges. The app was built as a web app first, with native packaging as a future goal — but several decisions made along the way created store-submission blockers.
Compliance scorecard
| Requirement | Guideline | Status | Notes |
|---|---|---|---|
| Account deletion in-app | Apple 5.1.1(v) | Pass | Settings → Delete Account |
| Sign in with Apple | Apple 4.8 | Pass | Offered alongside Google |
| Entertainment disclaimer | Best practice | Pass | Present on all divination pages + Terms |
| Auto-renewal disclosure | Apple 3.1.2 | Fixed | Added post-trial price + auto-renew text to checkout |
| Email unsubscribe | CAN-SPAM/GDPR | Fixed | Signed link + opt-out function |
| Email consent | GDPR Art. 6(1)(a) | Fixed | Explicit opt-in toggle, no silent enrollment |
| In-app purchase for digital content | Apple 3.1.3(a) | Fixed | Native Apple IAP bridge on iOS; Stripe on web only |
| Google Play Billing | Play policy | Fixed | Native Google Play Billing bridge on Android; Stripe on web only |
| Privacy policy | Apple 5.1.1 | Pass | Comprehensive GDPR policy |
| Data minimization | Apple 5.1.1 | Pass | Only necessary data collected |
The critical blocker: Stripe vs. In-App Purchase
Both Apple (Guideline 3.1.3(a)) and Google (Play Billing requirements) mandate that digital content and subscriptions must use the platform's native in-app purchase system. Heavenly Insight's premium subscription unlocks digital features (AI readings, unlimited charts, synastry reports) but is sold through Stripe Checkout — an external payment processor.
This is not a code bug — it is an architectural decision. The entire payment flow (checkout, webhook, subscription management) is built around Stripe. To ship to the App Store and Google Play, the native builds must use Apple IAP and Google Play Billing respectively. Stripe can remain for the web version only.
The lesson: Payment architecture should be decided based on distribution channels before building the checkout flow. If you plan to ship to app stores, design for IAP from day one — or build a payment abstraction layer that can swap between Stripe (web) and IAP (native).
📊 Q: Why can't we use Stripe for app store builds? — A: Stores require IAP for digital content
Subscription disclosure (fixed)
Apple requires the purchase flow to explicitly state: subscription length, auto-renewal terms, post-trial price, and cancellation policy. The original Premium page showed "3-day free trial" and "Cancel anytime" but never stated the critical line: "Subscription auto-renews at $14.99/month after the trial unless cancelled." This was added to the checkout card.
What could have been done better
- •A compliance checklist should have existed before the first feature was built. The email, payment, and consent issues all stem from the same root cause: building features without checking store and legal requirements first.
- •The Stripe integration was built before the distribution strategy was decided. This is the single most expensive rework item. A 30-minute conversation about "where will this app be published?" at the start would have changed the payment architecture.
- •The Privacy Policy and actual behavior drifted. The policy said email was a "Premium feature" but the code sent to all users. Documentation and code must be kept in sync — ideally through automated checks or by making the policy a view of the actual access rules.
Lessons Learned — What Could Have Been Done Better
Process lessons
- •Compliance-first, not feature-first. Every feature that touches user data, payments, emails, or subscriptions should pass a compliance check before building. The cost of building-then-fixing is 3-5x the cost of building-it-right.
- •Decide distribution channels before architecture. Web-only vs. app stores changes payment, auth, and even some UX patterns. This decision should be made in the first planning session.
- •Split large files early. The 1,788-line translations file was a maintainability liability. When a file crosses ~500 lines, split it. The platform warned about this repeatedly.
- •Test after every change, not just at the end. The email function was "working" (sending 4 emails) but was non-compliant. Testing for correctness (does it meet requirements?) is different from testing for function (does it run?).
Technical lessons
- •Centralize domain logic. The astroCalculations.js and premium.js utilities were good decisions — single sources of truth that made changes safe.
- •Use the platform's security primitives. RLS, secrets, and service-role access are powerful when used correctly. The app got this right on entities but missed it initially on email consent.
- •Signed tokens for public endpoints. The unsubscribe function is public but secure because it verifies an HMAC signature. This pattern (public endpoint + signed token) is the right way to handle email links, magic links, and share links.
- •Lazy-load routes. With 40+ pages, lazy loading keeps the initial bundle small. This was done correctly from the start.
What I would do differently next time
- •Create a compliance checklist as the first artifact of the project
- •Split translations into per-language files from day one
- •Abstract the payment layer so Stripe/IAP can be swapped per platform
- •Add a privacy review to every feature that stores or sends user data
- •Build a visual regression baseline for the 40+ pages
By the Numbers
| Metric | Value |
|---|---|
| Pages/routes | 40+ |
| Entities | 6 (+ built-in User) |
| Backend functions | 5 |
| Workflows | 1 (scheduled) |
| Languages | 6 |
| Astrology/divination tools | 30+ |
| Cultural astrology systems | 12+ |
| Supported currencies | 45+ |
| RLS-protected entities | 5 of 6 |
| Translation files (after split) | 6 per-language modules + 1 aggregator |
Natal Chart Calculator
Heavenly Insight is a precision natal chart calculator that maps the exact positions of the planets at the moment of your birth. Enter your birth date, time, and location to generate an accurate astrology birth chart with planetary positions, house cusps, zodiac signs, and astrological aspects. Our calculation engine supports multiple house systems including Placidus, Whole Sign, and Equal House, as well as both Tropical and Sidereal (Lahiri ayanamsa) zodiac systems — giving you a complete and reliable picture of your cosmic blueprint.
Beyond the natal chart, Heavenly Insight offers a full ephemeris calendar to track planetary movements throughout the month, daily horoscopes powered by your unique birth chart, moon phase tracking, and a complete cosmic toolkit. Explore tarot card readings, Elder Futhark rune castings, Pythagorean numerology calculations, Chinese zodiac analysis, astrological compatibility (synastry) reports, solar return charts, progressed charts, planetary hours, and cosmic weather forecasts — all in one beautifully crafted app.
Whether you're a beginner discovering your Sun, Moon, and Rising signs or an experienced astrologer studying transits, progressions, and aspect patterns like Grand Trines and T-Squares, Heavenly Insight provides the precision tools and meaningful interpretations you need to explore the cosmos and understand your astrological blueprint with confidence.