JS JIG online store home page screenshot

This project came from Sergey Kapustin — a great designer who is easy to work with. The site is for JS JIG, a fishing tackle manufacturer from Rostov-on-Don: Barbarian and Kogot jig heads, screw-in and quick-release drop weights. Four products and 149 size-and-weight variants. Sergey designed the store and was about to move the mockup to Tilda. I talked him out of it: with a catalog like this and online payments, a site builder quickly hits its ceiling. The deal was simple: his design, my code.

The storefront is plain HTML, CSS, and JavaScript with no build step or frameworks. The vertical Jig for Sports ticker along the left edge and the red pill navigation made it from the mockup into code. The cart is stored in localStorage, so the counter stays in sync across tabs. When you add an item, the card flies into the cart with an animation — and respects prefers-reduced-motion. Photos are served as webp with retina versions and a fallback.

Payments run through Ozon Pay: bank card, SBP instant payments, Ozon Card. Ozon ships no ready-made SDK, so I wrote the acquiring API adapter from scratch: request signatures, webhooks, receipts. Two security rules are baked into the tests: the order total is calculated only by the server from the price list in the database, and a payment counts as confirmed only after a direct status request — the webhook is never taken at its word. Receipts go out from Ozon Bank, so the store needs no online cash register of its own.

The admin panel runs on Laravel and Filament. The client edits prices and stock, reorders products by dragging, updates page texts, uploads photos, and exports orders to CSV. The key architectural idea: the panel patches the static files right on the server. Hitting “Update storefront” makes Laravel regenerate the products file, and a single uploaded PNG turns into three optimized images. The visitor always gets fast static pages, with no database and no rendering behind them.

I wrote the backend TDD-style: test first, then code. The result is 453 tests, with more lines of tests than of production code. Deployment is one button in GitHub Actions: tests run in Docker against a real MySQL, files sync to Beget hosting, smoke checks fire, and rollback is a single file swap if anything goes wrong. It took two weeks from the first commit to live payments.

Technologies: HTML5, CSS, Vanilla JS, PHP 8.5, Laravel 13, Filament 5, MySQL 8, Ozon Pay (Ozon Acquiring API), Docker, GitHub Actions, Beget

js-jig.ru