An F# library to connect to the Stripe API, including code generators to update the model and requests. Targets .NET Standard 2.0 and .NET Standard 2.1.
Disclaimer: FunStripe is not an official Stripe product and is not affiliated with Stripe, Inc.
See CHANGELOG.md for version history.
Get the latest version of FunStripe.Core from NuGet.
FunStripe types are organised into per-domain namespaces:
- Response models live under
Stripe.{Domain}(e.g.Stripe.PaymentMethod,Stripe.Customer,Stripe.Invoice). - Request option records live under
StripeRequest.{Domain}(e.g.StripeRequest.Payment,StripeRequest.Customers).
Open the domains you need:
open Stripe.PaymentMethod
open StripeRequest.PaymentHere's an example of how to create a new payment method:
let settings = RestApi.StripeApiSettings.New(apiKey = Config.StripeTestApiKey)
let getNewPaymentMethod () =
asyncResult {
return!
PaymentMethods.CreateOptions.New(
card = Choice2Of2 (PaymentMethods.Create'CardTokenParams.New("tok_visa")),
type' = PaymentMethods.Create'Type.Card
)
|> PaymentMethods.Create settings
}The result value is an AsyncResult<PaymentMethod,ErrorResponse>, giving you the requested object in case of success or a detailed error response if not.
The general format of API requests is <module>.<method> settings options.
To instantiate the settings you need to pass in your Stripe API key. Having local rather than global settings allows you to use different keys for different Stripe accounts if you need to.
For mutating requests (e.g. creating a charge), Stripe supports an Idempotency-Key header to safely retry requests without accidentally performing the same operation twice. You should supply a deterministic key derived from the operation — typically a stable GUID you generate and store alongside the operation.
You can set an idempotency key when creating settings:
let settings = RestApi.StripeApiSettings.New(apiKey = "<your Stripe API key>", idempotencyKey = "my-unique-operation-id")Or, more conveniently, use WithIdempotencyKey to apply a key per-request without affecting your base settings:
let createPaymentMethod idempotencyKey =
asyncResult {
return!
PaymentMethods.CreateOptions.New(
card = Choice2Of2 (PaymentMethods.Create'CardTokenParams.New("tok_visa")),
type' = PaymentMethods.Create'Type.Card
)
|> PaymentMethods.Create (settings.WithIdempotencyKey(idempotencyKey))
}The idempotency key is propagated transparently through the AsyncResult-based wrappers. Keys are never auto-generated — the caller always controls key derivation to guarantee retry safety.
Config.StripeTestApiKey reads from the STRIPE_TEST_API_KEY environment variable. Set it in your shell, a .env file, or a CI secret. For production use, inject the live key via your host's environment/configuration system rather than embedding it in source code.
The options can be provided using record notation or if there are many uninitialised properties you can use the static New method to instantiate the record more efficiently.
All list endpoints return a StripeList<'T> that includes the current page of items alongside the pagination metadata:
let listOptions = Customers.ListOptions.New(limit = 10)
let! page = Customers.List settings listOptions
// page.Data — the current page of Customer records
// page.HasMore — true if more pages are available
// page.Url — the endpoint URLTo automatically fetch all pages and combine them into a single list, use RestApi.listAllAsync:
let! allCustomers =
RestApi.listAllAsync
Customers.List
(fun id opts -> { opts with StartingAfter = Some id })
(fun (c: Customer) -> c.Id)
settings
(Customers.ListOptions.New())
// allCustomers is Result<Customer list, ErrorResponse>To handle a Stripe webhook (e.g. to fulfil an order after checkout.session.completed), verify the signature with WebhookSigning, deserialise the payload into a typed Event, and extract the resource from the event's raw data.object fragment with Util.deserialiseRaw:
open FunStripe
open Stripe.Event
let handleWebhook (signingSecret: string) (signatureHeader: string) (rawBody: string) =
match WebhookSigning.verifyWithDefaultTolerance signingSecret rawBody signatureHeader with
| Error e -> Error $"Invalid signature: {e}"
| Ok () ->
let event = Util.deserialise<Event> rawBody
match event.Type with
| EventType.CheckoutSessionCompleted ->
let session = event.Data.Object |> Util.deserialiseRaw<Stripe.Checkout.CheckoutSession>
// fulfil the order for session.Id here
Ok ()
| _ ->
Ok () // ignore event types you don't handleEvent.Data.Object is a RawJson value that preserves the fragment exactly as Stripe sent it; RawJson.Value exposes the raw text if you need it. Event types added by Stripe after this library version was generated deserialise to EventType.UnknownEnumValue "the.event.type" rather than failing, so unrecognised events can be ignored or logged safely.
FunStripe targets a specific Stripe API date-version. The version this build was generated from is exposed as a constant:
Config.DefaultStripeApiVersion // e.g. "2026-10-28.endive"The generator takes it from the spec's info.version, so it always matches the generated models. It is also embedded as an assembly-level attribute (Config.StripeApiVersionAttribute) and in the NuGet package tags, giving downstream projects an auditable record of the API surface they are compiled against.
By default, FunStripe does not send a Stripe-Version request header, so Stripe uses the version pinned to your account. If you want every request to be explicitly tied to the library's target API version — useful when upgrading or for forward-compatibility testing — pass it through StripeApiSettings:
let settings =
RestApi.StripeApiSettings.New(
apiKey = Config.StripeTestApiKey,
stripeVersion = Config.DefaultStripeApiVersion
)To test against a newer Stripe API version before upgrading the library, supply the target date directly:
let settings =
RestApi.StripeApiSettings.New(
apiKey = Config.StripeTestApiKey,
stripeVersion = "2026-10-28.endive" // override for forward-compatibility testing
)See CHANGELOG.md for the full FunStripe version → Stripe API version compatibility table.
FunStripe.Core.Fable was discontinued in 3.0.0. It never transpiled with the Fable compiler (the
shared source depends on System.Text.Json attributes and .NET reflection), and its HTTP layer
required XMLHttpRequest, which Node.js doesn't provide. Existing 2.x versions stay on NuGet
but are deprecated. For F# on Node.js, call Stripe's official stripe npm package through Fable
interop.
FunStripe.Core is generated as a set of per-domain F# files rather than a single monolith:
src/Stripe/{Domain}.fs— response models grouped by Stripe domain (e.g.PaymentMethod.fs,Customer.fs,Invoice.fs). Each file declaresnamespace Stripe.{Domain}.src/StripeRequest/{Domain}.fs— request option records and call-site modules (e.g.Payment.fs,Customers.fs). Each file declaresnamespace StripeRequest.{Domain}.src/StripeIds.fs— phantom-typedStripeId<'phantom>andStripeList<'T>, auto-opened so the wrappers are available everywhere without an explicitopen.
The two folders are wired into the project via auto-generated MSBuild props files (src/Stripe/Stripe.Modular.props and src/StripeRequest/StripeRequest.Modular.props), imported by FunStripe.Core.fsproj. Compile order is determined by the generator from the schema dependency graph.
By cloning the source repository, you can regenerate the modular files using the FunStripe.Generator console tool:
dotnet run --project tools/FunStripe.Generator -- \
--spec spec/stripe-openapi-2026-10-28.endive.json \
--output src \
--version 3.2.0All three arguments are optional. The spec and version default to the StripeApiVersion and FunStripeVersion in Directory.Build.props (the values shown above for this release), and the output to src. The generator emits:
src/StripeIds.fssrc/Stripe/{Domain}.fsplussrc/Stripe/Stripe.Modular.propssrc/StripeRequest/{Domain}.fsplussrc/StripeRequest/StripeRequest.Modular.props
The spec/ directory contains several historical Stripe OpenAPI specifications. To regenerate against a different version, pass its path via --spec. To use a spec version not already included, download it from the link in the References section and place it in the spec/ directory with the API version in the filename.
A Regenerate source files GitHub Actions workflow (.github/workflows/regenerate.yml) is also provided. Trigger it manually from the Actions tab; it will run the generator and open a pull request if any files changed.
The generator sources live under tools/FunStripe.Generator/ (notably ModelBuilderModular.fs, RequestBuilderAST.fs, and StripeIdsBuilder.fs); customise emission by editing those files. Before generating, the generator corrects the places where Stripe's spec disagrees with its API (SpecPatches.fs); the spec files themselves stay unchanged.
Stripe.net is the official Stripe library for .NET, maintained by the Stripe team. FunStripe is a community-maintained library written specifically for F#. Here are some reasons why an F# developer might prefer FunStripe:
FunStripe is written in F# for F# developers. API responses are returned as AsyncResult<'T, ErrorResponse>, which integrates naturally with F# async workflows and computation expressions, letting you handle success and error cases without exceptions using standard F# pattern matching. Stripe.net, being a C# library, uses exceptions for error handling and does not provide the same level of F# ergonomics.
All enumerated values (field types, event types, error codes, webhook event kinds) are represented as F# discriminated unions rather than plain strings. This enables exhaustive pattern matching and gives compile-time confidence that all cases are handled. In Stripe.net, these values are typically plain strings or string constants.
FunStripe aims to cover the full Stripe API surface and tracks each Stripe API release version. The FunStripe.Generator console tool regenerates the per-domain Stripe.{Domain} and StripeRequest.{Domain} files directly from the Stripe OpenAPI specification, making it straightforward to adopt new API versions. Stripe.net's model is maintained by Stripe rather than derived from the public OpenAPI spec, so the generation process is not available to contributors.
| Feature | FunStripe | Stripe.net |
|---|---|---|
| Language | F# | C# |
| Error handling | AsyncResult |
Exceptions |
| Enum types | Discriminated unions | Strings |
| Webhook event types | Discriminated unions | Strings |
| API coverage | Full | Full |
| Code generation | Yes (from OpenAPI spec) | No |
| Maintained by | Community (F# focused) | Stripe |
If you need official Stripe support or are working primarily in C#, Stripe.net is the right choice. If you want idiomatic F# with strong typing and tracked Stripe API versions, FunStripe is a great fit.