Integration Guide

Accept BB84 Payments On Your Site

Drop a QR code onto a checkout page, invoice, or donation button that already carries the amount and currency — no backend, no wallet connection, no API key. The customer scans it with the BB84 app, and the recipient, amount, and asset (ETH, USDC, or EURC) are filled in automatically.

This is the same payload shape bb84.com/receive has always generated for a plain ETH request — bb84-pay.js just makes it embeddable on your page, and extends it to USDC and EURC.

Quick Start

Add this to any page. Replace shop.bb84.eth with your own address or bb84.eth name.

<!-- 1. A placeholder where the QR should appear -->
<div data-bb84-pay
     data-to="shop.bb84.eth"
     data-amount="20"
     data-asset="usdc"></div>

<!-- 2. The widget script — finds it and renders the code -->
<script src="https://bb84.com/bb84-pay.js" defer></script>
That's it. bb84-pay.js scans the page for data-bb84-pay elements on load, lazy-loads the (tiny) QR-rendering library from cdnjs the first time it's needed, and draws a self-contained QR + amount caption in place.

The Payload

A BB84 payment-request QR encodes an ethereum: URI — the recipient, followed by the amount in the asset's smallest unit:

ethereum:<recipient>?value=<raw-units>&asset=<usdc|eurc>

<recipient> is a 0x… address, a *.bb84.eth name, or a real Mainnet *.eth name. Two concrete examples:

// Request 20 USDC
ethereum:shop.bb84.eth?value=20000000&asset=usdc

// Request 0.05 ETH — omit "asset" for plain ETH
ethereum:shop.bb84.eth?value=50000000000000000

You never need to hand-build this string — BB84Pay.buildValue(to, amount, asset) does the decimal-to-raw-units conversion for you (see Programmatic API).

Amount & Asset

value is always an integer in the asset's smallest unit, matching how BB84 stores balances on-chain:

Assetdata-assetDecimals"1" means
Ethereumeth (default — can be omitted)181 wei
Circle USDCusdc60.000001 USDC
Circle EURCeurc60.000001 EURC
⚠️
Leaving out data-amount renders an address-only QR (a generic "pay me" code, no fixed amount) — the customer types their own amount in the app. This is intentional: it's how the widget stays useful for tip jars and donation pages, not just fixed-price checkouts.

What Happens on Scan

1

Customer opens the BB84 app and taps the QR icon on Send

The camera reads the ethereum: URI straight off your page — no app switch, no copy-pasting an address.

2

Recipient, amount, and currency are filled in automatically

If the code specifies asset=usdc, the app switches its active currency to USDC so the right balance and OTA are shown — since this is an explicit request from you, not something the customer typed themselves.

3

Customer reviews and sends

Nothing leaves their device until they confirm — the widget only ever displays a request, it can't move funds on its own.

Under the hood this is the exact same shorthand the BB84 app also accepts when typed directly into the recipient field, e.g. 20usd.shop.bb84.eth — handy for sharing a request over chat instead of a QR image.

Declarative Embed

The simplest integration needs no JavaScript of your own. Add one <div data-bb84-pay> per QR code you want on the page:

<div
  data-bb84-pay
  data-to="shop.bb84.eth"       <!-- required -->
  data-amount="49.90"       <!-- optional -->
  data-asset="eurc"         <!-- optional, default "eth" -->
  data-size="220"           <!-- optional, pixels, default 200 -->
  data-label="true"          <!-- optional, shows "Requesting 49.90 EURC" -->
></div>

Elements are matched once on DOMContentLoaded. Adding a widget div later (e.g. after fetching a cart total) — call BB84Pay.init() to pick up any new ones, or just call BB84Pay.render() directly.

Programmatic API

For a checkout flow where the total isn't known until runtime:

BB84Pay.render("#checkout-qr", {
  to:     "shop.bb84.eth",
  amount: cartTotal.toFixed(2),   // decimal string, e.g. "49.90"
  asset:  "usdc",               // "eth" | "usdc" | "eurc" — default "eth"
  size:   220,
});

BB84Pay.render(target, options)

Draws the QR into target (a CSS selector string or a DOM element). Returns a Promise that resolves once drawn, and rejects with a descriptive error if to/amount don't parse — the widget also shows that error inline so a bad integration fails loudly, not silently.

OptionTypeDefaultNotes
tostringRequired. Address, bb84.eth, or Mainnet .eth name.
amountstring | numbernoneOmit for an address-only, any-amount QR.
asset"eth"|"usdc"|"eurc""eth"Ignored (no asset= param emitted) when "eth".
sizenumber (px)200Outer widget width/height.
labelbooleantrue when amount setShows a "Requesting X ASSET" caption under the code.

BB84Pay.buildValue(to, amount, asset)

Returns the raw ethereum:… string without rendering anything — useful if you're feeding your own QR library (a native app, a PDF invoice generator, etc.) instead of the DOM widget. Returns null if the inputs don't parse.

const value = BB84Pay.buildValue("shop.bb84.eth", "20", "usdc");
// "ethereum:shop.bb84.eth?value=20000000&asset=usdc"

Live Demo

This calls the real bb84-pay.js hosted at /bb84-pay.js — the exact widget your page would load.

No-JS Alternative

If your site can't load third-party scripts at all, generate the QR once ahead of time at bb84.com/receive (ETH only), download the PNG, and embed the image directly — a static <img> tag works everywhere. For USDC/EURC or a dynamically-computed amount, build the ethereum:… string yourself using the format in The Payload and feed it to any QR-code image generator.

Security Notes

  • The widget only displays a request — it never has access to funds, private keys, or the customer's device beyond rendering a QR image.
  • bb84-pay.js is static and unauthenticated by design, same as any QR-image generator — nothing about it needs an API key, and nothing you embed can be revoked or rate-limited server-side.
  • Double-check data-to is your own address or bb84.eth name before shipping — the widget validates the shape of the recipient (a valid address or ENS name) but has no way to know whose it is.
  • The QR-rendering library (qrcodejs) is lazy-loaded from cdnjs.cloudflare.com only once, and only if window.QRCode isn't already present — check your CSP allows that origin if you enforce one.

Support Ticket

Frage, Bug-Report oder Feedback — wir melden uns per E-Mail.

Impressum·Privacy·Terms