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]>
142 lines
4.5 KiB
Rust
142 lines
4.5 KiB
Rust
//! Berlin Group NextGenPSD2 PISP integration test.
|
|
//!
|
|
//! Wires the vendor-neutral [`PaymentInitiationService`] over a mock
|
|
//! Berlin Group transport and exercises the endpoint builder for the
|
|
//! various `payment-product` segments.
|
|
|
|
use op_core::{Currency, Money};
|
|
use op_openbanking::aisp::ConsentId;
|
|
use op_openbanking::berlin_group::{BerlinPaymentProduct, BerlinService};
|
|
use op_openbanking::fapi::OAuth2Token;
|
|
use op_openbanking::pisp::{
|
|
PaymentInitiation, PaymentInitiationService, PaymentInitiationStatus, PaymentKind, PaymentRef,
|
|
};
|
|
use op_openbanking::{Error, Result};
|
|
|
|
struct MockBerlinPisp {
|
|
svc: BerlinService,
|
|
product: BerlinPaymentProduct,
|
|
}
|
|
|
|
impl PaymentInitiationService for MockBerlinPisp {
|
|
fn create_consent(
|
|
&self,
|
|
_token: &OAuth2Token,
|
|
payment: &PaymentInitiation,
|
|
) -> Result<ConsentId> {
|
|
payment.validate()?;
|
|
// Real driver would POST to `payments_endpoint(product)`.
|
|
// We just record that the URL we'd hit looks correct.
|
|
let url = self.svc.payments_endpoint(self.product);
|
|
assert!(url.starts_with("https://"));
|
|
Ok(ConsentId("berlin-cs-1".into()))
|
|
}
|
|
|
|
fn submit(
|
|
&self,
|
|
_consent: &ConsentId,
|
|
_token: &OAuth2Token,
|
|
payment: &PaymentInitiation,
|
|
) -> Result<PaymentRef> {
|
|
payment.validate()?;
|
|
if payment.amount.currency != Currency::EUR {
|
|
return Err(Error::CurrencyMismatch(format!(
|
|
"Berlin Group SCT requires EUR; got {}",
|
|
payment.amount.currency
|
|
)));
|
|
}
|
|
Ok(PaymentRef("pay-1".into()))
|
|
}
|
|
|
|
fn status(
|
|
&self,
|
|
_token: &OAuth2Token,
|
|
_payment_ref: &PaymentRef,
|
|
) -> Result<PaymentInitiationStatus> {
|
|
Ok(PaymentInitiationStatus::AcceptedSettlementInProcess)
|
|
}
|
|
}
|
|
|
|
fn token() -> OAuth2Token {
|
|
OAuth2Token {
|
|
access_token: "tok".into(),
|
|
token_type: "Bearer".into(),
|
|
scopes: vec!["payments".into()],
|
|
expires_in: 600,
|
|
refresh_token: None,
|
|
cert_thumbprint: None,
|
|
}
|
|
}
|
|
|
|
fn payment() -> PaymentInitiation {
|
|
PaymentInitiation {
|
|
kind: PaymentKind::ImmediateSingle,
|
|
debtor_account: "DE89370400440532013000".into(),
|
|
creditor_account: "FR1420041010050500013M02606".into(),
|
|
creditor_name: "Acme EU".into(),
|
|
amount: Money::from_minor(99_99, Currency::EUR),
|
|
end_to_end_id: "BG-2026-001".into(),
|
|
remittance: Some("Invoice 9".into()),
|
|
requested_execution_date: None,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn instant_sct_endpoint_is_instant_segment() {
|
|
let svc = BerlinService {
|
|
aspsp_base_url: "https://xs2a.aspsp.example".into(),
|
|
aspsp_id: "AAAA1234".into(),
|
|
};
|
|
assert!(
|
|
svc.payments_endpoint(BerlinPaymentProduct::InstantSepaCreditTransfers)
|
|
.ends_with("/payments/instant-sepa-credit-transfers")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pisp_happy_path_creates_and_submits() {
|
|
let mock = MockBerlinPisp {
|
|
svc: BerlinService {
|
|
aspsp_base_url: "https://xs2a.aspsp.example".into(),
|
|
aspsp_id: "AAAA1234".into(),
|
|
},
|
|
product: BerlinPaymentProduct::SepaCreditTransfers,
|
|
};
|
|
let p = payment();
|
|
let consent = mock.create_consent(&token(), &p).expect("consent");
|
|
let r = mock.submit(&consent, &token(), &p).expect("submit");
|
|
let status = mock.status(&token(), &r).expect("status");
|
|
assert_eq!(status, PaymentInitiationStatus::AcceptedSettlementInProcess);
|
|
}
|
|
|
|
#[test]
|
|
fn non_eur_rejected_on_sct() {
|
|
let mock = MockBerlinPisp {
|
|
svc: BerlinService {
|
|
aspsp_base_url: "https://xs2a.aspsp.example".into(),
|
|
aspsp_id: "AAAA1234".into(),
|
|
},
|
|
product: BerlinPaymentProduct::SepaCreditTransfers,
|
|
};
|
|
let mut p = payment();
|
|
p.amount = Money::from_minor(10_000, Currency::GBP);
|
|
let err = mock
|
|
.submit(&ConsentId("c".into()), &token(), &p)
|
|
.expect_err("currency");
|
|
assert!(matches!(err, Error::CurrencyMismatch(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn zero_amount_rejected_at_validate() {
|
|
let mock = MockBerlinPisp {
|
|
svc: BerlinService {
|
|
aspsp_base_url: "https://xs2a.aspsp.example".into(),
|
|
aspsp_id: "AAAA1234".into(),
|
|
},
|
|
product: BerlinPaymentProduct::SepaCreditTransfers,
|
|
};
|
|
let mut p = payment();
|
|
p.amount = Money::from_minor(0, Currency::EUR);
|
|
let err = mock.create_consent(&token(), &p).expect_err("validate");
|
|
assert!(matches!(err, Error::PaymentInitiationInvalid { .. }));
|
|
}
|