Skip to content

Beer Store ​

A beer store on the store: styles and breweries, strength and volume, an age gate and the date of birth at checkout, container deposits, licence hours, Uber Direct delivery with an ID check at the door.

kitloom/beer-store · admins: kitloom/beer-store-filament, kitloom/beer-store-nova

Video lessons ​

Recorded in the Filament admin; the Nova admin works the same way.

A beer store on kitloom/store: what a beer is beyond a product (its style, brewery, strength, volume and container), the minimum age with an age gate on the site and the date of birth at checkout, container deposits, the hours the licence allows, and Uber Direct as a delivery method and courier with an ID check at the door. The admin is kitloom/beer-store-filament or kitloom/beer-store-nova; the theme made for it is kitloom/taproom-theme.

What a beer is ​

Beers and deposits

A beer is a product of the store, with more on its form, under Details:

FieldWhere it goes
ABV (%), IBU, Volume (ml)the rows under the description on the product page; the sheet's columns
Container — can, bottle, keg, growlerthe rows, and the deposit the cart adds
Style (IPA, lager, stout…) and Brewerytaxonomies of the product type, with pages of their own: /store/style/ipa/, /store/brewery/north-fork/
Packnot a field: a beer sold by the pack is a variable product with the option Pack and a variant per size (1, 6, 12, 24) — the deposit counts the containers in it
Best beforewith kitloom/inventory: the earliest expiry among the lots on the shelf, read from the warehouse

A product with a container or a strength is a beer: it starts at the Minimum age of the settings (18 in Manitoba, Quebec and Alberta, 19 elsewhere in Canada, 21 in the US) unless the form says otherwise. A glass or a tote on the same shelf is for anyone.

Age ​

Age, hours and the door: the beer store settings
  • The gate — Age gate on: every page asks "Are you 18 or older?" before it shows anything; the answer is kept in a cookie for the days set, and No sends the visitor to the Age gate exit. The question is drawn by the theme (the ageGate part), in the browser: the page cache keeps one page for everyone.
  • The checkout asks the date of birth when the cart holds anything with a minimum age, and refuses an order under it.
  • The order keeps the date (data.date_of_birth); the delivery is told what the courier must do at the door (Checked at the door): an ID for the minimum age, a signature, a photo of the handover. A courier called by API (Uber Direct) gets it as its verification.

Deposits ​

Beer Store → Deposits: one row per kind of container — the amount per container in the store's currency, the tax class the province taxes it by (zero where it is not taxed), on or off. A beer's container names its deposit; a pack of six cans adds six. The deposit rides on the cart line beside the price, is never discounted by a coupon, and is a line of its own on the order, the receipt and the emails ("Container deposit"), so the refund with the empties is one line to find.

Hours ​

Delivery and pickup hours, per weekday as 10:00-22:00, a day left empty closed, nothing set at all — any time. A delivery window the customer picks outside them is refused at checkout with the hours in the message; an order placed while closed without a window is refused too; Uber is not asked for a quote outside them, and the board refuses to call it.

Uber Direct ​

A courier from Uber brings the order from the store's door to the customer's, with the ID checked at the door. Set up once:

  1. At direct.uber.com (the Uber Direct dashboard) the organization has a Customer ID; under Developer → Applications make one with a Client ID and Client Secret. Under Webhooks add https://your-site/wp-json/beer-store/v1/uber for Delivery status and Courier update, and keep the signing key it shows.
  2. Beer Store → Settings → Uber Direct: the Customer ID, the Client ID, the Secret and the signing key (both encrypted, never shown again; leave the fields empty to keep them). Test mode runs the deliveries with Uber's robot courier: nothing moves, the statuses do. Check Uber access asks Uber for a token with what is saved.
  3. Store → Delivery zones: add the method Uber Direct to the zone the store delivers in, with the store as the pickup (name, address on one line, phone, a note for the courier), a markup on Uber's fee if you want one.

Then, on the site, a customer who types an address in the zone sees Uber Direct with Uber's fee (plus the markup) and the time it quotes, once the city or the postcode is known. When the order is ready (or paid, as the store's Call the courier setting says) the delivery is made at Uber — meet at the door, bring it back when nobody of age is there, verification as the delivery requires — and the board shows Uber's id and the tracking link. Uber's webhooks move it: scheduled, picked up, on the way, delivered (which completes the order); a delivery Uber could not make fails, and when the reason is the customer's age or ID the owner is told (beer_delivery.id_refused, with kitloom/notifications). A cancelled order calls the delivery off at Uber; store:sync-deliveries asks Uber about the active ones every fifteen minutes in case a webhook was missed.

Demo ​

bash
php artisan beer-store:demo   # the store's demo first when the site has no products, then five styles, four breweries, a dozen beers by the pack, the deposits, the hours, Uber on the zone, two orders of beer

For developers ​

  • The beer's fields reach the store through its ProductExtension (Products\BeerFields): the writer checks them, both admins draw them, the sheet exports them, the storeProduct part carries the rows and the taxonomies. Another vertical does the same with a class of its own.
  • The deposit is a LineExtras provider (Deposits\ContainerDeposits); the date of birth and the hours a CheckoutStep (Checkout\AgeStep); Uber a ShippingMethod (uber_direct) and a Courier on Laravel's HTTP client (Uber\UberClient, Uber\AccessTokens), with Testing\FakesUber for a test: connectUber(), fakeUber(), uberDeliveryReads(), postUberWebhook().
  • Settings: BeerStoreOptions; the hours through Hours\StoreHours; the deposits through Deposits\DepositWriter behind manage_beer_store.
  • kitloom-beer-store.uber.register turns Uber (the method, the courier, the webhook) off; kitloom-beer-store.notifications.register the notice.

Tests ​

From the application that installs the package: php vendor/bin/phpunit --testsuite BeerStore.

Read more ​

kitloom — modules and themes for Laravel.
Contact · Pricing · Services · License · Refunds · Privacy · Community · YouTube · [email protected]