▌ NICK COMMAND BASE ▐
2026-06-07 04:34

▌ RezMyCV.md

▒ PATH: RezMyCV.md
▒ SIZE: 6.8 KB
▒ MODIFIED: 2026-06-03 02:01
← BACK TO VAULT
# RezMyCV — AI-Powered CV Tailoring SaaS **Website:** https://rezmycv.com **GitHub:** ncik8/TailorMyCV **Stack:** Flask + Railway + Supabase + Stripe **Status:** Live and functional (June 2026) --- ## What It Does An AI-powered SaaS that helps job seekers tailor their CV to specific job descriptions. Users upload their CV, paste a job description URL, and get an ATS-optimized CV plus cover letter. ### Core Features - CV upload + AI parsing - Job description input (paste URL or text) - Gap analysis Q&A (identifies missing skills) - Tailored CV generation (ATS-optimized) - Cover letter generation (Pro+ tier) - PDF download - Profile editing (name, title, experience, education, skills, languages) ### Pricing Tiers | Tier | Price | Features | |------|-------|----------| | Free | $0 | 10 CV generations, tailored templates, Gap Q&A, PDF download | | Pro | $19/mo | Unlimited CV generations, priority support | | Pro+ | $25/mo | Everything in Pro + unlimited tailored cover letters | --- ## Tech Stack ### Backend - **Flask** (Python 3.9+) - **Routes:** Auth, CV upload/edit, Job paste, Gap analysis, Tailor, Dashboard, Upgrade, Stripe webhook - **Sessions:** Minimal — only `user_id` stored, all data fetched from Supabase per request - **API keys:** Stored in Railway env vars ### Database (Supabase) - **URL:** `https://tfkzirfxixlylizlmxeur.supabase.co` - **Tables:** - `users` — id, email, created_at - `profiles` — user_id, tier, cv_count, stripe_customer_id, stripe_subscription_id, stripe_subscription_status - `user_cvs` — user_id, name, title, email, phone, location, linkedin, summary, experience (JSON), education (JSON), skills (JSON), certifications (JSON), languages (JSON), projects (JSON), additional_info, raw_text, gap_answers (JSON) - `job_descriptions` — user_id, url, title, company, description, requirements (JSON), gap_questions (JSON), gap_answers (JSON), created_at ### Stripe Integration - **Products:** Test Pro ($1/HKD), Test Pro+ ($1.50/HKD) — demo prices for testing - **Real prices:** Pro $19/mo, Pro+ $25/mo - **Checkout:** `/checkout/<tier>` creates Stripe Checkout session - **Webhooks:** `/stripe/webhook` handles `checkout.session.completed`, `customer.subscription.created/updated/deleted` - **Upgrade flow:** `/upgrade-subscription/<tier>` uses `subscriptions.update()` to modify existing subscription (no duplicate) - **Cancel handling:** `customer.subscription.deleted` sets tier back to 'free', clears stripe_subscription_id and stripe_customer_id ### Key Files - `app.py` — main Flask app, all routes - `services/stripe_client.py` — Stripe helpers (create_checkout_session, construct_webhook_event, upgrade_subscription) - `services/user_cv.py` — load_cv, save_cv, parse_cv - `services/gap_analyzer.py` — generate_gap_questions, answer_gap - `services/tailor.py` — tailor_cv, optimize_cv (ATS keyword matching) - `services/cv_parser_ai.py` — AI parsing via MiniMax - `templates/` — HTML templates (dashboard, upgrade, edit_profile, gap_analyze, gap_answer, tailored_cv, etc.) --- ## Stripe Payment Flow (Fixed June 2026) ### The Problem Early Stripe integration had multiple bugs: 1. Webhook used `obj.get('subscription')` on Stripe objects — Stripe objects don't have `.get()` method → AttributeError 2. Session cached stale tier values — didn't read fresh tier from Supabase on each page load 3. Upgrade created duplicate subscriptions instead of modifying existing one ### The Fixes 1. Webhook handler uses `getattr(obj, 'subscription', None) or (obj['subscription'] if 'subscription' in obj else None)` — works for both dict-like and object-like Stripe responses 2. `init_session()` now fetches fresh tier from Supabase `profiles` table on every page load 3. Upgrade flow checks subscription status — if `incomplete_expired` or `canceled`, clears dead subscription first, then creates new one 4. `customer.subscription.deleted` webhook clears `stripe_customer_id` too (was missing) ### Upgrade Flow Logic ``` User on Pro → clicks "Upgrade to Pro+" → Check existing subscription status → If active: modify price via subscriptions.update() (proration=create_prorations) If dead: clear subscription_id + redirect to checkout If none: redirect to checkout ``` --- ## DNS Setup (June 2026) ### Registrar: name.com - Domain: rezmycv.com, renews Apr 25, 2027, $14/yr ### Nameservers: Cloudflare - `oswald.ns.cloudflare.com` - `paislee.ns.cloudflare.com` - Switched from Railway's default nameservers ### DNS Records (Cloudflare) - A record `@` → `192.0.2.1` (proxied) — Cloudflare CNAME flattening for apex - CNAME `www` → `yqc5rj4s.up.railway.app` (proxied) - TXT `_railway-verify` → `railway-verify=14c7a2cc582b682d2e576f345358ef62fbd4027d73498ef879038869e6307948` (grey cloud, for Railway SSL) ### Railway App Domain - `yqc5rj4s.up.railway.app` --- ## Email Setup ### Cloudflare Email Routing - Free email forwarding via Cloudflare - `hello@rezmycv.com` → personal email (configured in Cloudflare dashboard) - Also set up ForwardMX as backup --- ## Bug Fixes Log | Date | Bug | Fix | |------|-----|-----| | Jun 2, 2026 | Webhook `obj.get('subscription')` → AttributeError | Use `getattr()` with fallback | | Jun 2, 2026 | Session cached stale tier | `init_session()` now reads from Supabase | | Jun 2, 2026 | Pro → Pro+ created duplicate subscription | `upgrade_subscription()` modifies existing via `subscriptions.update()` | | Jun 2, 2026 | Cancel didn't clear stripe_customer_id | Added to webhook handler | | Jun 2, 2026 | "Current Plan" showed on wrong tiers | Use template `tier` var (fresh DB), not `session.get('tier')` | | Jun 2, 2026 | Dashboard name showed "Test Name}" | Extra `}` removed from template (was visual display bug in browser) | | Jun 2, 2026 | Landing page "30 seconds" | Changed to "seconds" | --- ## Marketing Plan (June 2026) ### Target Audience Job seekers who are tired of mass-applying with generic CVs ### Content Strategy - **Positioning:** "EasyApply is broken — do the work" / "Effort beats automation" - **RezMyCV is #1** in all content as the solution ### Distribution Channels 1. **CoinCUstrard** (Nick's news channel) — Day 1 article 2. **News outlets** — Day 2+ article (different angle) 3. **YouTube Long** (12k followers) — Day 2-3 4. **YouTube Shorts** — Day 3-4 5. **Reddit** — Day 4-5 (Nick posts genuine story → Henry handles comments) 6. **X** — Throughout 2 weeks (3-4 posts) ### Article Angle "10 Job Sites That Require Effort (And Why RezMyCV is #1)" — targeting people who clicked EasyApply 300 times with no interviews, positioning RezMyCV as the "effort" alternative that actually works. ### Reddit Story Hook "300 EasyApply → 0 interviews. 100 with RezMyCV → 7 interviews." --- ## Related Notes - [[Projects]] — project index - [[RezMyCV Content Calendar]] — content rollout schedule
▒▒▒ READY CPU: 12% MEM: 4.2G NET: OK OBSIDIAN ▒ VIEWING