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.
- The script reads your menu at a location with your publishable key.
- 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.
- The shopper adds items to a cart kept in their browser.
- Pay sends the lines to SatsRail, which prices and taxes them, checks stock and your opening hours, and makes the order.
- SatsRail's checkout opens over your page; the shopper pays from a bitcoin wallet, straight to yours.
- 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
| Attribute | What it does |
|---|---|
data-key | Your publishable key (pk_live_…). Required. A test key (pk_test_…) shows the menu but takes no orders. |
data-location | One of your locations, by its id. Without it, your main location, and a picker when you have more than one. |
data-checkout | How payment opens: overlay (the default, over your page), new_tab or redirect. |
data-lang | en or es. Without it, your Ubicada page's language. |
data-target | A 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.comallows example.com and www.example.com.*.example.comallows example.com and every subdomain.- Pages must be served over https.
localhost,*.localhostand127.0.0.1work 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.
| Status | Codes |
|---|---|
| 409 | ordering_paused, ordering_unavailable, plan_limit, location_closed |
| 422 | sku_unavailable, sold_out, insufficient_stock, invalid_quantity, empty_order, too_many_items, invalid_email, name_too_long, location_not_found |
| 403 | origin_not_allowed, live_key_required |
| 503 | price_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 →