1.8.0: the Payments API in all five clients (paymentsRisk, paymentsReceipt, paymentsVerify: POST /v1/payments/risk, /receipt, /verify; JS, Python, Go, Java, C#), with their tests and negative controls

This commit is contained in:
Aere Network 2026-10-11 20:20:40 +03:00
parent 0fe1828f5b
commit 1096c078cd
22 changed files with 250 additions and 28 deletions

View File

@ -47,7 +47,7 @@ const { valid } = await aere.pqVerify({
const receipt = await aere.notarize('0x' + sha256Hex);
const proof = await aere.proofOf('0x' + sha256Hex); // { notarized, firstSeenAt, … }
// 1.2.0 (2026-09-17): the proof with its post-quantum finality: first appearance from the contract log,
// 1.2.0 (2026-09-17): the proof under a post-quantum certificate: first appearance from the contract log,
// the anchor that covers it, a verdict, and how to verify every claim without Aere Cloud
const full = await aere.proof('0x' + sha256Hex); // { block, txHash, finality: 'post-quantum', pqAnchor: {…}, verify: {…} }
@ -76,6 +76,12 @@ const r = await aere.verify(envelope, { chain: 2800 }); // { verdict
const id = await aere.identityVerify({ presentation, audience: 'https://your.app/verifier', nonce, trustedIssuers: [issuerId], statusLists: [list] });
const ok = await aere.complianceCheck({ presentation, policy, audience: 'https://your.app/verifier', nonce, statusLists: [list], record: true });
// payments (1.8.0): the risk step and the receipt for a person or a company, judged by the published module `payments/`. Prices are in
// USD at the rate YOU declare in the inputs (it is not a market price); the asset is only the settlement. A REFUSE is a verdict, not an error.
const risk = await aere.paymentsRisk({ policy, inputs }); // ALLOW / REFUSE, reasons, unmeasured, verdictHash
const { receipt } = await aere.paymentsReceipt({ policy, inputs, txHash }); // after the payer paid: instruction + notarized AIP-23 envelope
const check = await aere.paymentsVerify({ receipt, policy, inputs }); // VALID / INVALID / PARTIAL
// transfer history for an address (token from genesis, native from launch)
const tx = await aere.transfers('0xYourAddress', 50);

View File

@ -1,4 +1,4 @@
// Probele clientului .NET (Aere.Cloud 1.7.0), fara framework de probe: fiecare proba tipareste "--- PASS: Nume" sau "--- FAIL: Nume: motiv",
// Probele clientului .NET (Aere.Cloud 1.8.0), fara framework de probe: fiecare proba tipareste "--- PASS: Nume" sau "--- FAIL: Nume: motiv",
// iar codul de iesire e numarul de probe picate (0 = toate verzi). Aceleasi garzi ca la clientul Go: cheia obligatorie, cheia trimisa NUMAI
// unde trebuie, redirectarile NEurmate (dovedit pe un server local real, fiindca numai handlerul implicit al SDK-ului o poate arata),
// exportul jurnalului verificat pe octetii primiti, verificarea unei copii pastrate (tinuta / stearsa / eroare), webhook-ul in timp constant.
@ -49,7 +49,7 @@ await Proba("ReadinessCheiaNumaiLaAttest", async () =>
await c.PqFinalityAsync(); Cer(!ciot.Ultima.Antete.ContainsKey("x-api-key"), "finalitatea AIP-21 fara cheie");
await c.HealthAsync(); Cer(!ciot.Ultima.Antete.ContainsKey("x-api-key"), "health fara cheie");
await c.AccountAsync(); Cer(ciot.Ultima.Antete.GetValueOrDefault("x-api-key") == Cheie, "account cu cheie");
Cer(ciot.Ultima.Antete.GetValueOrDefault("user-agent", "").StartsWith("aere-cloud-csharp/1.7.0"), "user-agent-ul clientului");
Cer(ciot.Ultima.Antete.GetValueOrDefault("user-agent", "").StartsWith("aere-cloud-csharp/1.8.0"), "user-agent-ul clientului");
});
await Proba("PerimetruFinalitateDovada", async () =>
@ -88,6 +88,19 @@ await Proba("IdentityCompliance", async () =>
Cer(ciot.Ultima.Url.EndsWith("/v1/compliance/check") && ciot.Ultima.Antete.ContainsKey("x-api-key") && ciot.Ultima.Corp == "{\"record\":true}", "POST /compliance/check: " + ciot.Ultima.Url + " " + ciot.Ultima.Corp);
});
await Proba("Payments", async () =>
{
var (c, ciot) = Client(_ => Json(200, "{\"verdict\":\"REFUSE\"}"));
var r = await c.PaymentsRiskAsync(new Dictionary<string, object> { ["policy"] = "p", ["inputs"] = "i" });
Cer(ciot.Ultima.Metoda == "POST" && ciot.Ultima.Url.EndsWith("/v1/payments/risk") && ciot.Ultima.Antete.ContainsKey("x-api-key"), "POST /payments/risk cu cheie: " + ciot.Ultima.Url);
Cer(ciot.Ultima.Corp == "{\"policy\":\"p\",\"inputs\":\"i\"}", "cererea trimisa ca atare: " + ciot.Ultima.Corp);
Cer(r.GetProperty("verdict").GetString() == "REFUSE", "un REFUSE e un verdict (200), nu o eroare");
await c.PaymentsReceiptAsync(new Dictionary<string, object> { ["txHash"] = "0x11" });
Cer(ciot.Ultima.Url.EndsWith("/v1/payments/receipt") && ciot.Ultima.Antete.ContainsKey("x-api-key") && ciot.Ultima.Corp == "{\"txHash\":\"0x11\"}", "POST /payments/receipt: " + ciot.Ultima.Url + " " + ciot.Ultima.Corp);
await c.PaymentsVerifyAsync(new Dictionary<string, object> { ["receipt"] = "r" });
Cer(ciot.Ultima.Url.EndsWith("/v1/payments/verify") && ciot.Ultima.Antete.ContainsKey("x-api-key") && ciot.Ultima.Corp == "{\"receipt\":\"r\"}", "POST /payments/verify: " + ciot.Ultima.Url + " " + ciot.Ultima.Corp);
});
await Proba("ExportulZilei", async () =>
{
var corp = Encoding.UTF8.GetBytes("{\"a\":1}\n{\"b\":2}\n");

View File

@ -10,7 +10,7 @@
<RootNamespace>Aere.Cloud</RootNamespace>
<AssemblyName>Aere.Cloud</AssemblyName>
<PackageId>Aere.Cloud</PackageId>
<Version>1.7.0</Version>
<Version>1.8.0</Version>
<Authors>Aere Network</Authors>
<Description>Official .NET client for the Aere Cloud API (Aere Network, chain 2800): RPC, post-quantum verification, the Trust API (AERE Proof Protocol envelopes), chain data, notarization and proofs, quantum readiness, audit log, webhooks and gas sponsorship.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -1,6 +1,6 @@
// Aere.Cloud - the official .NET client for the Aere Cloud API (Aere Network, chain 2800).
//
// Same routes and semantics as @aere/cloud (JS), aere-cloud (Python), aere-cloud (Java) and the Go client, 1.7.0. No dependency beyond
// Same routes and semantics as @aere/cloud (JS), aere-cloud (Python), aere-cloud (Java) and the Go client, 1.8.0. No dependency beyond
// the .NET base library. JSON routes return the server's JSON as a JsonElement, so the integrator binds it to whatever types they
// already use; the two routes that return FILES (the audit export and the check of a kept export) return typed results with the
// digest computed locally from the bytes received. Non-2xx answers are thrown as AereCloudException (HTTP status, server body, path).
@ -52,7 +52,7 @@ public sealed record KeptCheck(string Day, string State, string Digest, string L
public sealed class AereCloudClient : IDisposable
{
/// <summary>Version of this client, sent in the User-Agent (the edge refuses generic library agents on every route).</summary>
public const string Version = "1.7.0";
public const string Version = "1.8.0";
/// <summary>The production gateway.</summary>
public const string DefaultBaseUrl = "https://cloud.aere.network/v1";
@ -145,6 +145,18 @@ public sealed class AereCloudClient : IDisposable
/// with the reasons and, with record, an AIP-23 record without personal data.</summary>
public Task<JsonElement> ComplianceCheckAsync(object request, CancellationToken ct = default) => JsonAsync(HttpMethod.Post, "/compliance/check", request, true, ct);
/// <summary>Payments, the risk step (1.8.0): {policy, inputs} -> ALLOW / REFUSE with the reasons, what was measured and what was NOT (named in
/// "unmeasured", never simulated) and the hashes from which anyone reproduces the verdict; judged by the published module payments/risc.mjs.
/// A refusal is a verdict (HTTP 200), not an error.</summary>
public Task<JsonElement> PaymentsRiskAsync(object request, CancellationToken ct = default) => JsonAsync(HttpMethod.Post, "/payments/risk", request, true, ct);
/// <summary>Payments, the receipt (1.8.0): {policy, inputs, txHash, purpose?} for a payment ALREADY on the chain the gateway serves -> the receipt
/// (instruction + AIP-23 settlement envelope), notarized. AereCloudException 409 (risk_refused, tx_mismatch, chain_mismatch), 404, 502.</summary>
public Task<JsonElement> PaymentsReceiptAsync(object request, CancellationToken ct = default) => JsonAsync(HttpMethod.Post, "/payments/receipt", request, true, ct);
/// <summary>Payments, verify a receipt (1.8.0): {receipt:{instruction, envelope}, policy, inputs} -> VALID / INVALID / PARTIAL.</summary>
public Task<JsonElement> PaymentsVerifyAsync(object request, CancellationToken ct = default) => JsonAsync(HttpMethod.Post, "/payments/verify", request, true, ct);
/// <summary>GET /data/head.</summary>
public Task<JsonElement> DataHeadAsync(CancellationToken ct = default) => JsonAsync(HttpMethod.Get, "/data/head", null, true, ct);
/// <summary>GET /data/validators.</summary>
@ -153,7 +165,7 @@ public sealed class AereCloudClient : IDisposable
public Task<JsonElement> AnchorsAsync(int limit = 10, CancellationToken ct = default) => JsonAsync(HttpMethod.Get, "/data/anchors?limit=" + limit, null, true, ct);
/// <summary>GET /data/anchors/{height}.</summary>
public Task<JsonElement> AnchorAsync(ulong height, CancellationToken ct = default) => JsonAsync(HttpMethod.Get, "/data/anchors/" + height, null, true, ct);
/// <summary>The post-quantum finality point (finalized/safe = the parent of the last certified anchor).</summary>
/// <summary>The post-quantum certified point (finalized/safe = the parent of the last certified anchor; evidence that its signers sealed it, not by itself that it was decided, AIP-22 erratum 2026-10-01).</summary>
public Task<JsonElement> FinalityAsync(CancellationToken ct = default) => JsonAsync(HttpMethod.Get, "/data/finality", null, true, ct);
/// <summary>GET /data/transfers?address=&amp;limit=.</summary>
public Task<JsonElement> TransfersAsync(string address, int limit = 50, CancellationToken ct = default) =>
@ -162,7 +174,7 @@ public sealed class AereCloudClient : IDisposable
public Task<JsonElement> NotarizeAsync(string hash, CancellationToken ct = default) => JsonAsync(HttpMethod.Post, "/notarize", new Dictionary<string, string> { ["hash"] = hash }, true, ct);
/// <summary>GET /notarize/{hash}.</summary>
public Task<JsonElement> ProofOfAsync(string hash, CancellationToken ct = default) => JsonAsync(HttpMethod.Get, "/notarize/" + Esc(hash), null, true, ct);
/// <summary>The proof with its post-quantum finality (first appearance, covering anchor, verdict, verification steps).</summary>
/// <summary>The proof under a post-quantum certificate (first appearance, covering anchor, verdict, verification steps).</summary>
public Task<JsonElement> ProofAsync(string hash, CancellationToken ct = default) => JsonAsync(HttpMethod.Get, "/proof/" + Esc(hash), null, true, ct);
// ---- quantum readiness ----
@ -188,7 +200,7 @@ public sealed class AereCloudClient : IDisposable
return JsonAsync(HttpMethod.Post, "/pq/readiness/perimeter", b, true, ct);
}
/// <summary>AIP-21 per-block post-quantum finality. client: "besu", "nethermind" or "both" (with `agreement`). No key.</summary>
/// <summary>AIP-21 per-block post-quantum seal record (participation, not by itself the decision: AIP-21 erratum 2026-10-01). client: "besu", "nethermind" or "both" (with `agreement`). No key.</summary>
public Task<JsonElement> PqFinalityAsync(string block = "latest", string network = "testnet", string client = "besu", CancellationToken ct = default) =>
JsonAsync(HttpMethod.Get, "/pq/finality/" + Esc(block) + "?network=" + Esc(network) + "&client=" + Esc(client), null, false, ct);

View File

@ -2,8 +2,8 @@
The official .NET client for the [Aere Cloud API](https://aere.network/cloud.html) on Aere Network (chain 2800).
**No dependencies** beyond the .NET base library (`System.Net.Http`, `System.Text.Json`, `System.Security.Cryptography`).
Targets .NET 8 (LTS) and runs on later versions. Same routes and semantics as the JS, Python, Go and Java clients 1.7.0,
including `VerifyAsync`, the Trust API, and `IdentityVerifyAsync` / `ComplianceCheckAsync`.
Targets .NET 8 (LTS) and runs on later versions. Same routes and semantics as the JS, Python, Go and Java clients 1.8.0,
including `VerifyAsync`, the Trust API, and `IdentityVerifyAsync` / `ComplianceCheckAsync`, and the payments routes `PaymentsRiskAsync` / `PaymentsReceiptAsync` / `PaymentsVerifyAsync` (1.8.0: the risk step and the receipt, judged by the published module `payments/`; USD at the rate you declare, a REFUSE is a verdict).
JSON routes return the server's JSON as a `JsonElement`, so you bind it to the types you already use. The two routes
that return files (the audit export and the check of a kept export) return typed results, with the SHA-256 computed

View File

@ -26,6 +26,8 @@ P = [
("lantul cerut scapat din Trust API", '(chain > 0 ? "?chain=" + chain : "")', '(chain > 0 ? "" : "")', "TrustApiVerify"),
("conformitatea pe ruta identitatii (1.7.0)", 'JsonAsync(HttpMethod.Post, "/compliance/check", request, true, ct);', 'JsonAsync(HttpMethod.Post, "/identity/verify", request, true, ct);', "IdentityCompliance"),
("identitatea fara cheie (1.7.0)", 'JsonAsync(HttpMethod.Post, "/identity/verify", request, true, ct);', 'JsonAsync(HttpMethod.Post, "/identity/verify", request, false, ct);', "IdentityCompliance"),
("chitanta pe ruta riscului (1.8.0)", 'JsonAsync(HttpMethod.Post, "/payments/receipt", request, true, ct);', 'JsonAsync(HttpMethod.Post, "/payments/risk", request, true, ct);', "Payments"),
("verificarea platii fara cheie (1.8.0)", 'JsonAsync(HttpMethod.Post, "/payments/verify", request, true, ct);', 'JsonAsync(HttpMethod.Post, "/payments/verify", request, false, ct);', "Payments"),
("un rezultat RPC fara forma citit ca zero", ' throw new AereCloudException(200, e.GetRawText(), "/rpc");', ' return 0;', "EroriSiBlockNumber"),
]
if sys.argv[1] == "n": print(len(P)); sys.exit(0)

View File

@ -32,6 +32,9 @@ verdict, err := c.Verify(ctx, json.RawMessage(envelopeBytes), 2800)
// identity and compliance (1.7.0): the presentation, keys, lists and policy travel as their JSON bytes (numbers above 2^53 stay exact)
id, err := c.IdentityVerify(ctx, aerecloud.IdentityVerifyRequest{Presentation: presBytes, Audience: "https://your.app/verifier", Nonce: nonce,
// payments (1.8.0): the policy and the inputs travel as their JSON bytes (an amount above 2^53 stays exact); a REFUSE is a verdict
risk, err := c.PaymentsRisk(ctx, aerecloud.PaymentsRiskRequest{Policy: policyBytes, Inputs: inputsBytes})
rec, err := c.PaymentsReceipt(ctx, aerecloud.PaymentsReceiptRequest{Policy: policyBytes, Inputs: inputsBytes, TxHash: txHash})
TrustedIssuers: []json.RawMessage{json.RawMessage(`"aere-id:..."`)}, StatusLists: []json.RawMessage{listBytes}})
ok, err := c.ComplianceCheck(ctx, aerecloud.ComplianceCheckRequest{Presentation: presBytes, Policy: policyBytes, Audience: "https://your.app/verifier", Nonce: nonce, Record: true})

View File

@ -30,7 +30,7 @@ import (
)
// Version of this client, sent in the User-Agent (the edge refuses generic library agents on every route).
const Version = "1.7.0"
const Version = "1.8.0"
// DefaultBaseURL is the production gateway.
const DefaultBaseURL = "https://cloud.aere.network/v1"
@ -210,6 +210,48 @@ func (c *Client) ComplianceCheck(ctx context.Context, req ComplianceCheckRequest
return c.jsonReq(ctx, "POST", "/compliance/check", req, true)
}
// PaymentsRiskRequest is the body of POST /v1/payments/risk (1.8.0). The policy and the inputs are kept as their JSON bytes: the
// verdict's hashes cover their canonical text, and a Go map or float64 would change an amount above 2^53.
type PaymentsRiskRequest struct {
Policy json.RawMessage `json:"policy"`
Inputs json.RawMessage `json:"inputs"`
}
// PaymentsReceiptRequest is the body of POST /v1/payments/receipt (1.8.0).
type PaymentsReceiptRequest struct {
Policy json.RawMessage `json:"policy"`
Inputs json.RawMessage `json:"inputs"`
TxHash string `json:"txHash"`
Purpose string `json:"purpose,omitempty"`
}
// PaymentsVerifyRequest is the body of POST /v1/payments/verify (1.8.0): the receipt as PaymentsReceipt returned it.
type PaymentsVerifyRequest struct {
Receipt json.RawMessage `json:"receipt"`
Policy json.RawMessage `json:"policy"`
Inputs json.RawMessage `json:"inputs"`
}
// PaymentsRisk (1.8.0): one payment by a person or a company judged under YOUR declared policy by the published module
// (payments/risc.mjs) -> ALLOW / REFUSE with the reasons, what was measured and what was NOT (named in "unmeasured", never simulated),
// and the hashes from which anyone reproduces the verdict. A refusal is a verdict (HTTP 200), not an error.
func (c *Client) PaymentsRisk(ctx context.Context, req PaymentsRiskRequest) (json.RawMessage, error) {
return c.jsonReq(ctx, "POST", "/payments/risk", req, true)
}
// PaymentsReceipt (1.8.0): the receipt of a payment ALREADY on the chain the gateway serves; the verdict is reproduced and must be
// ALLOW, the transaction must be the payment judged, and the receipt (instruction + AIP-23 settlement envelope) comes back notarized.
// An *Error with Status 409 (risk_refused, tx_mismatch, chain_mismatch), 404 (tx_not_found) or 502 (notarization_failed) otherwise.
func (c *Client) PaymentsReceipt(ctx context.Context, req PaymentsReceiptRequest) (json.RawMessage, error) {
return c.jsonReq(ctx, "POST", "/payments/receipt", req, true)
}
// PaymentsVerify (1.8.0): the published module's rules against the policy, the inputs and the chain, and the envelope judged by the
// reference verifier -> VALID / INVALID / PARTIAL (PARTIAL: the notarization is not under a certified anchor yet).
func (c *Client) PaymentsVerify(ctx context.Context, req PaymentsVerifyRequest) (json.RawMessage, error) {
return c.jsonReq(ctx, "POST", "/payments/verify", req, true)
}
func (c *Client) DataHead(ctx context.Context) (json.RawMessage, error) {
return c.jsonReq(ctx, "GET", "/data/head", nil, true)
}
@ -223,7 +265,7 @@ func (c *Client) Anchor(ctx context.Context, height uint64) (json.RawMessage, er
return c.jsonReq(ctx, "GET", "/data/anchors/"+strconv.FormatUint(height, 10), nil, true)
}
// Finality returns the post-quantum finality point (finalized/safe = the parent of the last certified anchor), 2026-09-26.
// Finality returns the post-quantum certified point (finalized/safe = the parent of the last certified anchor; evidence that its signers sealed it, not by itself that it was decided, AIP-22 erratum 2026-10-01), 2026-09-26.
func (c *Client) Finality(ctx context.Context) (json.RawMessage, error) {
return c.jsonReq(ctx, "GET", "/data/finality", nil, true)
}
@ -237,7 +279,7 @@ func (c *Client) ProofOf(ctx context.Context, hash string) (json.RawMessage, err
return c.jsonReq(ctx, "GET", "/notarize/"+esc(hash), nil, true)
}
// Proof: the proof with its post-quantum finality (first appearance, covering anchor, verdict, verification steps).
// Proof: the proof under a post-quantum certificate (first appearance, covering anchor, verdict, verification steps).
func (c *Client) Proof(ctx context.Context, hash string) (json.RawMessage, error) {
return c.jsonReq(ctx, "GET", "/proof/"+esc(hash), nil, true)
}
@ -281,7 +323,7 @@ func (c *Client) ReadinessPerimeter(ctx context.Context, domain string, o Perime
return c.jsonReq(ctx, "POST", "/pq/readiness/perimeter", b, true)
}
// PQFinality: AIP-21 per-block post-quantum finality. client: "besu", "nethermind" or "both" (with `agreement`). No key.
// PQFinality: AIP-21 per-block post-quantum seal record (participation, not by itself the decision: AIP-21 erratum 2026-10-01). client: "besu", "nethermind" or "both" (with `agreement`). No key.
func (c *Client) PQFinality(ctx context.Context, block, network, client string) (json.RawMessage, error) {
return c.jsonReq(ctx, "GET", "/pq/finality/"+esc(block)+"?network="+url.QueryEscape(network)+"&client="+url.QueryEscape(client), nil, false)
}

View File

@ -256,3 +256,30 @@ func TestVerifyWebhook(t *testing.T) {
t.Fatal("o semnatura gresita sau un corp atins a trecut")
}
}
// 1.8.0: cererile de plati pleaca pe rutele lor, cu cheie, cu politica si intrarile NESCHIMBATE (o suma peste 2^53 ramane exacta)
func TestPaymentsPastreazaOctetii(t *testing.T) {
s, c := porneste(t)
pol := `{"v":1,"kind":"aere-payment-risk-policy","id":"p"}`
in := `{"chainId":28001,"amount":12345678901234567891,"asset":"native"}`
if _, err := c.PaymentsRisk(context.Background(), PaymentsRiskRequest{Policy: json.RawMessage(pol), Inputs: json.RawMessage(in)}); err != nil {
t.Fatal(err)
}
v := s.ultima()
if v.Method != "POST" || v.Path != "/v1/payments/risk" || v.Key == "" || !strings.Contains(v.Body, `"inputs":`+in) || !strings.Contains(v.Body, `"policy":`+pol) {
t.Fatalf("cererea de risc: %+v", v)
}
if _, err := c.PaymentsReceipt(context.Background(), PaymentsReceiptRequest{Policy: json.RawMessage(pol), Inputs: json.RawMessage(in), TxHash: "0x11", Purpose: "order 7"}); err != nil {
t.Fatal(err)
}
if v = s.ultima(); v.Path != "/v1/payments/receipt" || v.Key == "" || !strings.Contains(v.Body, `"txHash":"0x11"`) || !strings.Contains(v.Body, `"purpose":"order 7"`) || !strings.Contains(v.Body, `"inputs":`+in) {
t.Fatalf("cererea de chitanta: %+v", v)
}
rec := `{"instruction":{"amount":"12345678901234567891"},"envelope":{"v":1}}`
if _, err := c.PaymentsVerify(context.Background(), PaymentsVerifyRequest{Receipt: json.RawMessage(rec), Policy: json.RawMessage(pol), Inputs: json.RawMessage(in)}); err != nil {
t.Fatal(err)
}
if v = s.ultima(); v.Path != "/v1/payments/verify" || v.Key == "" || !strings.Contains(v.Body, `"receipt":`+rec) {
t.Fatalf("cererea de verificare: %+v", v)
}
}

View File

@ -28,6 +28,10 @@ const PLANTARI = [
['IdentityVerify trece prezentarea printr-un map', 'return c.jsonReq(ctx, "POST", "/identity/verify", req, true)',
'var m map[string]any\n\t_ = json.Unmarshal(req.Presentation, &m)\n\tb, _ := json.Marshal(m)\n\treq.Presentation = b\n\treturn c.jsonReq(ctx, "POST", "/identity/verify", req, true)', 'TestIdentityVerifyPastreazaOctetii'],
['ComplianceCheck pe ruta de identitate', 'return c.jsonReq(ctx, "POST", "/compliance/check", req, true)', 'return c.jsonReq(ctx, "POST", "/identity/verify", req, true)', 'TestIdentityVerifyPastreazaOctetii'],
// 1.8.0: intrarile de risc trecute printr-un map (suma peste 2^53 devine float64) si chitanta trimisa pe ruta riscului
['PaymentsRisk trece intrarile printr-un map', 'return c.jsonReq(ctx, "POST", "/payments/risk", req, true)',
'var m map[string]any\n\t_ = json.Unmarshal(req.Inputs, &m)\n\tb, _ := json.Marshal(m)\n\treq.Inputs = b\n\treturn c.jsonReq(ctx, "POST", "/payments/risk", req, true)', 'TestPaymentsPastreazaOctetii'],
['PaymentsReceipt pe ruta riscului', 'return c.jsonReq(ctx, "POST", "/payments/receipt", req, true)', 'return c.jsonReq(ctx, "POST", "/payments/risk", req, true)', 'TestPaymentsPastreazaOctetii'],
];
function ruleaza(src) {

View File

@ -92,12 +92,40 @@ export class AereCloud {
*/
complianceCheck(request) { return this._req('POST', '/compliance/check', { body: request }); }
/**
* payments, the risk step (1.8.0): one payment by a person or a company judged under YOUR declared policy by the published module
* (payments/risc.mjs) -> ALLOW / REFUSE with the reasons, what was measured and what was NOT (named in `unmeasured`, never simulated:
* no sanctions list, no identity tier), and the hashes (policyHash, inputsHash, verdictHash) from which anyone reproduces the verdict.
* A refusal is a verdict (HTTP 200, `verdict: 'REFUSE'`), not an error. Needs a gateway that serves POST /v1/payments/risk.
* @param {{policy:object, inputs:object}} request inputs: { chainId, payer, payee, asset, amount, rate:{usdPerUnit, source, at}, history, now }
* @returns {Promise<{verdict:'ALLOW'|'REFUSE', reasons:string[], unmeasured:string[], measured:object, policyHash:string, inputsHash:string, verdictHash:string, verifier:object, reproduce:string}>}
*/
paymentsRisk(request) { return this._req('POST', '/payments/risk', { body: request }); }
/**
* payments, the receipt (1.8.0): for a payment ALREADY on the chain the gateway serves. The verdict is reproduced and must be ALLOW,
* the transaction must be the payment judged, and the receipt (an instruction and its AIP-23 settlement envelope) comes back notarized.
* Throws AereCloudError 409 (risk_refused, tx_mismatch, chain_mismatch), 404 (tx_not_found) or 502 (notarization_failed, with the
* receipt built but without a time). The amount in USD is exact at the rate YOU declare in the inputs; it is not a market price.
* @param {{policy:object, inputs:object, txHash:string, purpose?:string}} request
* @returns {Promise<{receipt:{instruction:object, envelope:object}, verdict:object, verifier:object, reproduce:string}>}
*/
paymentsReceipt(request) { return this._req('POST', '/payments/receipt', { body: request }); }
/**
* payments, verify a receipt (1.8.0): the rules of the published module against the policy, the inputs and the chain, and the
* envelope judged by the reference verifier -> VALID / INVALID / PARTIAL (PARTIAL: the notarization is not under a certified anchor yet).
* @param {{receipt:{instruction:object, envelope:object}, policy:object, inputs:object}} request
* @returns {Promise<{verdict:'VALID'|'INVALID'|'PARTIAL', reasons:string[], rules:object, envelope:object|null, amountUsd:string, verifier:object, reproduce:string}>}
*/
paymentsVerify(request) { return this._req('POST', '/payments/verify', { body: request }); }
// ---- chain data ----
dataHead() { return this._req('GET', '/data/head'); }
validators() { return this._req('GET', '/data/validators'); }
anchors(limit = 10) { return this._req('GET', `/data/anchors?limit=${encodeURIComponent(limit)}`); }
anchor(height) { return this._req('GET', `/data/anchors/${encodeURIComponent(height)}`); }
// 2026-09-26: the post-quantum finality point (finalized/safe = the parent of the last certified anchor), the tag nodes do not serve yet
// 2026-09-26: the post-quantum certified point (finalized/safe = the parent of the last certified anchor; evidence that its signers sealed it, not by itself that it was decided, AIP-22 erratum 2026-10-01); the tags nodes did not serve then
finality() { return this._req('GET', '/data/finality'); }
transfers(address, limit = 50) {
return this._req('GET', `/data/transfers?address=${encodeURIComponent(address)}&limit=${encodeURIComponent(limit)}`);
@ -107,7 +135,7 @@ export class AereCloud {
/** Notarizeaza un digest de 32 de octeti (0x + 64 hex); noi platim gazul. */
notarize(hash) { return this._req('POST', '/notarize', { body: { hash } }); }
proofOf(hash) { return this._req('GET', `/notarize/${encodeURIComponent(hash)}`); }
// 2026-09-17: the proof with its post-quantum finality (first appearance, covering anchor, verdict, verification steps)
// 2026-09-17: the proof under a post-quantum certificate (first appearance, covering anchor, verdict, verification steps)
proof(hash) { return this._req('GET', `/proof/${encodeURIComponent(hash)}`); }
// quantum readiness scan of a domain's public TLS edge: free without a key; { attest: true } notarizes the report digest
@ -125,8 +153,8 @@ export class AereCloud {
return this._req('POST', '/pq/readiness/perimeter', { body });
}
// AIP-21 (2026-09-18): the per-block post-quantum finality record (seals heard by a validator, re-verified, quorum
// verdict). Free, no key. Testnet 28001 first; `network` is reserved for chain 2800 once the method reaches its validators.
// AIP-21 (2026-09-18): the per-block post-quantum seal record (seals heard by a validator, re-verified, quorum
// verdict: evidence of participation, not by itself of the decision, AIP-21 erratum 2026-10-01). Free, no key. Testnet 28001 first; `network` is reserved for chain 2800 once the method reaches its validators.
// `client`: 'besu' (default), 'nethermind', or 'both' (two independent implementations, with `agreement` computed by the gateway).
pqFinality(block = 'latest', network = 'testnet', client = 'besu') {
return this._req('GET', `/pq/finality/${encodeURIComponent(block)}?network=${encodeURIComponent(network)}&client=${encodeURIComponent(client)}`, { auth: false });

View File

@ -30,6 +30,8 @@ String verdict = aere.verify(envelopeJson, 2800);
// identity and compliance (1.7.0): the request as JSON text, sent as given; read "verdict" (PARTIAL: some rows not judged)
String id = aere.identityVerify("{\"presentation\":" + presentationJson + ",\"audience\":\"https://your.app/verifier\",\"nonce\":\"" + nonce + "\"}");
// payments (1.8.0): the risk step and the receipt, the request as JSON text; USD at the rate you declare; a REFUSE is a verdict
String risk = aere.paymentsRisk("{\"policy\":" + policyJson + ",\"inputs\":" + inputsJson + "}");
String ok = aere.complianceCheck(requestWithPolicyJson);
// verify a webhook delivery (constant-time HMAC) — never trust one without this:
@ -42,10 +44,10 @@ and `.path`.
## Since 1.5.0 (same surface as the JS and Python SDKs 1.5.0)
```java
aere.proof("0x" + sha256Hex); // a proof with its post-quantum finality
aere.proof("0x" + sha256Hex); // a proof under a post-quantum certificate
aere.readiness("example.com", false, false); // quantum-readiness scan, free, no key sent
aere.readinessPerimeter("example.com", new String[]{"api"}, null, false); // a domain's whole public perimeter
aere.pqFinality("latest", "testnet", "both"); // AIP-21 per-block PQ finality, both clients
aere.pqFinality("latest", "testnet", "both"); // AIP-21 per-block PQ seal record, both clients
AereCloud.AuditExport day = aere.auditExport("2026-09-24", "jsonl");
// day.bytes are the exact bytes of the day file; day.verified is true only if THEIR sha256 equals the stated digest

View File

@ -30,6 +30,9 @@ const PLANTARI = [
// 1.7.0: conformitatea trimisa pe ruta identitatii; identitatea fara cheie
['complianceCheck pe ruta identitatii', 'return req("POST", "/compliance/check", requestJson, true);', 'return req("POST", "/identity/verify", requestJson, true);', '1.7.0'],
['identityVerify fara cheie', 'return req("POST", "/identity/verify", requestJson, true);', 'return req("POST", "/identity/verify", requestJson, false);', '1.7.0'],
// 1.8.0: chitanta pe ruta riscului; verificarea fara cheie
['paymentsReceipt pe ruta riscului', 'return req("POST", "/payments/receipt", requestJson, true);', 'return req("POST", "/payments/risk", requestJson, true);', '1.8.0'],
['paymentsVerify fara cheie', 'return req("POST", "/payments/verify", requestJson, true);', 'return req("POST", "/payments/verify", requestJson, false);', '1.8.0'],
];
// 1.6.1: poarta Android = clasa compileaza cu `javac --release 8`. Un API de dupa Java 8 trece probele pe JDK 21 si trebuie prins
// numai de poarta; plantarea NU e o proba, e o compilare care TREBUIE sa cada.

View File

@ -33,7 +33,8 @@ public final class AereCloud {
// urmate (ca inainte: HttpClient nu le urma implicit); URLEncoder cu numele setului de caractere (varianta cu Charset e
// Java 10+). Aceeasi suprafata publica.
// 1.7.0 (2026-09-30): identityVerify() si complianceCheck() (POST /v1/identity/verify, /v1/compliance/check)
public static final String VERSIUNE = "1.7.0";
// 1.8.0 (2026-10-03): paymentsRisk(), paymentsReceipt(), paymentsVerify() (POST /v1/payments/risk, /receipt, /verify)
public static final String VERSIUNE = "1.8.0";
private static final String BAZA_IMPLICITA = "https://cloud.aere.network/v1";
private static final int TIMP_MS = 30000;
@ -168,13 +169,19 @@ public final class AereCloud {
public String identityVerify(String requestJson) { return req("POST", "/identity/verify", requestJson, true); }
/** compliance check (1.7.0): {presentation, policy, audience, nonce, statusLists?, revocations?, record?} -> COMPLIANT / NOT_COMPLIANT with the reasons and, with record, an AIP-23 record without personal data. */
public String complianceCheck(String requestJson) { return req("POST", "/compliance/check", requestJson, true); }
/** payments, the risk step (1.8.0): {policy, inputs} -> ALLOW / REFUSE with the reasons, what was measured and what was NOT (named in "unmeasured", never simulated), and the hashes from which anyone reproduces the verdict; judged by the published module payments/risc.mjs. A refusal is a verdict (HTTP 200), not an error. */
public String paymentsRisk(String requestJson) { return req("POST", "/payments/risk", requestJson, true); }
/** payments, the receipt (1.8.0): {policy, inputs, txHash, purpose?} for a payment ALREADY on the chain the gateway serves -> the receipt (instruction + AIP-23 settlement envelope), notarized. AereCloudException 409 (risk_refused, tx_mismatch, chain_mismatch), 404 (tx_not_found), 502 (notarization_failed). */
public String paymentsReceipt(String requestJson) { return req("POST", "/payments/receipt", requestJson, true); }
/** payments, verify a receipt (1.8.0): {receipt:{instruction, envelope}, policy, inputs} -> VALID / INVALID / PARTIAL. */
public String paymentsVerify(String requestJson) { return req("POST", "/payments/verify", requestJson, true); }
// ---- chain data ----
public String dataHead() { return req("GET", "/data/head", null, true); }
public String validators() { return req("GET", "/data/validators", null, true); }
public String anchors(int limit) { return req("GET", "/data/anchors?limit=" + limit, null, true); }
public String anchor(long height) { return req("GET", "/data/anchors/" + height, null, true); }
/** 2026-09-26: the post-quantum finality point (finalized/safe = the parent of the last certified anchor). */
/** 2026-09-26: the post-quantum certified point (finalized/safe = the parent of the last certified anchor; evidence that its signers sealed it, not by itself that it was decided, AIP-22 erratum 2026-10-01). */
public String finality() { return req("GET", "/data/finality", null, true); }
public String transfers(String address, int limit) {
return req("GET", "/data/transfers?address=" + address + "&limit=" + limit, null, true);

View File

@ -127,6 +127,16 @@ public final class TestAereCloud {
lc.complianceCheck("{\"policy\":{}}");
cere(ultima.get()[1].equals("/v1/compliance/check") && ultima.get()[2] != null && ultima.get()[3].equals("{\"policy\":{}}"), String.join(" | ", ultima.get()));
});
proba("1.8.0: paymentsRisk, paymentsReceipt si paymentsVerify trimit cererea neschimbata pe rutele lor, cu cheie", () -> {
String cerere = "{\"policy\":{\"id\":\"p\"},\"inputs\":{\"amount\":\"12345678901234567891\"}}";
lc.paymentsRisk(cerere);
String[] v = ultima.get();
cere(v[0].equals("POST") && v[1].equals("/v1/payments/risk") && v[2] != null && v[3].equals(cerere), String.join(" | ", v));
lc.paymentsReceipt("{\"txHash\":\"0x11\"}");
cere(ultima.get()[1].equals("/v1/payments/receipt") && ultima.get()[2] != null && ultima.get()[3].equals("{\"txHash\":\"0x11\"}"), String.join(" | ", ultima.get()));
lc.paymentsVerify("{\"receipt\":{}}");
cere(ultima.get()[1].equals("/v1/payments/verify") && ultima.get()[2] != null && ultima.get()[3].equals("{\"receipt\":{}}"), String.join(" | ", ultima.get()));
});
proba("1.5.0: auditExport: jsonl verified=true pe octetii care dau digestul, FALSE pe alti octeti, null la cef; un 400 se ridica", () -> {
raspunsuri.put("/v1/account/audit", new Object[]{200, zi, new String[]{"x-aere-digest", dZi, "x-aere-day", "2026-05-01", "x-aere-entries", "1"}});
AereCloud.AuditExport e = lc.auditExport("2026-05-01", "jsonl");

View File

@ -1,6 +1,6 @@
{
"name": "@aere/cloud",
"version": "1.7.0",
"version": "1.8.0",
"description": "Official client for the Aere Cloud API: keyed RPC, post-quantum verification, chain data, notarization, webhooks and gas sponsorship on Aere Network (chain 2800).",
"type": "module",
"main": "index.mjs",

View File

@ -43,6 +43,10 @@ receipt = aere.notarize("0x" + sha256_hex)
r = aere.verify(envelope, chain=2800) # {"verdict", "levels", "verifier", "reproduce"}
# identity and compliance (1.7.0): an AERE Identity presentation judged by the published command line; read "verdict"
idv = aere.identity_verify({"presentation": presentation, "audience": "https://your.app/verifier", "nonce": nonce, "trustedIssuers": [issuer_id]})
# payments (1.8.0): the risk step and the receipt, judged by the published module payments/; USD at the rate you declare; a REFUSE is a verdict
risk = aere.payments_risk({"policy": policy, "inputs": inputs})
receipt = aere.payments_receipt({"policy": policy, "inputs": inputs, "txHash": tx_hash})["receipt"]
check = aere.payments_verify({"receipt": receipt, "policy": policy, "inputs": inputs})
cc = aere.compliance_check({"presentation": presentation, "policy": policy, "audience": "https://your.app/verifier", "nonce": nonce, "record": True})
proof = aere.proof_of("0x" + sha256_hex)
@ -54,7 +58,7 @@ acct = aere.sponsor_create_pq_account("0x09" + falcon_pk_hex, salt=0)
Every method raises `AereCloudError` on a non-2xx response, carrying `.status` and the server's
JSON `.body` so you can act on the exact error.
## Proof with post-quantum finality, quantum readiness (1.2.0, 2026-09-17)
## Proof under a post-quantum certificate, quantum readiness (1.2.0, 2026-09-17)
```python
full = aere.proof(digest_hex) # first appearance, covering post-quantum anchor, finality, verification steps

View File

@ -14,7 +14,7 @@ import urllib.request
import urllib.error
from typing import Any, Optional
__version__ = "1.7.0"
__version__ = "1.8.0"
__all__ = ["AereCloud", "AereCloudError", "verify_webhook"]
_BAZA = "https://cloud.aere.network/v1"
@ -124,6 +124,25 @@ class AereCloud:
request: {presentation, policy, audience, nonce, statusLists?, revocations?, record?}."""
return self._req("POST", "/compliance/check", request)
def payments_risk(self, request: dict) -> dict:
"""payments, the risk step (1.8.0): one payment by a person or a company judged under YOUR declared policy by the published
module (payments/risc.mjs) -> ALLOW / REFUSE with the reasons, what was measured and what was NOT (named in 'unmeasured', never
simulated), and the hashes from which anyone reproduces the verdict. A refusal is a verdict (HTTP 200), not an error.
request: {policy, inputs}; inputs: {chainId, payer, payee, asset, amount, rate:{usdPerUnit, source, at}, history, now}."""
return self._req("POST", "/payments/risk", request)
def payments_receipt(self, request: dict) -> dict:
"""payments, the receipt (1.8.0): for a payment ALREADY on the chain the gateway serves; the verdict is reproduced and must be
ALLOW, the transaction must be the payment judged, and the receipt (instruction + AIP-23 settlement envelope) comes back
notarized. Raises AereCloudError 409 (risk_refused, tx_mismatch, chain_mismatch), 404 (tx_not_found), 502 (notarization_failed).
request: {policy, inputs, txHash, purpose?}. The USD amount is exact at the rate YOU declare; it is not a market price."""
return self._req("POST", "/payments/receipt", request)
def payments_verify(self, request: dict) -> dict:
"""payments, verify a receipt (1.8.0): the published module's rules against the policy, the inputs and the chain, and the
envelope judged by the reference verifier -> VALID / INVALID / PARTIAL. request: {receipt:{instruction, envelope}, policy, inputs}."""
return self._req("POST", "/payments/verify", request)
# ---- chain data ----
def data_head(self) -> dict:
return self._req("GET", "/data/head")
@ -138,7 +157,7 @@ class AereCloud:
return self._req("GET", f"/data/anchors/{int(height)}")
def finality(self) -> dict:
"""2026-09-26: the post-quantum finality point (finalized/safe = the parent of the last certified anchor)."""
"""2026-09-26: the post-quantum certified point (finalized/safe = the parent of the last certified anchor; evidence that its signers sealed it, not by itself that it was decided, AIP-22 erratum 2026-10-01)."""
return self._req("GET", "/data/finality")
def transfers(self, address: str, limit: int = 50) -> dict:

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "aere-cloud"
version = "1.7.0"
version = "1.8.0"
description = "Official Python client for the Aere Cloud API (Aere Network, chain 2800): RPC, post-quantum verification, chain data, notarization, webhooks, gas sponsorship."
readme = "README.md"
requires-python = ">=3.9"

View File

@ -103,6 +103,22 @@ def t_identitate():
assert c.compliance_check(cc)["verdict"] == "COMPLIANT"
assert tr.cereri[1].full_url.endswith("/compliance/check") and json.loads(tr.cereri[1].data.decode()) == cc
def t_plati():
tr = Transport([(200, {"verdict": "REFUSE", "reasons": ["above the per-payment limit"]}), (201, {"receipt": {"instruction": {}, "envelope": {}}}), (200, {"verdict": "PARTIAL"})])
c = AereCloud("k", opener=tr)
cerere = {"policy": {"v": 1, "kind": "aere-payment-risk-policy", "id": "p"}, "inputs": {"chainId": 28001, "asset": "native", "amount": "400000000000000000", "history": [], "now": 1}}
assert c.payments_risk(cerere)["verdict"] == "REFUSE", "un REFUSE e un verdict (200), nu o eroare"
assert tr.cereri[0].full_url.endswith("/payments/risk") and tr.cereri[0].get_method() == "POST"
assert tr.cereri[0].get_header("X-api-key") == "k", "payments_risk cere cheia"
assert json.loads(tr.cereri[0].data.decode()) == cerere
cc = {**cerere, "txHash": "0x" + "11" * 32, "purpose": "order 7"}
assert "receipt" in c.payments_receipt(cc)
assert tr.cereri[1].full_url.endswith("/payments/receipt") and json.loads(tr.cereri[1].data.decode()) == cc
cv = {"receipt": {"instruction": {"a": 1}, "envelope": {"b": 2}}, "policy": cerere["policy"], "inputs": cerere["inputs"]}
assert c.payments_verify(cv)["verdict"] == "PARTIAL"
assert tr.cereri[2].full_url.endswith("/payments/verify") and json.loads(tr.cereri[2].data.decode()) == cv
assert tr.cereri[2].get_header("X-api-key") == "k", "payments_verify cere cheia"
def t_pq_sponsor():
tr = Transport([(200, {"txHash": "0xab", "account": "0xcd", "alreadyDeployed": False})])
c = AereCloud("k", opener=tr)
@ -127,6 +143,7 @@ proba("un status de eroare devine AereCloudError cu corpul serverului", t_eroare
proba("rutele de date construiesc corect query-ul", t_query)
proba("verify (Trust API, 1.6.0): ruta, ?chain, cheia si plicul neschimbat", t_verify)
proba("identity_verify si compliance_check (1.7.0): rutele, cheia si cererea neschimbata", t_identitate)
proba("payments_risk, payments_receipt si payments_verify (1.8.0): rutele, cheia si cererea neschimbata", t_plati)
proba("sponsor_create_pq_account: corp si cale corecte", t_pq_sponsor)
proba("verify_webhook: buna trece; stricata, prelungita, corp atins pica", t_webhook)

View File

@ -16,6 +16,11 @@ const PLANTARI = [
['JS: identityVerify trimite o cerere schimbata', 'index.mjs', "identityVerify(request) { return this._req('POST', '/identity/verify', { body: request }); }", "identityVerify(request) { return this._req('POST', '/identity/verify', { body: { ...request, maxAgeSeconds: 3600 } }); }", 'js', '1.7.0: identityVerify si complianceCheck'],
['Python: compliance_check pe ruta identitatii', 'python/aere_cloud/__init__.py', 'return self._req("POST", "/compliance/check", request)', 'return self._req("POST", "/identity/verify", request)', 'py', 'identity_verify si compliance_check'],
['Python: identity_verify trimite o cerere schimbata', 'python/aere_cloud/__init__.py', 'return self._req("POST", "/identity/verify", request)', 'return self._req("POST", "/identity/verify", {**request, "maxAgeSeconds": 3600})', 'py', 'identity_verify si compliance_check'],
// 1.8.0: chitanta trimisa pe ruta riscului; verificarea cu cererea schimbata
['JS: paymentsReceipt pe ruta riscului', 'index.mjs', "paymentsReceipt(request) { return this._req('POST', '/payments/receipt', { body: request }); }", "paymentsReceipt(request) { return this._req('POST', '/payments/risk', { body: request }); }", 'js', 'paymentsRisk, paymentsReceipt si paymentsVerify'],
['JS: paymentsVerify trimite o cerere schimbata', 'index.mjs', "paymentsVerify(request) { return this._req('POST', '/payments/verify', { body: request }); }", "paymentsVerify(request) { return this._req('POST', '/payments/verify', { body: { ...request, policy: {} } }); }", 'js', 'paymentsRisk, paymentsReceipt si paymentsVerify'],
['Python: payments_receipt pe ruta riscului', 'python/aere_cloud/__init__.py', 'return self._req("POST", "/payments/receipt", request)', 'return self._req("POST", "/payments/risk", request)', 'py', 'payments_risk, payments_receipt si payments_verify'],
['Python: payments_verify trimite o cerere schimbata', 'python/aere_cloud/__init__.py', 'return self._req("POST", "/payments/verify", request)', 'return self._req("POST", "/payments/verify", {**request, "policy": {}})', 'py', 'payments_risk, payments_receipt si payments_verify'],
];
function copie() {
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-sdk-cn-'));

View File

@ -73,6 +73,24 @@ await test('1.7.0: identityVerify si complianceCheck trimit cererea neschimbata
assert.deepEqual(rute['POST /compliance/check']._vazut.body, cc);
});
await test('1.8.0: paymentsRisk, paymentsReceipt si paymentsVerify trimit cererea neschimbata pe rutele lor, cu cheie; un 409 se ridica cu corpul lui', async () => {
const rute = { 'POST /payments/risk': { status: 200, corp: { verdict: 'REFUSE', reasons: ['above the per-payment limit'] } }, 'POST /payments/receipt': { status: 201, corp: { receipt: { instruction: {}, envelope: {} } } }, 'POST /payments/verify': { status: 200, corp: { verdict: 'PARTIAL' } } };
const c = new AereCloud({ apiKey: 'ak2800.0xAAA.AERE-SINTETIC-s', baseUrl: BAZA_TEST, fetch: fetchFals(rute) });
const cerere = { policy: { v: 1, kind: 'aere-payment-risk-policy', id: 'p' }, inputs: { chainId: 28001, payer: '0x' + 'aa'.repeat(20), payee: '0x' + 'bb'.repeat(20), asset: 'native', amount: '400000000000000000', rate: { usdPerUnit: '0.5', source: 'declared', at: '2026-10-03T00:00:00Z' }, history: [], now: 1 } };
assert.equal((await c.paymentsRisk(cerere)).verdict, 'REFUSE', 'un REFUSE e un verdict (200), nu o eroare');
assert.deepEqual(rute['POST /payments/risk']._vazut.body, cerere);
assert.ok(rute['POST /payments/risk']._vazut.headers['x-api-key'], 'paymentsRisk cere cheia');
const cc = { ...cerere, txHash: '0x' + '11'.repeat(32), purpose: 'order 7' };
assert.ok((await c.paymentsReceipt(cc)).receipt, 'chitanta intoarsa');
assert.deepEqual(rute['POST /payments/receipt']._vazut.body, cc);
const cv = { receipt: { instruction: { a: 1 }, envelope: { b: 2 } }, policy: cerere.policy, inputs: cerere.inputs };
assert.equal((await c.paymentsVerify(cv)).verdict, 'PARTIAL');
assert.deepEqual(rute['POST /payments/verify']._vazut.body, cv);
assert.ok(rute['POST /payments/verify']._vazut.headers['x-api-key'], 'paymentsVerify cere cheia');
const c2 = new AereCloud({ apiKey: 'ak2800.0xAAA.AERE-SINTETIC-s', baseUrl: BAZA_TEST, fetch: fetchFals({ 'POST /payments/receipt': { status: 409, corp: { error: 'risk_refused', verdict: 'REFUSE' } } }) });
await assert.rejects(() => c2.paymentsReceipt(cc), (e) => e instanceof AereCloudError && e.status === 409 && e.body.error === 'risk_refused');
});
await test('rutele de date construiesc corect query-ul', async () => {
const rute = {
'GET /data/anchors?limit=3': { status: 200, corp: { anchors: [] } },