1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! Stripe, behind kasapay's [`Provider`](kasapay_core::Provider) trait.
//!
//! A thin adapter over [`async-stripe`], which is generated from Stripe's own
//! OpenAPI document and regenerated weekly. This crate does not re-derive that
//! work; it maps PaymentIntents onto kasapay's [`Charge`](kasapay_core::Charge)
//! and gets out of the way.
//!
//! # What maps onto what
//!
//! - A charge is a PaymentIntent. `ChargeRequest::order` has no Stripe field of
//! its own, so it travels as metadata under [`ORDER_METADATA_KEY`].
//! - A PaymentIntent that still needs the payer comes back as
//! [`NextAction::ConfirmOnClient`](kasapay_core::NextAction::ConfirmOnClient)
//! carrying the `client_secret` for Stripe.js.
//! - [`Stripe::refund`] gives money back, and [`Stripe::cancel`] withdraws a
//! payment that was never captured. Neither is on the shared trait yet.
//! - A refunded PaymentIntent still reads `succeeded`, so how much of a
//! payment has been given back is [`Stripe::refunds`] summed rather than a
//! status to read.
//! - **A declined card is not [`Status::Failed`](kasapay_core::Status::Failed).**
//! Stripe puts such a PaymentIntent back to `requires_payment_method`, which
//! arrives as [`Status::RequiresAction`](kasapay_core::Status::RequiresAction)
//! — honest, because the payer can try another card, but a caller waiting
//! for `Failed` from Stripe waits forever.
//! - Anything this crate does not model is reachable through
//! [`Stripe::client`], which hands back the `async-stripe` client itself.
//! - A saved card is a `pm_…`, which stands alone rather than needing a
//! second handle beside it — see the [`saved`] module.
//! [`Provider::instruments`](kasapay_core::Provider::instruments) lists a
//! customer's, the same as [`Stripe::stored_cards`] with the brand and last
//! four turned into a label; [`Stripe::charge_saved_card`] charges one
//! without a card number in sight, and [`Stripe::forget_card`] detaches one.
//!
//! # Retrying
//!
//! Safe. `ChargeRequest::idempotency_key` is sent as Stripe's
//! `Idempotency-Key`, so replaying a charge with the same key takes the money
//! once. Without a key, a retry is a second charge.
//!
//! [`Provider::capture`](kasapay_core::Provider::capture)'s own `idempotency`
//! travels the same way — a capture is a `POST` too, and Stripe reads its
//! `Idempotency-Key` the same regardless of which endpoint it is sent to.
//! Without one, a second capture takes the funds a second time.
//!
//! # Which `async-stripe`
//!
//! Exactly `1.0.0-rc.8`, pinned rather than ranged. The 1.0 line is a
//! prerelease and a candidate has changed generated types before —
//! `PaymentIntent` gained a public field between rc.6 and rc.7 — so which one
//! this is built against is not left to resolution.
//!
//! It costs a caller one thing: a crate that depends on `async-stripe` itself
//! has to be on the same candidate, because two exact pins at different
//! candidates cannot resolve together. [`Stripe::client`] hands the client back
//! so that reaching what this crate does not model needs no second dependency.
//! The pin becomes a range the day 1.0.0 ships.
//!
//! # Example
//!
//! ```no_run
//! use kasapay_core::{ChargeRequest, Currency, Money, OrderRef, Provider, Secret};
//! use kasapay_stripe::Stripe;
//!
//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
//! let stripe = Stripe::new(&Secret::new(std::env::var("STRIPE_SECRET_KEY")?));
//!
//! let request = ChargeRequest::builder(
//! OrderRef::new("ord-2026-0001"),
//! Money::parse("19.99", Currency::Usd)?,
//! )
//! .description("one coffee")
//! .build()?;
//!
//! let charge = stripe.charge(&request).await?;
//! println!("{:?} {:?}", charge.status, charge.next_action);
//! # Ok(())
//! # }
//! ```
//!
//! [`async-stripe`]: https://docs.rs/async-stripe
pub use crate;
pub use crate;