open-standards/openpay/crates/op-openbanking/src/stet.rs
Transaction Science 6ea90afbed ingest wave A4: 3 crates · smart-byte-atproto + op-openbanking + eoc-distributed
Smart Byte (1 crate, 2127 LOC, 35 tests)
  smart-byte-atproto    ATProto (Bluesky federation) adapter.
                        PLC + did:web resolution; XRPC client; CARv1 encode/decode
                        with CIDv1 (dag-cbor + raw, SHA-256); sketch MST with
                        order-independent BLAKE3 root; Repo + UnsignedCommit /
                        SignedCommit (Ed25519 over CBOR); firehose transport
                        trait + in-memory impl; AT URI ↔ smart-byte AID bridge
                        with SAID commitment.

OpenPay (1 crate, 2397 LOC, 44 tests)
  op-openbanking        UK OBIE R/W v3.1.10/3.1.11, Berlin Group NextGenPSD2
                        XS2A v1.3.13, STET PSD2 v1.7, Australia CDR Banking v1.31,
                        FDX v5/v6, SGFinDex shapes. AISP / PISP / PIIS / VRP
                        traits. FAPI 1.0 Advanced (PS256/ES256, mTLS, RFC 8705
                        certificate binding, JWK registration). Operators wire
                        their own JWS signer + HTTP transport — keeps the crate
                        KMS-agnostic.

EOC (1 crate, 2179 LOC, 32 tests)
  eoc-distributed       Distributed-inference scheduling. Worker trait with
                        Capability + Accelerator (CPU/GPU/TPU/NPU/Mixed) +
                        Load.busyness()/projected_micro_joules()/projected_g_co2e().
                        RoundRobin / LeastBusy / LatencyWeighted / JouleWeighted
                        (default) / CarbonWeighted routers. Work-stealing
                        scheduler. vLLM-style continuous batching with KV-block
                        budget. Ray-Serve-style auto-scaler with cooldown.
                        KV-cache-aware sticky-session routing.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-24 12:22:09 -04:00

101 lines
3.3 KiB
Rust

//! STET PSD2 API binding (France).
//!
//! Reference: *STET PSD2 API Documentation Part 1* v1.7. STET is the
//! French PSD2 dialect — adopted by BNP Paribas, Crédit Agricole,
//! Société Générale, BPCE, La Banque Postale. The dialect is
//! structurally similar to Berlin Group but uses different endpoint
//! and field names.
//!
//! STET v1.7 does **not** include a VRP profile. Operators needing
//! recurring debits use SEPA Direct Debit instead.
use serde::{Deserialize, Serialize};
/// STET endpoint families. Mapped to `/v1/{stet-endpoint-segment}`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum StetEndpointKind {
/// `accounts` — AISP root.
Accounts,
/// `balances` — balance reads for a known account.
Balances,
/// `transactions` — transaction history.
Transactions,
/// `payment-requests` — PISP (single immediate or future-dated).
PaymentRequests,
/// `funds-confirmations` — CBPII / CoF.
FundsConfirmations,
/// `trusted-beneficiaries` — read of the PSU's trusted beneficiary
/// list, used by STET for the SCA-exemption white-list flow.
TrustedBeneficiaries,
}
impl StetEndpointKind {
/// Path segment used under `/v1/...`.
#[must_use]
pub const fn as_segment(self) -> &'static str {
match self {
Self::Accounts => "accounts",
Self::Balances => "balances",
Self::Transactions => "transactions",
Self::PaymentRequests => "payment-requests",
Self::FundsConfirmations => "funds-confirmations",
Self::TrustedBeneficiaries => "trusted-beneficiaries",
}
}
}
/// STET consent. STET pins consent semantics tightly to the PSU's
/// `psuId` (the customer-bank-issued identifier passed in the
/// `PSU-ID` header). The TPP cannot create a consent without prior
/// PSU authentication.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StetConsent {
/// `psuId` of the customer the consent applies to.
pub psu_id: String,
/// IBANs the consent covers (empty for `global` consents on
/// AISP-only flows where the ASPSP returns the list).
pub ibans: Vec<String>,
/// Expiry per STET `validUntil`.
pub valid_until: time::Date,
}
/// STET service handle.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StetService {
/// ASPSP base URL.
pub aspsp_base_url: String,
}
impl StetService {
/// Build a `/v1/{segment}` URL.
#[must_use]
pub fn endpoint(&self, kind: StetEndpointKind) -> String {
let trimmed = self.aspsp_base_url.trim_end_matches('/');
format!("{}/v1/{}", trimmed, kind.as_segment())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn endpoint_segments_are_kebab_case() {
assert_eq!(StetEndpointKind::PaymentRequests.as_segment(), "payment-requests");
assert_eq!(
StetEndpointKind::FundsConfirmations.as_segment(),
"funds-confirmations"
);
}
#[test]
fn endpoint_url_constructs() {
let svc = StetService {
aspsp_base_url: "https://api.banque.example".into(),
};
assert_eq!(
svc.endpoint(StetEndpointKind::Accounts),
"https://api.banque.example/v1/accounts"
);
}
}