Skip to main content

kasapay/
lib.rs

1//! One payment API over any payment provider.
2//!
3//! Write against [`Provider`] and which provider takes the money becomes a
4//! deployment decision rather than a rewrite. Five ship with this workspace —
5//! Stripe, iyzico, PayTR, Mollie and PayPal — and a provider that lives
6//! elsewhere is a first-class one: implement [`Provider`], name it with
7//! [`ProviderId::new`]. Everything a caller needs is re-exported here; the
8//! bundled adapters are behind features, one each.
9//!
10//! ```toml
11//! kasapay = { version = "0.0.5", features = ["stripe", "iyzico"] }
12//! ```
13//!
14//! # The one thing to understand first
15//!
16//! [`Provider::charge`] does not mean the money moved. It returns a [`Charge`]
17//! whose [`Status`] is often [`Status::RequiresAction`], with a [`NextAction`]
18//! saying what the payer must do — confirm in the browser for Stripe, follow a
19//! deep link into iyzico's app for iyzico. Treating a returned `Charge` as a
20//! completed payment is the mistake this crate is shaped to prevent.
21//!
22//! # What the trait answers
23//!
24//! [`Provider`] is `charge`, `charge_status`, `capture`, `cancel`, `refund`,
25//! `lookup` and `instruments`, and [`Capabilities`] says which of them a given
26//! provider actually does — before there is a payment to ask about. A
27//! capability that says yes and a call that then fails is a bug in the
28//! adapter.
29//!
30//! **Giving money back is [`Provider::refund`]**, which answers a [`Refund`]
31//! with its own life: its own identifier where the provider issues one, its
32//! own [`RefundStatus`], and at iyzico's counter its own [`NextAction`],
33//! because there the payer approves the refund in an app. [`Status`] has no
34//! `Refunded` and will not grow one — no provider reports a refund as a
35//! payment's status.
36//!
37//! **The call whose answer never arrived is [`Provider::lookup`]**, keyed by
38//! [`OrderRef`] — the caller's own reference, which they had before they sent
39//! anything. `Ok(None)` means the provider has no record and the charge can
40//! safely be sent again. Two of the five can answer it; the rest say what to
41//! do instead.
42//!
43//! # What arrives without being asked for
44//!
45//! [`Webhook`] is the second trait: it takes the headers and bytes of a
46//! [`Delivery`], shows they are the provider's, and says what they mean as an
47//! [`Event`].
48//!
49//! ```no_run
50//! use kasapay::{Delivery, EventKind, Webhook};
51//!
52//! # async fn handle(verifier: &dyn Webhook, headers: &[(&str, &str)], body: &[u8]) -> &'static str {
53//! match verifier.verify(&Delivery::new(headers, body)).await {
54//!     // The identifier goes into a unique index before anything ships: the
55//!     // second delivery of an event must collide rather than ship twice.
56//!     Ok(event) if event.kind == EventKind::Captured => "ship it",
57//!     // Not an error. A provider adding an event type is normal, and
58//!     // refusing one earns days of redeliveries for something nobody wanted.
59//!     Ok(_) => "acknowledged",
60//!     // Do not act on it — and still answer the provider what the provider
61//!     // documents. Those are two different questions.
62//!     Err(_) => "acknowledged",
63//! }
64//! # }
65//! ```
66//!
67//! # Choosing a provider at runtime
68//!
69//! ```no_run
70//! use std::sync::Arc;
71//! use kasapay::{Provider, ProviderId};
72//!
73//! # #[cfg(all(feature = "stripe", feature = "iyzico"))]
74//! # fn pick(
75//! #     id: ProviderId,
76//! #     stripe: kasapay::stripe::Stripe,
77//! #     iyzico: kasapay::iyzico::in_store::Client,
78//! # )
79//! # -> Option<Arc<dyn Provider>> {
80//! match id {
81//!     ProviderId::STRIPE => Some(Arc::new(stripe)),
82//!     ProviderId::IYZICO => Some(Arc::new(iyzico)),
83//!     _ => None,
84//! }
85//! # }
86//! ```
87
88#[doc(inline)]
89pub use kasapay_core::{
90    Address, BasketItem, Buyer, Capabilities, Charge, ChargeRequest, ChargeRequestBuilder,
91    ChargeRequestError, Currency, Delivery, Error, ErrorKind, Event, EventId, EventKind, Id,
92    IdKind, IdSource, IdempotencyKey, Instrument, InstrumentId, ItemKind, Money, MoneyError,
93    NextAction, OrderRef, PaymentId, Provider, ProviderId, Raw, Refund, RefundId, RefundReason,
94    RefundRequest, RefundRequestBuilder, RefundRequestError, RefundStatus, RepeatedHeader, Secret,
95    Status, UnknownCurrency, Webhook, async_trait, kind,
96};
97
98#[cfg(feature = "iyzico")]
99#[doc(inline)]
100pub use kasapay_iyzico as iyzico;
101#[cfg(feature = "mollie")]
102#[doc(inline)]
103pub use kasapay_mollie as mollie;
104#[cfg(feature = "paypal")]
105#[doc(inline)]
106pub use kasapay_paypal as paypal;
107#[cfg(feature = "paytr")]
108#[doc(inline)]
109pub use kasapay_paytr as paytr;
110#[cfg(feature = "stripe")]
111#[doc(inline)]
112pub use kasapay_stripe as stripe;