80 lines
8.5 KiB
Markdown
80 lines
8.5 KiB
Markdown
# AERE Payments: the risk step and the receipt
|
|
|
|
The two pieces that were missing from the payment chain for people and companies (identity -> authorization -> risk -> payment ->
|
|
proof -> settlement -> receipt), built as pure modules on what already exists: the AIP-23 `settlement` envelope (`proof-kinds/`),
|
|
the notary call of the house (`agents/agent-ancora.mjs`) and the AIP-23 reference verifier. Prices are in USD; the asset is only the
|
|
settlement.
|
|
|
|
**Risk (`risc.mjs`).** A declared policy (`definePolicy`: a per-payment limit and a sliding-window limit in USD per payer, the assets
|
|
allowed, the counterparties refused with the source of each entry) and the inputs of one payment (payer, payee, asset and amount, the
|
|
USD rate DECLARED as an input with its source and time, the payer's history as this issuer recorded it, the time) give one verdict
|
|
(`judeca`): ALLOW or REFUSE, the reasons, what was measured, and what was NOT. The verdict's hash covers the policy's hash and the
|
|
inputs' hash, so anyone with the same policy and inputs reproduces it; a policy with a field the engine does not know is refused, so
|
|
no one can be shown a rule that is not applied. The signals the module does not have are named in every verdict, never simulated:
|
|
sanctions screening (no source), identity tier, device or location, chargeback history, and the payer's payments outside this
|
|
issuer's own receipts. Without a declared rate the USD value is unmeasured and the policy refuses; it is never guessed.
|
|
|
|
**Receipt (`chitanta.mjs`).** An instruction (payer, payee, the amount in the asset and in USD at the declared rate, the transaction
|
|
and its block, the risk verdict with its policy under hash, the unmeasured signals) and the AIP-23 `settlement` envelope that commits
|
|
to it by hash. The envelope is what the issuer notarizes on AereNotary: the receipt's time is then the chain's, read back under a
|
|
post-quantum certified anchor by the reference verifier. `verificaChitanta` checks that the envelope commits to the instruction, that
|
|
the USD amount is exactly amount x rate, that the policy shown hashes to the one under the verdict, that the verdict reproduces from
|
|
the inputs and was ALLOW, and, with the chain facts, that the transaction is this payment (from, to, value, block, status).
|
|
|
|
```
|
|
node proba-risc.mjs # 23 rows, no chain: the verdicts under a policy, a receipt verified, nine forged ones refused by name,
|
|
# a malformed payment refused by name (a verdict, not an exception)
|
|
node proba-risc.mjs --control-negativ # 10 plantings on copies of risc.mjs and chitanta.mjs; each forged fixture must then be accepted
|
|
AERE_TESTNET_KEY_FILE=<key> AERE_ETHERS_DIR=<dir with node_modules/ethers> AERE_VERIFY_PROOF=<verify-proof.mjs> node proba-plata-28001.mjs
|
|
# the flow on testnet 28001; AERE_VERIFY_PROOF names the reference verifier (verify-proof.mjs as served at aere.network/tools, with verify-anchor.mjs, mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json beside it and `npm install` done there),
|
|
# and without a usable one the flow stops NEMASURAT before it reads or sends anything
|
|
node proba-plata-fara-verificator.mjs # 4 rows, no chain: that refusal, before any request, against a local stub that counts them
|
|
node verifica-chitanta.mjs dovezi/chitanta-28001-<date>.json --rpc https://testnet-rpc.aere.network --verify-proof <verify-proof.mjs>
|
|
node proba-verifica-chitanta.mjs # 20 rows, no chain: the public reader against a local RPC stub and a stub of the reference
|
|
# verifier, each exit code and verdict word as POST /v1/payments/verify gives them
|
|
```
|
|
|
|
Measured on 2026-10-02 on testnet 28001 (9/9): a person's payment of 0.4 tAERE at a declared 0.5 USD/tAERE = 0.20 USD allowed, paid
|
|
by a plain transfer the payer signed, its receipt issued, notarized by the payee and read back with finality PASSED under a
|
|
post-quantum certified anchor; a payment of 1.50 USD refused above the per-payment limit with no transaction sent; the fourth payment
|
|
in the window refused above the count; the receipt forged (amount changed) INVALID; a looser policy shown under the receipt's policy
|
|
hash INVALID. The public reader (`verifica-chitanta.mjs`) needs only Node.js, and reads the chain by plain JSON-RPC; its verdict `VALID`
|
|
also needs the reference verifier, verify-proof.mjs as served at aere.network/tools, with verify-anchor.mjs, mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json beside it and `npm install` done there (corrected 2026-10-27: this said the reader needs only Node.js, and
|
|
`verify-proof.mjs` alone stops at its first import). Its file is the
|
|
receipt with the policy and the inputs the issuer judged (the `receiptFile` of the Payments API's receipt route), and its verdicts are
|
|
those of POST /v1/payments/verify: exit 0 `VALID` only when the transaction is compared on chain and the reference verifier finds the
|
|
notarization under a post-quantum certified anchor (finality `PASSED`), 1 `INVALID` with the reasons, 2 `PARTIAL` when something was
|
|
not measured (no `--rpc`, no chain answer, an RPC of another chain, no `--verify-proof`, a reference verifier that ran and gave no
|
|
verdict, the policy or the inputs missing, finality `PENDING` or `UNMEASURED`). Corrected 2026-10-27: until this date it wrote `VALID`
|
|
and exited 0 without `--verify-proof` and while the notarization was pending, and it called a file without the policy and the inputs
|
|
`INVALID`. Corrected again the same day: an RPC of another chain was handed on to the reference verifier, which answered `FAILED`, so
|
|
an authentic receipt came out `INVALID` (the envelope is now judged offline and the verdict is `PARTIAL`); a verifier that did not
|
|
start was reported as "not run" (it is now "ran, gave no JSON", with its error line); a file that is not a receipt ended in an
|
|
exception with the exit code of `INVALID` (it is now `INVALID` by name).
|
|
|
|
Every refusal names its rule, in the module's working language: `rules` on a risk verdict (`forma`, `activ`, `contrapartida`,
|
|
`curs`, `limita`, `fereastra`, that is form, asset, counterparty, rate, limit and window; outside the hashed body, so the hash of a
|
|
verdict already issued is unchanged), `reguli` on a receipt verdict (`legatura`, `suma`, `politica`, `risc`, `lant`, that is binding,
|
|
amount, policy, risk and chain). The Payments API of Aere Cloud returns the risk rules under their English names since 2026-10-27,
|
|
translated at its boundary. (Corrected 2026-10-27: this paragraph gave the English names as the ones the module returns.) The rate is a
|
|
decimal string (a JSON number is refused, not rounded), and a history entry the engine cannot count is refused, not left out of the
|
|
window. The conformance suite (`conformance/`) holds a vector for each of these rules and runs any implementation against them.
|
|
|
|
## What it is not, and what is not measured
|
|
|
|
- **Not KYC, not AML, not sanctions screening.** The policy's refused counterparties are addresses this house measured as sinks on
|
|
28001 (contracts with no path to withdraw what is sent to them, with the deployment record as source) and the zero address; no
|
|
sanctions list is consulted, and the verdict says so in `unmeasured`. An identity or compliance record (`identity/`) can be named in
|
|
the inputs; it is not judged here.
|
|
- **The policy is the issuer's word.** One issuer, one policy, one key: the receipt proves that THIS policy, as hashed, gave THIS verdict
|
|
on THESE inputs; it does not prove the policy is a good one, nor that the issuer's history of the payer is complete (it is only the
|
|
issuer's own receipts).
|
|
- **The rate is declared, not observed.** The USD amount is exact at the rate the issuer declares as an input (source and time on the
|
|
receipt); a wrong declared rate gives a wrong USD figure, honestly attributed.
|
|
- **Native transfers only in the measured flow.** An ERC-20 payment (the testnet USD token through x402) is supported by the policy's
|
|
asset list but not measured; the receipt's chain check compares `value` only for the native asset.
|
|
- **This module is not a service.** The issuer runs it in its own process, or calls the hosted Payments API of Aere Cloud, which runs
|
|
these modules (`POST /v1/payments/risk`, `/receipt`, `/verify`, with a key); there is no queue and no callback, and nothing here
|
|
moves money. (Corrected 2026-10-27: this line said "Not a payment API", while the hosted API exists.)
|
|
- **Chain 2800: nothing** ran there; test keys only, the payee's derived from the payer's and never printed.
|