04 — Commerce / Sake / 2026

FUJISANSAKE

A store for the Bushido series, five sakes made at the foot of Mt Fuji. Retail orders from individuals and a trade channel that shows wholesale prices only to approved stockists run on the same site.

2026
Fuji, Shizuoka
CLIENT
Kondo Pharmacy Co., Ltd. — 株式会社近藤薬局
SECTOR
Commerce / Sake
ROLE
Design, development, payments and stock, admin, bilingual, SEO, operation
YEAR
2026
LIVE
sakefujisan.com ↗

01 / PROJECT

WHAT THE
PROJECT IS.

The Bushido series is five sakes made with Mt Fuji spring water and rice from Hyogo and Shizuoka. Alongside a page for each one, the site carries the water, the rice and the brewing, food pairings and a guide for stockists, in Japanese and English.

There are two ways to buy. An individual goes from the cart to Stripe's payment page; a business registers as a stockist, and once approved sees wholesale prices and a price list by the case. Because this is mail-order alcohol, the site confirms the buyer is over 20, carries the underage-drinking notice and publishes the disclosures Japan's specified commercial transactions law requires.

The side that runs the shop is built too. The admin opens on sales, orders that need attention and stock warnings, and from the browser covers shipping and refunds, prices and stock, stockist approval, enquiries and inviting staff. I handled it end to end: design, implementation, payments and stock, the admin, SEO, deployment and operation.

02 / HOW IT WORKS

THE DECISIONS
UNDER IT.

FROM THE IMPLEMENTATION

01
The last bottle is not sold twice

Stock is reserved when the customer goes to pay and taken off the shelf once payment is confirmed. Taking it off only at confirmation leaves the few minutes on the payment page open, and in them several people can buy the same last bottle. Not overselling rests on one UPDATE that only succeeds while on-hand minus reserved still covers the quantity; D1 has no interactive transactions, so when one line of a multi-line order comes up short, the lines already reserved are put back. Checkout sessions expire after 30 minutes, Stripe's minimum, so an abandoned payment does not hold stock for a day.

02
Wholesale prices open on approval, not on sign-up

Registering as a business is self-declared, so it shows nothing on its own. What opens wholesale pricing is an approved stockist review, and an account with no review on record counts as unapproved. If the review cannot be read, the prices stay hidden: a price once shown cannot be taken back. A liquor licence number is required only of retailers and wholesalers — a restaurant or inn pouring on the premises is not selling alcohol and needs none, and a required field that does not match the business turns away the right customers and invites made-up answers.

03
The webhook confirms an order, exactly once

Stripe may deliver the same event more than once. The order is confirmed by an UPDATE that only moves a pending order to confirmed, and only the delivery that actually made that change takes the stock and sends the emails. A failed email does not fail the webhook — the order is confirmed either way, so it is logged instead; Stripe is only asked to retry when the confirmation itself could not be written. Refunds and chargebacks raised from the Stripe dashboard come back the same way, with full and partial refunds recorded against the order.

04
Price and stock are kept apart

Sakes, sizes and descriptions live in code; price and stock are overrides in the database, in two separate tables. In one table, correcting a price would create a row with zero stock and sell the item out on the spot. Staff move stock at every count; only the owner can change a price. With no row, the code's value stands, and if the database cannot be read the shop keeps selling at the code's prices. Checkout, the admin and the wholesale price list all read the merged catalogue, so no path is left where a price changed in the admin does not apply.

05
The age check is enforced on the server

Age is confirmed when a visitor arrives and again just before payment. The second check does not rest on the checkbox: the server action that starts checkout refuses a request without it, because a server action can be called without the page. The legal disclosures come from a single file, and a value not yet confirmed is left empty rather than filled with a plausible placeholder, which can go live looking like the real thing.

03 / DELIVERED

WHAT WAS
BUILT.

  1. 01

    Pages for five sakes, reading on the water, the rice and the brewing, food pairings, Japanese / English

  2. 02

    Accounts with email verification, cart, Stripe Checkout and order confirmation by webhook

  3. 03

    Stockist registration and review, with wholesale prices and a by-the-case price list shown only once approved

  4. 04

    Stock reserved during checkout, and a stock alert sent only when a threshold is crossed

  5. 05

    Order history, a receipt made for printing, and cancellation requests before shipping

  6. 06

    An admin with a sales dashboard, order filters and CSV export, packing slips, full and partial refunds, price and stock editing, stockist review, enquiries, and staff invites with two roles

  7. 07

    Age confirmation, the underage-drinking notice and the specified commercial transactions disclosures

  8. 08

    Rate limiting on sign-in, registration and enquiries, product structured data, per-page metadata and sitemap

04 / TECHNOLOGY

THE STACK
BEHIND IT.

  • Next.js
  • React
  • TypeScript
  • Cloudflare Workers
  • D1
  • Drizzle
  • Better Auth
  • Stripe
  • Resend
  • Tailwind CSS
  • Jest
NEXT — 05Jurakuen→ALL WORKS ↗