Read one version-pinned SDK workflow, its negative case, and the exact claim that workflow supports.
Install the client version exercised by this repository:
pnpm add stripe@22.5.0Point the official client at one instance-scoped URL. This adapter is the complete first-party
example; maxNetworkRetries: 0 keeps retry behavior outside the claim.
import { createStripeSdkFetch } from "@localhost2137/stripe";
import Stripe from "stripe";
export interface LocalStripeConnection {
readonly apiUrl: string;
readonly secretKey: string;
}
/** Builds the official Stripe SDK against one localhost2137 instance-scoped account. */
export function createLocalStripe(connection: LocalStripeConnection): Stripe {
return new Stripe(connection.secretKey, {
httpClient: Stripe.createFetchHttpClient(createStripeSdkFetch(connection.apiUrl)),
maxNetworkRetries: 0,
});
}The checked workflow uses normal SDK calls for a success, a provider-shaped error, and a later state transition:
import { createTestRuntime } from "localhost2137/testing";
import { afterEach, describe, expect, it } from "vitest";
import config from "../localhost.config.js";
import { createLocalStripe } from "../src/local-stripe.js";
const runtimes: Array<Awaited<ReturnType<typeof createTestRuntime>>> = [];
afterEach(async () => {
await Promise.all(runtimes.splice(0).map((runtime) => runtime.close()));
});
describe("official Stripe SDK", () => {
it("creates and renews a subscription through normal SDK calls", async () => {
const runtime = await createTestRuntime({ config, port: 0, storage: "temporary" });
runtimes.push(runtime);
const instance = await runtime.createInstance();
try {
const product = await instance.stripe.createProduct({ name: "Pro" });
const price = await instance.stripe.createPrice({
productId: product.id,
unitAmount: 2_500,
});
const client = createLocalStripe(instance.stripe.connection);
const customer = await client.customers.create({
email: "ada@example.test",
name: "Ada",
});
await expect(client.customers.retrieve("cus_missing")).rejects.toMatchObject({
code: "customer_missing",
statusCode: 404,
type: "StripeInvalidRequestError",
});
const subscription = await client.subscriptions.create({
customer: customer.id,
items: [{ price: price.id }],
});
expect(subscription).toMatchObject({
customer: customer.id,
id: "sub_000001",
latest_invoice: "in_000001",
status: "active",
});
await instance.clock.advance("30d");
const invoices = await client.invoices.list({
limit: 10,
subscription: subscription.id,
});
expect(invoices.data.map(({ id }) => id)).toEqual(["in_000001", "in_000002"]);
expect(invoices.data[1]).toMatchObject({
amount_paid: 2_500,
customer: customer.id,
paid: true,
subscription: subscription.id,
});
} finally {
await instance.destroy();
}
});
});pnpm --filter @localhost2137/example-stripe-sdk testWith Stripe Node 22.5.0, this checked path establishes all of the following together:
404 StripeInvalidRequestError with the documented
local customer_missing code;The test uses privileged operations only to arrange a product and price. It does not claim that the application-facing Stripe API created those two resources.
The workflow says nothing about customer listing, subscription cancellation, another Stripe SDK version, every field on the returned objects, webhook delivery, retry policy, pagination, external account configuration, or behavior on Stripe's servers.
Do not turn “unverified” into “unsupported.” Unsupported behavior is deliberately rejected or omitted. Unverified behavior simply has no claim here. Check the Stripe support reference and add an application-level test for every extra path your code depends on.
| Layer | Evidence to look for |
|---|---|
| Routing and transport | Exact methods, paths, query or body encoding, authentication, status, and error envelope |
| Data | Fields, defaults, IDs, validation, ordering, and pagination used by the application |
| State | The sequence connecting one request to later reads or writes |
| Time and delivery | Clock transitions, event identity, signatures, retries, duplicates, and ordering |
| Client | The locked SDK or adapter version that exercised the path |
An endpoint name is not a compatibility claim. A passing happy path does not establish its errors. A correct error envelope does not establish retry behavior. Keep each statement no broader than its executable evidence.
The generic plugin contract verifies lifecycle, isolation, persistence, routing, and runtime integration. Provider fidelity comes from plugin-specific protocol, SDK, state, event, and negative tests. Operations and emulated APIs explains why control operations and provider routes are separate evidence; Choose the right test boundary explains which claims still need provider-facing checks.