Your Menu on Your Website

One script tag. Your whole menu, a cart, and pickup orders paid in bitcoin.

How It Works

The menu widget, menu.js, draws your catalog on any page of your website, with a cart. A shopper adds items, gives an email for the receipt and pays on SatsRail's checkout, in an overlay on your page. The order is a pickup order at one of your locations, like an order from your Ubicada page.

  1. The script reads your menu at a location with your publishable key.
  2. It draws your products by type, with their photos, prices, options and what is sold out, in your Ubicada page's colours, fonts and corners.
  3. The shopper adds items to a cart kept in their browser.
  4. Pay sends the lines to SatsRail, which prices and taxes them, checks stock and your opening hours, and makes the order.
  5. SatsRail's checkout opens over your page; the shopper pays from a bitcoin wallet, straight to yours.
  6. The widget thanks the shopper with the order number and a link to the receipt. The order arrives in your Orders, marked "Website".

Set it up in the merchant portal under Ecommerce → Your website, which shows your snippet and your menu as a website shows it. Selling one product? The Embed Button is simpler.

Add It to Your Page

Paste this where your menu goes. It fits the width it is given.

<div id="satsrail-menu"></div>
<script src="https://satsrail.com/js/menu.js"
  data-key="pk_live_..."
  async></script>
Options
AttributeWhat it does
data-keyYour publishable key (pk_live_…). Required. A test key (pk_test_…) shows the menu but takes no orders.
data-locationOne of your locations, by its id. Without it, your main location, and a picker when you have more than one.
data-checkoutHow payment opens: overlay (the default, over your page), new_tab or redirect.
data-langen or es. Without it, your Ubicada page's language.
data-targetA CSS selector for where the menu goes, if not #satsrail-menu.

The menu is drawn in a shadow root: your website's styles don't change it, and its styles don't change your website. On your page itself it adds only its fonts, and stops the page scrolling while its cart or the checkout is open.

Allowed Websites

Until you add a website in Ecommerce → Your website, your menu, your publishable key and your embedded checkout work on any website. Once you add one, only the websites you list can use them: a page anywhere else gets 403 origin_not_allowed, and the widget says so.

  • example.com allows example.com and www.example.com.
  • *.example.com allows example.com and every subdomain.
  • Pages must be served over https. localhost, *.localhost and 127.0.0.1 work over http too, while you build.
  • The checkout and the receipt can be framed only by the websites you list.

A publishable key is public by design. The list keeps other websites from showing your menu and taking orders for you; a program on a server sends no Origin and can still read your menu, as it can read your Ubicada page.

When It Takes Orders

Your menu shows on every plan. It takes orders by the same rules as your Ubicada page:

  • the e-commerce module is on for your account;
  • online orders aren't paused (one Pause for both);
  • you can take payments, and your plan takes more orders;
  • the pickup location is open by its opening hours (a location with none set is always open).

When it can't, the menu shows without Add and says why, in the shopper's language.

Receiving Orders

A website order is an ordinary order: it appears in Orders, sends the shopper the receipt, and fires your webhooks: order.created, order.updated, invoice.paid and payment.received. Its metadata says where it came from:

{ "source": "website", "pickup": true, "origin": "example.com" }

On your page, the widget dispatches satsrail:order on document when an order is placed and again once it is paid, with { order_number, paid, receipt_url }.

Build Your Own

The widget uses three endpoints, which you can call from your own page. Send your publishable key in an X-Embed-Key header. They answer CORS from any origin; your allowed websites are checked by SatsRail.

Read the menu
curl https://app.satsrail.com/api/v1/pub/website/menu?location=LOCATION_ID \
  -H "X-Embed-Key: pk_live_..."

The products on sale at the location, each with its skus (an id, a name, a price and whether it is sold out there); the locations and whether each is open; ordering (open, and a reason and message when not); currency_format; and your Ubicada page's design.

Place an order
curl -X POST https://app.satsrail.com/api/v1/pub/website/orders \
  -H "X-Embed-Key: pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "location_id": "LOCATION_ID",
    "email": "shopper@example.com",
    "name": "Ana",
    "items": [ { "sku_id": "SKU_ID", "qty": 2 } ]
  }'

SatsRail prices and taxes the lines; any price you send is ignored. Up to 20 lines, 1 to 99 of each. It answers 201 with the order's order_number, totals, checkout_url (to frame), payment_url (to open), status_url and expires_at.

StatusCodes
409ordering_paused, ordering_unavailable, plan_limit, location_closed
422sku_unavailable, sold_out, insufficient_stock, invalid_quantity, empty_order, too_many_items, invalid_email, name_too_long, location_not_found
403origin_not_allowed, live_key_required
503price_unavailable: the bitcoin price can't be read; try again in a minute

Each error has a message for the shopper, in the menu's language, and details.errors names the sku_id it is about.

Was it paid?
curl "https://app.satsrail.com/api/v1/pub/website/orders/ORDER_ID?token=ORDER_TOKEN" \
  -H "X-Embed-Key: pk_live_..."

The order's status, number and, once paid, its receipt_url; with the order's own token from the order's answer. Inside the overlay, SatsRail's checkout also posts satsrail:completed to your page when it is paid, as for the Embed Button.

Limits

Counted per shopper, never per key, so a busy website doesn't run out: 60 menu and status reads a minute, 10 orders a minute per shopper, and 120 orders a minute per key.

Your menu, on your site, today

Open Ecommerce → Your website in the portal, copy your snippet and paste it into your page.

Get Your API Key →