A Developer's Honest Journey

Building an E-commerce Site from Scratch

The real story of building Peacock's Monocle — including the 2 AM debugging sessions, three payment processor switches, and everything that went wrong.

📅 June 2025 – Present 🛠️ Django + Railway + PostgreSQL ☕ ~47 cups of coffee
Scroll to explore

June 2025

Chapter 1: The Vision

Peacock's Monocle is a mobile boutique — a traveling fashion shop that sets up at events, markets, and pop-ups. The goal was simple: build an online presence so customers could browse and buy between events.

0
Lines of code
Optimism
1
Tutorial watched
💡
Key Decision

Chose Django over simpler options like Shopify. Why? Full control, no monthly fees eating into margins, and the ability to customize everything. Plus, learning Python/Django would be useful beyond this one project.

Late June 2025

Chapter 2: "It Works on My Machine"

Time to deploy. How hard could it be? The answer: harder than expected. Railway seemed simple enough, but the devil was in the details.

What Went Wrong

Forgot the Procfile. Then forgot gunicorn in requirements. Then the static files didn't load. Then the database connection string was wrong. Each fix revealed a new problem.

The Fix

Created a deployment checklist. Every. Single. Time.

Procfile
web: gunicorn peacocks_monocle.wsgi --log-file - release: python manage.py migrate

"The gap between 'working locally' and 'working in production' is where most projects die. Don't let it be yours."

— Lesson learned the hard way

July – August 2025

Chapter 3: The Three-Week Hash Error

This chapter alone could be its own horror novel. We needed payment processing, and what followed was weeks of cryptographic frustration.

3
Payment processors tried
200+
"Invalid HASH" errors
17
Emails to support
Attempt #1: Stripe Connect

Started with Stripe Connect because "it's what everyone uses." Realized too late it was designed for marketplaces with multiple sellers. We just needed simple checkout. Overkill.

Attempt #2: Payroc (WorldNet TPS)

Switched to Payroc for lower fees. Then began the infamous hash error saga. SHA-512 hashing. Exact field ordering. Amount in cents vs dollars. Date format with single-digit months. Every tiny detail mattered.

The hash string format that finally worked
TERMINALID:ORDERID:AMOUNT:DATETIME:RECEIPTPAGEURL:VALIDATIONURL:SECRET # NOT this (what we tried first): TERMINALID:ORDERID:AMOUNT:DATETIME:SECRET # The missing URLs in the hash cost us 2 weeks.
Attempt #3: Square

Finally switched to Square. Simpler API, better documentation, and it just... worked. Sometimes the best solution is knowing when to pivot.

"The payment processor's engineer said our code was correct. It was. The documentation just didn't mention that two additional URLs were required in the hash."

— From an email at 1:47 AM

August 2025

Chapter 4: The Case of the Black Images

Products need images. Simple, right? Upload an image, display it on the page. Except when Cloudinary URLs don't work, admin uploads show black squares, and local paths don't exist in production.

The Problem

We had cloudinary_id, cloudinary_public_id, image, and image_url fields scattered across models. Templates used different fields. Some worked locally, none worked in production.

The Solution

Standardized on Cloudinary's CloudinaryField and created a single image_url property that always returns the correct URL, with transformations for thumbnails.

products/models.py
@property def image_url(self): if self.cloudinary_id: return f"https://res.cloudinary.com/{cloud}/image/upload/{self.cloudinary_id}" return "/static/images/placeholder.jpg"

August 2025

Chapter 5: Why Are My Emails in Spam?

Order confirmations, password resets, magic links — they all need email. And email deliverability is its own special circle of developer hell.

📧
The Email Journey

Console backend (development) → Gmail SMTP (app passwords required) → Namecheap PrivateEmail (finally stable)

💡
Lesson Learned

Namecheap's DNS doesn't support MX records the way you'd expect. After hours trying to set up Resend with custom DNS, we gave up and used Namecheap's own SMTP server. Sometimes the "inferior" solution is the one that ships.

Late 2025

Chapter 6: We're Live!

After months of debugging, refactoring, and a few keyboard-smashing moments, Peacock's Monocle went live. Real customers. Real orders. Real revenue.

Products displayed
Payments processing
Emails sending
Inventory tracking

"Done is better than perfect. But done AND working is better than both."

— Post-launch reflection
💡
What We'd Do Differently

1. Start with Square from day one — simpler beats cheaper.
2. Set up proper staging environment before touching production.
3. Write tests for payment flows (we learned this the hard way).
4. Keep a decision log — future you will thank present you.

The Stack That Got Us Here

Tools we actually use, not just recommend. These survived the journey.

🚂
Railway
Hosting & PostgreSQL
Square
Payment processing
☁️
Cloudinary
Image hosting
📧
Namecheap
Domains & email

See all our recommended tools →

Ready to Build Your Own?

Try our AI-powered code generator. Describe what you want in plain English, get working code in seconds.

Try Code Forge → Visit Peacock's Monocle