open-standards/openpay/crates/op-core/src/rails.rs
Transaction Science 70ecd6caa0 Initial import — three open standards under one shelf
Transaction Science open standards monorepo. Each subdirectory is a
self-contained reference protocol with its own README and licence.

- openpay/    Apache-2.0  payment-acceptance stack (Rust, 26 crates)
- smart-byte/ CC-BY-4.0   content-addressed value carrier (spec)
- eoc/        CC-BY-4.0   energy-optimised compute (spec)

The wire format and the right to fork are public. The optional hosted
services are the offer; the protocols are not.
2026-05-23 07:58:09 -04:00

87 lines
3.4 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Rail abstraction.
//!
//! `op-core` is rail-agnostic. Concrete drivers (Hyperswitch, `FedNow`, PIX,
//! UPI, Apple Pay, ...) live in `op-rails-*` crates and implement the
//! [`Acquirer`] trait. The orchestrator routes a `Payment<Created>` to a
//! driver by inspecting the payment's [`RailKind`].
//!
//! ## Async
//!
//! The trait is intentionally NOT `async fn` in a trait (yet — Rust 1.95
//! supports it but with object-safety footguns). Drivers return a
//! `Pin<Box<dyn Future>>` via the `async_trait` crate, or callers use a
//! runtime-specific concrete type. We will refine this when the
//! `return_type_notation` RFC stabilizes.
use serde::{Deserialize, Serialize};
use crate::method::PaymentMethod;
use crate::money::Money;
/// Top-level family of payment rails.
///
/// A specific driver crate maps each variant to one or more underlying
/// schemes (e.g. `A2a` covers `FedNow`, RTP, PIX, UPI, SEPA Instant; the
/// driver picks based on currency and routing).
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum RailKind {
/// Card networks (Visa, Mastercard, Amex, Discover, `RuPay`, Elo, etc.)
/// via a PSP. Requires `PaymentMethod::Vault | Wallet | Emv`.
Card,
/// Account-to-account / instant rails: `FedNow`, RTP, PIX, UPI, SEPA Inst.
/// Requires `PaymentMethod::A2a` or `PaymentMethod::Qr`.
A2a,
/// Mobile wallet rails using device cryptograms (Apple Pay, Google Pay).
/// Requires `PaymentMethod::Wallet`.
Wallet,
/// QR-presented payment (`EMVCo` merchant-presented or
/// consumer-presented). Often resolves to A2a underneath.
Qr,
/// Crypto / stablecoin settlement (USDC on Solana / Base / Ethereum,
/// EURC, PYUSD, etc.). Requires `PaymentMethod::Crypto`. The
/// fee profile that motivates the whole rail family: sub-cent
/// settlement vs. 100–300 bp on card.
Crypto,
}
/// What an acquirer returns after attempting authorization or capture.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AcquirerResponse {
/// Opaque id from the rail (auth code, end-to-end id, network txn id).
pub rail_ref: String,
/// How much the rail confirmed (may be less than requested).
pub amount: Money,
/// Free-form status from the rail.
pub status: AcquirerStatus,
}
/// Normalized acquirer status. Drivers map their native codes to this.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AcquirerStatus {
/// Authorized (card) or accepted-for-settlement (A2A).
Approved,
/// Soft decline — caller may retry, possibly with a different method.
SoftDecline,
/// Hard decline — do not retry with the same method.
HardDecline,
/// Suspected fraud; the caller should freeze and review.
Fraud,
/// Transient failure (timeout, network); retry-safe.
Transient,
}
/// An acquirer driver. Implemented by crates in the `op-rails-*` family.
///
/// The trait is dyn-compatible: callers can hold `Box<dyn Acquirer>` and
/// route across multiple rails at runtime.
pub trait Acquirer: Send + Sync {
/// The rail family this driver serves. The orchestrator uses this to
/// route an incoming payment to the correct driver.
fn rail_kind(&self) -> RailKind;
/// Human-readable driver name (e.g. `"hyperswitch"`, `"fednow-frb"`).
fn name(&self) -> &'static str;
/// True if this driver can handle the given payment method.
fn supports(&self, method: &PaymentMethod) -> bool;
}