feat(paykit): add stripe managedPayments and apiVersion options#111
Conversation
|
@aristotl-dylan is attempting to deploy a commit to the maxktz Team on Vercel. A member of the Team first needs to authorize it. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds Stripe API version pinning and an optional managed payments preview feature to the Stripe provider: new exported Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant Provider as Stripe Provider
participant SDK as Stripe SDK
participant API as Stripe API
Dev->>Provider: stripe({ managedPayments: true, apiVersion: "2026-03-04.preview", ... })
Provider->>Provider: Validate apiVersion ends with ".preview" and meets minimum
Provider->>SDK: Instantiate StripeSdk client with apiVersion
Note over Dev,Provider: Later — create subscription checkout
Dev->>Provider: createSubscriptionCheckout(...)
Provider->>Provider: Check options.managedPayments
Provider->>API: checkout.sessions.create({ ..., managed_payments: { enabled: true } })
API-->>Provider: Session created
Provider-->>Dev: Return session response
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Adds `apiVersion` and `managedPayments` options to the Stripe provider
config. `apiVersion` defaults to the pinned `PAYKIT_STRIPE_API_VERSION`
so upstream Stripe changes don't silently break integrations. Setting
`managedPayments: true` adds `managed_payments: { enabled: true }` to
subscription checkout sessions and requires a preview apiVersion,
validated at runtime construction.
Refs getpaykit#109
75583de to
b322e0b
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/paykit/src/providers/stripe.ts (1)
909-920: Consider moving themanagedPaymentsvalidation to a shared helper to protect all construction paths.The preview-version guard (lines 910–916) only runs in
createStripeRuntime. While the public library API is protected—createStripeRuntimeis the documented entry point—the exportedcreateStripeProviderfactory function can still be called directly withmanagedPayments: trueand an incompatible API version without validation. Direct callers (whether tests or future internal code) would then pass invalid config tocheckout.sessions.create(...)without the clearerPROVIDER_INVALID_CONFIGerror.To improve encapsulation, extract the guard into a shared validation function called by both
createStripeRuntimeandcreateStripeProvider, or makecreateStripeProvidermodule-local if it is only intended for internal use.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/paykit/src/providers/stripe.ts` around lines 909 - 920, The managedPayments/API-version guard is only performed in createStripeRuntime, so callers of createStripeProvider can bypass the STRIPE_MANAGED_PAYMENTS_API_VERSION check; extract the validation into a shared helper (e.g., validateStripeManagedPayments(options)) that checks options.managedPayments against options.apiVersion (comparing to STRIPE_MANAGED_PAYMENTS_API_VERSION) and throws PayKitError.from("BAD_REQUEST", PAYKIT_ERROR_CODES.PROVIDER_INVALID_CONFIG, ...) when mismatched, then call that helper from both createStripeRuntime and createStripeProvider (or alternatively make createStripeProvider non-exported if it should be internal-only).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/paykit/src/providers/stripe.ts`:
- Around line 909-920: The managedPayments/API-version guard is only performed
in createStripeRuntime, so callers of createStripeProvider can bypass the
STRIPE_MANAGED_PAYMENTS_API_VERSION check; extract the validation into a shared
helper (e.g., validateStripeManagedPayments(options)) that checks
options.managedPayments against options.apiVersion (comparing to
STRIPE_MANAGED_PAYMENTS_API_VERSION) and throws PayKitError.from("BAD_REQUEST",
PAYKIT_ERROR_CODES.PROVIDER_INVALID_CONFIG, ...) when mismatched, then call that
helper from both createStripeRuntime and createStripeProvider (or alternatively
make createStripeProvider non-exported if it should be internal-only).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: d80565b1-6fea-4dc4-bfaf-b523bc3223ec
📒 Files selected for processing (7)
landing/content/docs/providers/stripe.mdxpackages/paykit/src/core/errors.tspackages/paykit/src/index.tspackages/paykit/src/providers/__tests__/stripe.test.tspackages/paykit/src/providers/provider.tspackages/paykit/src/providers/stripe.tspackages/stripe/src/stripe-provider.ts
✅ Files skipped from review due to trivial changes (4)
- packages/paykit/src/index.ts
- packages/paykit/src/core/errors.ts
- packages/stripe/src/stripe-provider.ts
- landing/content/docs/providers/stripe.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/paykit/src/providers/tests/stripe.test.ts
|
|
||
| async createSubscriptionCheckout(data) { | ||
| const sessionParams: StripeSdk.Checkout.SessionCreateParams = { | ||
| const sessionParams: StripeSdk.Checkout.SessionCreateParams & { |
There was a problem hiding this comment.
I presume once the managed payments feature is out of preview it will be generally available the SDK and we won't need this union?
There was a problem hiding this comment.
yep, but some people ask for it already, managed payment is very useful (taxes handled)
| export function createStripeRuntime(options: StripeProviderConfig): StripeRuntime { | ||
| return createStripeProvider(new StripeSdk(options.secretKey), options); | ||
| const apiVersion = options.apiVersion ?? PAYKIT_STRIPE_API_VERSION; | ||
| if (options.managedPayments && apiVersion !== STRIPE_MANAGED_PAYMENTS_API_VERSION) { |
There was a problem hiding this comment.
If Stripe update the SDK preview version and someone tries to use it, it will throw here, even though it shouldn't... I presume?
Trying to think of a better way to manage that.
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
|
yo @aristotl-dylan, why do you have an empty account? |
I made the pr with my work account on accident lol |
Replaces strict equality check with >= comparison so newer Stripe preview versions are accepted without requiring a PayKit update.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/paykit/src/providers/stripe.ts (1)
508-514:⚠️ Potential issue | 🟠 MajorEnforce the managed-payments/apiVersion invariant in
createStripeProvider()too.
createSubscriptionCheckout()usesoptions.managedPayments(line 597), but validation only runs increateStripeRuntime(). Direct callers ofcreateStripeProvider()(including the test helper at line 143) bypass validation entirely, allowingmanaged_paymentsto be enabled without the required preview API version.Suggested refactor
+function assertManagedPaymentsConfig(options: StripeProviderConfig): string { + const apiVersion = options.apiVersion ?? PAYKIT_STRIPE_API_VERSION; + if (options.managedPayments && !isManagedPaymentsApiVersion(apiVersion)) { + throw PayKitError.from( + "BAD_REQUEST", + PAYKIT_ERROR_CODES.PROVIDER_INVALID_CONFIG, + `stripe({ managedPayments: true }) requires a preview API version >= "${STRIPE_MANAGED_PAYMENTS_MIN_VERSION}.preview" (got "${apiVersion}"). Managed Payments is a Stripe preview feature; see https://docs.stripe.com/payments/managed-payments`, + ); + } + return apiVersion; +} + export function createStripeProvider( client: StripeSdk, options: StripeProviderConfig, ): StripeRuntime { + assertManagedPaymentsConfig(options); const currency = options.currency ?? "usd"; ... } export function createStripeRuntime(options: StripeProviderConfig): StripeRuntime { - const apiVersion = options.apiVersion ?? PAYKIT_STRIPE_API_VERSION; - if (options.managedPayments) { - ... - } + const apiVersion = assertManagedPaymentsConfig(options); const client = new StripeSdk(options.secretKey, { apiVersion: apiVersion as StripeSdk.LatestApiVersion, }); - return createStripeProvider(client, options); + return createStripeProvider(client, { ...options, apiVersion }); }Also applies to: 588-603, 908-927
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/paykit/src/providers/stripe.ts` around lines 508 - 514, createStripeProvider currently allows options.managedPayments to be true without verifying the required preview API version (the same invariant enforced in createStripeRuntime), which lets callers (and tests) bypass the safety check; fix by enforcing the same validation inside createStripeProvider: when options.managedPayments is true, assert options.apiVersion meets the required preview/API version (or call the existing validation used by createStripeRuntime), otherwise throw a clear error; extract the check into a shared helper (e.g., validateManagedPaymentsApiVersion) and call it from both createStripeProvider and createStripeRuntime so createSubscriptionCheckout and test helpers cannot enable managed_payments without the correct apiVersion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/paykit/src/providers/stripe.ts`:
- Around line 910-921: The preview-version validation around
options.managedPayments is too permissive; replace the endsWith + naive prefix
compare with a regex that enforces the canonical Stripe preview format and then
compare the date prefix against STRIPE_MANAGED_PAYMENTS_MIN_VERSION.
Specifically, in the stripe provider block that checks options.managedPayments
and uses apiVersion, validate apiVersion with a regex like
/^\d{4}-\d{2}-\d{2}\.preview$/ (reject otherwise), extract the YYYY-MM-DD
prefix, then compare that prefix against STRIPE_MANAGED_PAYMENTS_MIN_VERSION
(string compare is safe for zero-padded YYYY-MM-DD) and if either the format is
invalid or it’s less than the minimum throw the same PayKitError.from(...,
PAYKIT_ERROR_CODES.PROVIDER_INVALID_CONFIG, ...) with an updated message that
includes the offending apiVersion and the required minimum.
---
Outside diff comments:
In `@packages/paykit/src/providers/stripe.ts`:
- Around line 508-514: createStripeProvider currently allows
options.managedPayments to be true without verifying the required preview API
version (the same invariant enforced in createStripeRuntime), which lets callers
(and tests) bypass the safety check; fix by enforcing the same validation inside
createStripeProvider: when options.managedPayments is true, assert
options.apiVersion meets the required preview/API version (or call the existing
validation used by createStripeRuntime), otherwise throw a clear error; extract
the check into a shared helper (e.g., validateManagedPaymentsApiVersion) and
call it from both createStripeProvider and createStripeRuntime so
createSubscriptionCheckout and test helpers cannot enable managed_payments
without the correct apiVersion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 3f77a4ff-4fc5-44b8-a2c3-fc8efd6d34ab
📒 Files selected for processing (2)
packages/paykit/src/providers/__tests__/stripe.test.tspackages/paykit/src/providers/stripe.ts
| if (options.managedPayments) { | ||
| const isPreview = apiVersion.endsWith(".preview"); | ||
| const datePrefix = apiVersion.split(".preview")[0]; | ||
| const meetsMinimum = | ||
| datePrefix !== undefined && datePrefix >= STRIPE_MANAGED_PAYMENTS_MIN_VERSION; | ||
|
|
||
| if (!(isPreview && meetsMinimum)) { | ||
| throw PayKitError.from( | ||
| "BAD_REQUEST", | ||
| PAYKIT_ERROR_CODES.PROVIDER_INVALID_CONFIG, | ||
| `stripe({ managedPayments: true }) requires a preview API version >= "${STRIPE_MANAGED_PAYMENTS_MIN_VERSION}.preview" (got "${apiVersion}"). Managed Payments is a Stripe preview feature; see https://docs.stripe.com/payments/managed-payments`, | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
python - <<'PY'
MIN = "2026-03-04"
for api_version in [
"zzz.preview",
"2026-3-4.preview",
"2026-03-04.preview",
]:
is_preview = api_version.endswith(".preview")
date_prefix = api_version.split(".preview")[0]
meets_minimum = date_prefix >= MIN
print(api_version, {"accepted": is_preview and meets_minimum})
PYRepository: getpaykit/paykit
Length of output: 164
🏁 Script executed:
cd packages/paykit && wc -l src/providers/stripe.tsRepository: getpaykit/paykit
Length of output: 87
🏁 Script executed:
cd packages/paykit && sed -n '905,925p' src/providers/stripe.tsRepository: getpaykit/paykit
Length of output: 1001
🏁 Script executed:
cd packages/paykit && sed -n '1,20p' src/providers/stripe.ts | grep -E "(PAYKIT_STRIPE_API_VERSION|STRIPE_MANAGED_PAYMENTS_MIN_VERSION)"Repository: getpaykit/paykit
Length of output: 179
🏁 Script executed:
cd packages/paykit && find . -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "managedPayments" 2>/dev/nullRepository: getpaykit/paykit
Length of output: 100
🏁 Script executed:
cd packages/paykit && rg "managedPayments" src/providers/__tests__/stripe.test.ts -A 3 -B 3Repository: getpaykit/paykit
Length of output: 1663
🏁 Script executed:
# Verify the suggested fix logic
python - <<'PY'
import re
MIN = "2026-03-04"
def isManagedPaymentsApiVersion(apiVersion: str) -> bool:
match = re.match(r'^(\d{4}-\d{2}-\d{2})\.preview$', apiVersion)
datePrefix = match.group(1) if match else None
return datePrefix is not None and datePrefix >= MIN
test_cases = [
("zzz.preview", False),
("2026-3-4.preview", False),
("2026-03-04.preview", True),
("2026-03-05.preview", True),
("2026-03-03.preview", False),
("2025-10-29.clover", False),
("2026-03-04", False), # missing .preview
]
for api_version, expected in test_cases:
result = isManagedPaymentsApiVersion(api_version)
status = "✓" if result == expected else "✗"
print(f"{status} {api_version}: {result} (expected {expected})")
PYRepository: getpaykit/paykit
Length of output: 352
Extract API version validation into a regex-based predicate to reject malformed preview versions.
The current validation accepts arbitrary strings like "zzz.preview" or "2026-3-4.preview" because it only checks endsWith(".preview") and then applies lexicographic >= comparison on an unvalidated prefix. This weakens fail-fast validation and pushes the error to the Stripe SDK.
Replace the inline string operations with a regex-based validation that enforces the canonical Stripe API version format (YYYY-MM-DD.preview):
Suggested fix
+function isManagedPaymentsApiVersion(apiVersion: string): boolean {
+ const datePrefix = /^(\d{4}-\d{2}-\d{2})\.preview$/.exec(apiVersion)?.[1];
+ return datePrefix != null && datePrefix >= STRIPE_MANAGED_PAYMENTS_MIN_VERSION;
+}
+
export function createStripeRuntime(options: StripeProviderConfig): StripeRuntime {
const apiVersion = options.apiVersion ?? PAYKIT_STRIPE_API_VERSION;
if (options.managedPayments) {
- const isPreview = apiVersion.endsWith(".preview");
- const datePrefix = apiVersion.split(".preview")[0];
- const meetsMinimum =
- datePrefix !== undefined && datePrefix >= STRIPE_MANAGED_PAYMENTS_MIN_VERSION;
-
- if (!(isPreview && meetsMinimum)) {
+ if (!isManagedPaymentsApiVersion(apiVersion)) {
throw PayKitError.from(
"BAD_REQUEST",
PAYKIT_ERROR_CODES.PROVIDER_INVALID_CONFIG,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/paykit/src/providers/stripe.ts` around lines 910 - 921, The
preview-version validation around options.managedPayments is too permissive;
replace the endsWith + naive prefix compare with a regex that enforces the
canonical Stripe preview format and then compare the date prefix against
STRIPE_MANAGED_PAYMENTS_MIN_VERSION. Specifically, in the stripe provider block
that checks options.managedPayments and uses apiVersion, validate apiVersion
with a regex like /^\d{4}-\d{2}-\d{2}\.preview$/ (reject otherwise), extract the
YYYY-MM-DD prefix, then compare that prefix against
STRIPE_MANAGED_PAYMENTS_MIN_VERSION (string compare is safe for zero-padded
YYYY-MM-DD) and if either the format is invalid or it’s less than the minimum
throw the same PayKitError.from(..., PAYKIT_ERROR_CODES.PROVIDER_INVALID_CONFIG,
...) with an updated message that includes the offending apiVersion and the
required minimum.
|
|
Resolve conflicts from provider adapter layer refactor. Adapt managed payments feature to use new PaymentProvider interface. Fix logger test for time-only dev format.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/stripe/src/__tests__/stripe.test.ts (1)
133-146: Align test fixture with publicmanagedPaymentsinvariant.When
managedPaymentsis enabled, this helper should also set a previewapiVersion; otherwise the test is exercising a configuration that publicstripe(...)rejects.Proposed test-helper adjustment
function createCheckoutRuntime( createSession: ReturnType<typeof vi.fn>, managedPayments: boolean, ) { return createStripeProvider( { checkout: { sessions: { create: createSession } }, } as never, { + apiVersion: managedPayments ? "2026-03-04.preview" : undefined, managedPayments, secretKey: "sk_test_123", webhookSecret: "whsec_123", }, ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/stripe/src/__tests__/stripe.test.ts` around lines 133 - 146, The test helper createCheckoutRuntime currently calls createStripeProvider with managedPayments true but doesn't set a preview apiVersion, causing tests to exercise a config that the public stripe(...) API would reject; update createCheckoutRuntime so that when the managedPayments parameter is true it also passes an apiVersion (preview) in the provider options (i.e., include apiVersion alongside managedPayments, secretKey, webhookSecret) so the fixture matches the public managedPayments invariant expected by createStripeProvider/stripe(...).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/stripe/src/__tests__/stripe.test.ts`:
- Around line 132-217: Add a JSDoc `@see` tag linking the issue URL (e.g., `#109`)
to this regression test block: put a short JSDoc comment with `@see` and the issue
reference immediately above the describe("managed payments", ...) block so the
tests for createCheckoutRuntime/createSubscriptionCheckout and the stripe(...)
apiVersion checks are annotated as regression coverage for that issue.
---
Nitpick comments:
In `@packages/stripe/src/__tests__/stripe.test.ts`:
- Around line 133-146: The test helper createCheckoutRuntime currently calls
createStripeProvider with managedPayments true but doesn't set a preview
apiVersion, causing tests to exercise a config that the public stripe(...) API
would reject; update createCheckoutRuntime so that when the managedPayments
parameter is true it also passes an apiVersion (preview) in the provider options
(i.e., include apiVersion alongside managedPayments, secretKey, webhookSecret)
so the fixture matches the public managedPayments invariant expected by
createStripeProvider/stripe(...).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 578340e2-7dc1-4f72-8fb7-de6bfd01b9d9
📒 Files selected for processing (5)
packages/paykit/src/core/__tests__/logger.test.tspackages/paykit/src/core/errors.tspackages/stripe/src/__tests__/stripe.test.tspackages/stripe/src/index.tspackages/stripe/src/stripe-provider.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/paykit/src/core/errors.ts
- packages/stripe/src/stripe-provider.ts
|
Thank you |
Closes #109
Summary
Adds two new options to the Stripe provider config so users can pin the Stripe API version and opt into Stripe Managed Payments on subscription checkout sessions.
apiVersion(optional, defaults toPAYKIT_STRIPE_API_VERSION="2025-10-29.clover") — pins the Stripe SDK to a known-good version so account-level default drift or a Stripe SDK bump doesn't silently change behavior in user integrations. Users can override it, including to opt into preview versions.managedPayments(optional, defaultfalse) — whentrue, addsmanaged_payments: { enabled: true }to subscription checkout sessions so Stripe takes over tax calculation, payment method selection, and billing address collection. RequiresapiVersion: "2026-03-04.preview"(the preview channel Stripe currently ships the feature on); construction throws with a clear error otherwise.Why
managed_paymentsis a Stripe Checkout Session field that is only available on Stripe API version2026-03-04.previewand is not yet in thestripeNode SDK types. Previously thestripe()config surface had no way to setapiVersionat all, so users couldn't opt into this (or any other) preview feature, and the SDK fell back to the account-level default. Fixing this needed a small extension toStripeProviderOptionsplus threading the version through tonew StripeSdk(...).Because Managed Payments is preview and has strict restrictions (digital products only, no shipping, mutually exclusive with
payment_method_types,automatic_tax, Connect fields, etc.), the feature is gated behind an explicit opt-in flag and documented with a preview warning.Changes
packages/paykit/src/providers/provider.tsapiVersion?: stringandmanagedPayments?: booleantoStripeProviderOptionswith JSDocpackages/paykit/src/providers/stripe.tsPAYKIT_STRIPE_API_VERSION, threadedapiVersionintonew StripeSdk(...), added validation atcreateStripeRuntime, addedmanaged_paymentsfield to subscription checkout sessions via an inline type intersection (no@ts-expect-error)packages/paykit/src/core/errors.tsPROVIDER_INVALID_CONFIGpackages/paykit/src/index.tsPAYKIT_STRIPE_API_VERSIONpackages/stripe/src/stripe-provider.tsPAYKIT_STRIPE_API_VERSIONfrom@paykitjs/stripepackages/paykit/src/providers/__tests__/stripe.test.tslanding/content/docs/providers/stripe.mdxDesign decisions
apiVersionrather than leaving it unset. This shields users from Stripe's account-default drift (the one well-known footgun on the Stripe Node SDK). Users who need a newer or preview version pass it explicitly.managedPayments: truewithout the required previewapiVersionthrows atcreateStripeRuntimeconstruction time with a message that points at the Stripe Managed Payments docs. No auto-forcing, no silent fallback.managed_paymentsfield rather than@ts-expect-error. Preserves type safety on the rest ofSessionCreateParamsand silently absorbs the change if Stripe later addsmanaged_paymentsto official SDK types.appInfochanges in this PR (kept scope tight to the issue).Test plan
pnpm typecheckclean across all workspace packagespnpm exec vitest runinpackages/paykit/— 20/20 passing, including 4 new tests:managed_paymentsis added to the session create call when enabledmanaged_paymentsis absent when disabledcreateStripeRuntimethrows whenmanagedPayments: truebutapiVersionis missing/wrongcreateStripeRuntimesucceeds with the previewapiVersionpnpm format:checkcleanpnpm lint— no new warnings (4 preexisting inlanding/, unrelated)managedPayments: true+ preview API version (not run; would require a live account)Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests