HPAY Request v1
HPAY Request v1 is the link format behind the HPAY tools in Games/Biome 1.21.0. It is Beta: tested by the project's own test suites, not independently audited. Start with small amounts.
Everything here is non-custodial. The payer's own wallet signs, and the money goes straight from the payer to the address in the request. Hartii holds no keys, funds or balances, takes no fee, and the tools add no contract. The payer pays the normal network fee. The network is Quai mainnet, zone Cyprus-1, chain 9.
The request link
A request is one link with a query string, normally on https://hartiibiome.com/pay:
https://hartiibiome.com/pay?to=<your 0x00 address>&amt=1500000000000000000&ref=order-1001&chain=9&v=1| Field | Meaning | Rules |
|---|---|---|
to | Who is paid. Required. | A Hartii @name, or a Cyprus-1 Quai address (starts with 0x00). A mixed-case address must pass its checksum. Qi addresses and the zero address are refused. |
amt | Exact amount in wei. Optional. | Digits only, no leading zero, above 0 and below 10^27 wei (one billion QUAI). Without it the payer chooses the amount. |
usd | The dollar price the amount was quoted at. Optional. | Above $0 and at most $1,000,000.00, up to two decimals. Only valid together with amt. It is a label on a quoted QUAI amount, never an instruction to convert. |
ref | Your order reference. Optional. | 1 to 64 characters: letters, digits and . _ : -, starting with a letter or digit. |
tag | The last digits of amt. Optional. | 1 to 9 digits, written as text (keep leading zeros). amt must end with it. A tag needs amt. A tag of all zeros is refused when a link is built. A tagged request needs a 0x address, not a name, because a name can change owner. |
exp | When the request ends. Optional. | Unix seconds, no later than 2100-01-01 UTC. |
memo | A note for the payer. Optional. | Plain text, up to 140 characters. Display only; it is never written on the chain. Hidden and direction-changing characters are refused. |
chain | Network. | 9 when present. |
v | Format version. | 1 when present. |
How a link is read:
- Unknown keys are ignored. A repeated known key refuses the whole link, because two readers could disagree on which copy wins.
- Any bad known field refuses the whole link. A bad value never half-applies.
- Old PayLink links (
to,amt,memo,exp,chain,v) read unchanged, and the old pay page ignores the new fields. The olderamountkey (decimal QUAI, converted to exact wei) still works. Giving bothamtandamountis refused. - Every refusal has a code and a plain sentence for the person. The repository publishes 107 shared test vectors that the Gallery copy and the CLI must pass unchanged.
A request is matchable (it can be tied to one payment on the chain) only with a 0x address, an exact amt, a
non-zero tag and an exp. The tag makes each amount unique, so the chain shows who paid.
Pricing in dollars
GET https://hartiibiome.com/api/hpay/quote?usd=25.00The answer is JSON with ok: true, version: 1, and these fields:
| Field | Meaning |
|---|---|
usd | The dollar price, as text with two decimals. |
amountWei, amountQuai | The QUAI for that price, exact. Rounded up to the wei, so the payee is never short. |
quaiUsd, quaiPerUsd | The mean price the quote used, and its inverse. |
issuedAt, expiresAt, ttlSeconds | When the quote was made and when its lock ends. ttlSeconds is 900. |
spreadPercent, maxSpreadPercent | How far apart the answering sources were, and the limit (2). |
sources | Each source with its name, price, time and age. |
Rules, from the Function's code:
- The dollar amount is 0.01 to 1,000,000.00 with up to two decimals. Nothing but that number is read.
- Three price sources: MEXC (QUAIUSDT), Gate.io (QUAI_USDT, last trade, dated when it is read) and CoinGecko. At least two must answer, and every source that answers must be at most 5 minutes old and agree with the others within 2 percent. CoinGecko is an aggregate that can include the exchanges, so these are separate sources, not independent ones. USDT is treated as a dollar. The quote uses the mean of the answering prices.
- If fewer than two answer, any answering source is stale, or they disagree, the answer is HTTP 503 with a reason and no quote. Nothing guesses a price.
- The quote is locked for 15 minutes (
expiresAt). The rates are cached at the edge for 30 seconds and a refusal for 10 seconds. - Pages that price in dollars check the answer again in the browser: at least two sources, the 2 percent limit and the arithmetic. An answer that fails any check is never shown as a price.
requestFromQuote(quote, { to, ref, tag, memo }) in hpayRequest.mjs turns a quote into a request that ends when
the quote does. generateTag makes a 6 digit tag by default, never all zeros, and tagAmount adds the tag to a price
by raising it by at most 10^digits - 1 wei (under a billionth of a QUAI at nine digits).
The dollar figure in a link is the link maker's own price. The chain only records QUAI.
Price history for statements
GET https://hartiibiome.com/api/hpay/history?from=<unix>&to=<unix>The hourly QUAI dollar price, for Statements and bookkeeping. It is not a quote.
- One source, labelled: MEXC QUAIUSDT hourly candles, the price at the start of each hour.
- At most 1000 hours per call, not before 2025-01-01, and not more than an hour ahead.
- An hour with no candle has no price. It is left out, and a page shows a dash, never 0.
Matching a payment to a request, with no contract
A matchable request can be matched on the chain. A payment counts only if all of this is true, judged on the Quai node's own answers:
- the transaction succeeded (receipt status 1);
- the money went to the request's address, as a plain transfer or as a QuaiRelay send;
- the amount is exactly
amt; - the block time is inside the window from when the request was made to
exp(a start time more than 5 minutes in the future is refused); - it is the earliest such payment, and it has enough confirmations.
The result is paid, pending, unmatched or expired. A payment that looks related but fails a rule goes to an
unmatched list for a person to look at and is never marked paid:
| Reason | Meaning |
|---|---|
wrong-amount | A successful payment in the window within 2 percent of the amount, but not equal. |
late | The exact amount, after the expiry. |
duplicate | The exact amount again, after the one that paid. |
ambiguous | The exact amount, but another open request has the same payee and amount. |
unverified | The exact amount, but the node could not confirm it, or it was a contract call, not a transfer. |
A transfer that failed on the chain moved no money and is ignored.
Where the data comes from:
- Plain transfers are found through Quaiscan. QuaiRelay sends are found through the node's
Relayed(from, to, amount)events, because Quaiscan lists no internal transfers on Quai. - Quaiscan only discovers. Each candidate is read again from the node, and where the two disagree the node wins.
- A source that fails is reported as a gap. It is never read as "no payment". A latest block that cannot be read can
never reach
paid. - A Quaiscan that is more than 60 blocks behind the node, or cannot say, makes the answer incomplete, so "Expired" is not final.
Confirmations. The default is 12, a placeholder until finality on Quai has been measured on mainnet. The library takes 1 to 1000. Each tool lets the person or the shop set it (see each tool for its range).
Honest limits
- Beta, not independently audited.
- Plain transfers are discovered through Quaiscan. When it is down, the page says what is missing and does not report an empty list as a fact.
- Dollar prices for payments need at least two sources that agree. Dollar values on statements come from one source and are not a quote.
- Wrong, late and duplicate payments are listed for a person to handle. Any refund is done by the payee. HPAY never moves money back.