Codex · III

The buy button

The buy button

The buy button is a few lines of HTML you paste on any page. No framework, no install step on your end. The button renders on first paint; the provider picker and the payment-provider SDKs load on demand the first time a buyer clicks. It works in any site or framework that renders HTML, including the no-code site builders that let you paste HTML embeds.

Confirmed working with: Angular, Hugo, Jekyll, plain HTML, Webflow (Custom Code embed), Carrd (Embed element), Framer (HTML embed), Squarespace (Code Block), WordPress (Custom HTML block), Ghost, Notion sites (HTML embed widgets). If your site lets you paste HTML on a page, the buy button works there.

Attribute reference

AttributeRequiredWhat it acceptsNotes
endpointNoURL stringAlmost always omit this. The button auto-detects its API base: https://api.coinmoebius.com in production, http://localhost:8787 on localhost. Only set it as an override for an unusual setup (e.g., a self-hosted proxy in front of the API).
project-idYesString, prefixed proj_From your project page in the dashboard. Safe to expose publicly, it's an identifier, not a credential.
product-idYes (in strict mode) / Recommended (in ad-hoc mode)String, any format you chooseYour internal product identifier. In strict mode (the default), this is how the worker looks up the price from your catalog. In ad-hoc mode, it's still passed through to your webhooks in metadata.productId so you can map transactions back to inventory. See the Strict mode vs ad-hoc mode section below.
amountOnly in ad-hoc modeDecimal number as stringThe price in major units (e.g., dollars, not cents). Must be positive. Omit this in strict mode. The worker reads the canonical price from your catalog and ignores any value in the HTML. Required only when the project is in ad-hoc mode and the product-id is not in the catalog.
currencyOnly in ad-hoc modeThree-letter ISO 4217 code or your own unit nameUSD, EUR, etc. for cards / crypto rails. The pay-by-mail provider accepts any string (e.g., GBK) because the merchant is the one settling the transaction. Same rule as amount: omit in strict mode, required in ad-hoc mode for products not in the catalog.
labelNoStringThe button text. Defaults to Buy. Use this to distinguish buttons on the same page.
customer-refNoString, any format you chooseAn opaque identifier for the buyer in your own system, like a logged-in user id. The button forwards it to the worker as metadata.customerRef, and the worker tags the transaction with it. Later you can ask "which of my users paid?" using your own id, without us holding any actual customer data. Omit for anonymous checkouts.
themeNodark or lightPicks the built-in color scheme for the button and popup together. Defaults to dark. Set theme="light" for the light scheme. Your CSS variables override either one. See the styling guide.
editable-amountNotrue or falseWhen true, the popup shows an amount field the buyer fills in (tips, donations, pay-what-you-want). Defaults to false, where the amount you set is fixed and no field appears. The payment buttons stay disabled until the amount is a positive number.

Multiple buttons on one page

The script registers the button once, then any number of instances can render on a page. Each instance is independent. The example below uses strict mode (the default): no amount or currency in the HTML, because the worker looks the price up from each product in your catalog.

<coin-moebius-buy
  project-id="proj_YOUR_ID"
  product-id="t-shirt-medium"
  label="T-shirt (medium)">
</coin-moebius-buy>

<coin-moebius-buy
  project-id="proj_YOUR_ID"
  product-id="mug-blue"
  label="Blue mug">
</coin-moebius-buy>

If your project is in ad-hoc mode (donation widgets, tip jars), each button also includes amount and currency. See Strict mode vs ad-hoc mode for the full picture.

Theming the button

The buy button renders inside a Shadow DOM, which means none of your page's existing CSS reaches into it. That's deliberate: it keeps the button looking the same on every site, and stops a stray * { box-sizing: ... } rule from breaking the picker modal. You style it through two surfaces from your own stylesheet: CSS custom properties for colors, fonts, and shape, and ::part() selectors for everything else. No JavaScript involved.

Every variable and part is documented on the styling guide, with live examples you can click and copy:

Open the interactive styling guide →

The success and cancel URLs

When you connect Stripe or NOWPayments in the dashboard's Providers tab, you set two URLs:

  • Success URL, where the buyer lands after a successful payment. Stripe and NOWPayments append a query parameter (?session_id=... for Stripe, ?NP_id=... for NOWPayments) so your success page can identify which transaction completed. Most static sites just show a generic "Thanks, your payment is processing" message and rely on the dashboard for the source of truth.
  • Cancel URL, where the buyer lands if they back out before paying. Often the same as your cart page.

Both URLs are configured per-provider in the dashboard, the element doesn't need to know about them.

Ready to wire it up?

Free tier covers most sites and never asks for a card.