Expand description
Stripe, behind kasapay’s 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
and gets out of the way.
§What maps onto what
- A charge is a PaymentIntent.
ChargeRequest::orderhas no Stripe field of its own, so it travels as metadata underORDER_METADATA_KEY. - A PaymentIntent that still needs the payer comes back as
NextAction::ConfirmOnClientcarrying theclient_secretfor Stripe.js. Stripe::refundgives money back, andStripe::cancelwithdraws 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 isStripe::refundssummed rather than a status to read. - A declined card is not
Status::Failed. Stripe puts such a PaymentIntent back torequires_payment_method, which arrives asStatus::RequiresAction— honest, because the payer can try another card, but a caller waiting forFailedfrom Stripe waits forever. - Anything this crate does not model is reachable through
Stripe::client, which hands back theasync-stripeclient itself. - A saved card is a
pm_…, which stands alone rather than needing a second handle beside it — see thesavedmodule.Provider::instrumentslists a customer’s, the same asStripe::stored_cardswith the brand and last four turned into a label;Stripe::charge_saved_cardcharges one without a card number in sight, andStripe::forget_carddetaches 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’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
use kasapay_core::{ChargeRequest, Currency, Money, OrderRef, Provider, Secret};
use kasapay_stripe::Stripe;
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);Modules§
- saved
- Charging a card Stripe already holds, and what a listing says about one.
Structs§
- Refund
- Money given back off a Stripe payment.
- Stripe
- Takes payments through Stripe.
- Webhooks
- Verifies deliveries Stripe makes to one webhook address.
Enums§
- Refund
State - Where a refund has got to.
Constants§
- DEFAULT_
TIMEOUT - How long a request waits before it is given up on.
- DEFAULT_
TOLERANCE - How far a delivery’s timestamp may be from now.
- ORDER_
METADATA_ KEY - The order reference travels as PaymentIntent metadata under this key.
- REFUND_
REASON_ METADATA_ KEY RefundReason::Other’s own words travel as refund metadata under this key — Stripe’sreasontakes three values and none of them is free text.