Expand description
cratefield-adapter-stripe: the Payments port over the Stripe REST API
(issue #102). Uses the runtime’s HttpClient and Clock ports — no
vendor SDK — so the same adapter runs on Workers and natively.
Card data never crosses this adapter. Every call creates or reads a
Stripe object by id, or returns a hosted Stripe URL the browser is
redirected to; card numbers are entered on Stripe’s own pages. The harness
holds Stripe identifiers, nothing more (see docs/PAYMENTS.md).
Degraded mode. Stripe::not_configured reports
PaymentsError::NotConfigured without any network call, so a venture with
no Stripe keys still builds and runs.
Verification. Request shaping, error mapping, and webhook signature
verification (including a tampered signature and a stale timestamp) are unit
tested here against a scripted HttpClient. The live path against Stripe is
needs-human (issue #102 acceptance): it needs real test-mode keys, which
do not live in the repo.
§cratefield-adapter-stripe
The Payments port over the Stripe REST API,
for the Cratefield harness (issue #102).
It talks to api.stripe.com through the runtime’s HttpClient port, so the one
adapter runs unchanged on Cloudflare Workers and on the native runtime — no
vendor SDK.
§Card data never crosses this adapter
Every call creates or reads a Stripe object by id, or returns a hosted Stripe
URL the browser is redirected to (Checkout, Connect onboarding). Card numbers,
CVCs and expiries are entered on Stripe’s own pages and never reach the harness;
it holds Stripe identifiers only. See docs/PAYMENTS.md.
§What it does
create_checkout/create_subscription_checkout— hosted Checkout inpayment/subscriptionmode; returns the URL to redirect to.create_connect_account_link— creates an Express Connect account (a coach) when needed and returns a hosted onboarding link.charge_with_transfer— a destination charge (PaymentIntentwithtransfer_data[destination]andapplication_fee_amount): the platform keeps its fee, the rest goes to the connected account.refund— a full or partial refund of a prior payment.create_portal_session— a hosted customer-portal session (POST /v1/billing_portal/sessions): the page where a customer changes plan, updates their payment method, reads invoices and cancels.get_subscription/list_subscriptions— read one subscription by id (GET /v1/subscriptions/{id}), or every subscription for a customer. The list sendsstatus=all, since Stripe omits canceled subscriptions by default.report_usage— reports one metered usage amount to a Stripe Billing Meter (POST /v1/billing/meter_events). Theidentifieris the exactly-once key and also theIdempotency-Key, so a retry is safe; Stripe’sduplicate_meter_eventis returned asalready_reported: true, not an error.verify_webhook— verifies theStripe-SignatureHMAC-SHA256 and its timestamp (5-minute tolerance) over the raw body, then returns the event.
Every mutating call sends an Idempotency-Key. When no key is configured the
adapter reports NotConfigured without any network call.
§Usage
use std::sync::Arc;
use cratefield_adapter_stripe::Stripe;
let http = Arc::new(cratefield_runtime_cloudflare::FetchClient);
let clock = Arc::new(cratefield_runtime_cloudflare::WorkersClock);
let payments: Arc<dyn cratefield_core::Payments> = match env.secret("STRIPE_SECRET_KEY").ok() {
Some(key) => Arc::new(Stripe::new(
http,
clock,
key.to_string(),
env.secret("STRIPE_WEBHOOK_SECRET").map(|s| s.to_string()).unwrap_or_default(),
)),
None => Arc::new(Stripe::not_configured()),
};
let runtime = cratefield_runtime_cloudflare::Cloudflare::new().payments_arc(payments);§Verification
Request shaping, error mapping, and webhook verification (a tampered signature
and a stale timestamp are both refused) are unit-tested against a scripted
HttpClient. The live path against Stripe is needs-human: it needs real
test-mode keys, which do not live in the repo.
MIT. Built in the open for Cratefield, a Factory Zero venture.
Structs§
Constants§
- WEBHOOK_
TOLERANCE - How much clock skew a webhook timestamp may have before it is rejected. Stripe recommends five minutes.