[GH-ISSUE #8920] Stripe subscriptions can’t be created using only metered usage. #28559

Closed
opened 2026-04-17 20:00:36 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @zhouzi on GitHub (Apr 2, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8920

Originally assigned to: @bytaesu on GitHub.

Is this suited for github?

  • Yes, this is suited for github

Reproduction

Try upgrading to a subscription with metered usage

Current vs. Expected behavior

I expected the user to subscribe but it fails with an error message:

Quantity should not be specified where usage_type is metered. Remove quantity from line_items[0]

What version of Better Auth are you using?

^1.5.4

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6000",
    "release": "23.6.0",
    "cpuCount": 10,
    "cpuModel": "Apple M1 Pro",
    "totalMemory": "16.00 GB",
    "freeMemory": "0.22 GB"
  },
  "node": {
    "version": "v22.21.1",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.4"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "Unknown",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Client

Auth config (if applicable)


Additional context

This issue was initially reported in #4025 before being automatically closed and then locked. Not sure what's the team's stance on this.

It's also related to #4282

Originally created by @zhouzi on GitHub (Apr 2, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8920 Originally assigned to: @bytaesu on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### Reproduction Try upgrading to a subscription with metered usage ### Current vs. Expected behavior I expected the user to subscribe but it fails with an error message: > Quantity should not be specified where usage_type is `metered`. Remove quantity from `line_items[0]` ### What version of Better Auth are you using? ^1.5.4 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6000", "release": "23.6.0", "cpuCount": 10, "cpuModel": "Apple M1 Pro", "totalMemory": "16.00 GB", "freeMemory": "0.22 GB" }, "node": { "version": "v22.21.1", "env": "development" }, "packageManager": { "name": "npm", "version": "10.9.4" }, "frameworks": null, "databases": null, "betterAuth": { "version": "Unknown", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript ``` ### Additional context This issue was initially reported in #4025 before being automatically closed and then locked. Not sure what's the team's stance on this. It's also related to #4282
GiteaMirror added the bug label 2026-04-17 20:00:36 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Apr 2, 2026):

Hi @zhouzi! I'm Dosu and I’m helping the better-auth team.

This is a confirmed limitation in the Stripe plugin — the upgradeSubscription endpoint always sets quantity on the base price line item, with no conditional logic to omit it for metered usage plans [1]. This is the same issue reported in #4025.

The root cause is in packages/stripe/src/routes.ts, where the checkout session line items are constructed:

line_items: [
  ...(!isSeatOnlyPlan
    ? [{ price: priceIdToUse, quantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1 }]
    : []),
  ...(isAutoManagedSeats ? [{ price: plan.seatPriceId, quantity: memberCount }] : []),
  ...(plan.lineItems ?? []),
]

The base price item always includes quantity, which Stripe rejects for metered usage prices.

Possible workaround: You can try leveraging the lineItems array on your plan config while making the plan a "seat-only" plan (setting seatPriceId equal to priceId). When isSeatOnlyPlan is true, the base price item is skipped, and you can provide your metered price via lineItems without a quantity [2]:

{
  name: "metered-plan",
  priceId: "price_dummy",
  seatPriceId: "price_dummy", // Makes it a seat-only plan, skipping the base price item
  lineItems: [
    { price: "price_your_metered_id" } // No quantity — Stripe allows this for metered prices
  ]
}

That said, this is a hack and may not work cleanly with the upgrade logic. A proper fix would involve adding a metered flag to StripePlan and conditionally omitting quantity on the base price item [3]. The change is fairly straightforward in routes.ts [4]:

- { price: priceIdToUse, quantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1 }
+ { price: priceIdToUse, ...(plan.metered ? {} : { quantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1 }) }

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:4179016197 --> @dosubot[bot] commented on GitHub (Apr 2, 2026): <!-- Greeting --> Hi @zhouzi! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a confirmed limitation in the Stripe plugin — the `upgradeSubscription` endpoint always sets `quantity` on the base price line item, with no conditional logic to omit it for metered usage plans [[1]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts#L1079-L1082). This is the same issue reported in #4025. The root cause is in `packages/stripe/src/routes.ts`, where the checkout session line items are constructed: ```typescript line_items: [ ...(!isSeatOnlyPlan ? [{ price: priceIdToUse, quantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1 }] : []), ...(isAutoManagedSeats ? [{ price: plan.seatPriceId, quantity: memberCount }] : []), ...(plan.lineItems ?? []), ] ``` The base price item **always** includes `quantity`, which Stripe rejects for metered usage prices. **Possible workaround:** You can try leveraging the `lineItems` array on your plan config while making the plan a "seat-only" plan (setting `seatPriceId` equal to `priceId`). When `isSeatOnlyPlan` is true, the base price item is skipped, and you can provide your metered price via `lineItems` without a quantity [[2]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts#L1077-L1091): ```typescript { name: "metered-plan", priceId: "price_dummy", seatPriceId: "price_dummy", // Makes it a seat-only plan, skipping the base price item lineItems: [ { price: "price_your_metered_id" } // No quantity — Stripe allows this for metered prices ] } ``` That said, this is a hack and may not work cleanly with the upgrade logic. A proper fix would involve adding a `metered` flag to `StripePlan` and conditionally omitting `quantity` on the base price item [[3]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/types.ts). The change is fairly straightforward in `routes.ts` [[4]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts#L1081): ```diff - { price: priceIdToUse, quantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1 } + { price: priceIdToUse, ...(plan.metered ? {} : { quantity: isAutoManagedSeats ? 1 : ctx.body.seats || 1 }) } ``` <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=brand-link).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=ab44bba5-a337-44cb-b5a7-6edf797e600d&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=ab44bba5-a337-44cb-b5a7-6edf797e600d&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=ab44bba5-a337-44cb-b5a7-6edf797e600d&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=ab44bba5-a337-44cb-b5a7-6edf797e600d&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=ab44bba5-a337-44cb-b5a7-6edf797e600d&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=ab44bba5-a337-44cb-b5a7-6edf797e600d&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=ab44bba5-a337-44cb-b5a7-6edf797e600d&feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=chat-badge)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20a%20confirmed%20limitation%20in%20the%20Stripe%20plugin%20%E2%80%94%20the%20%60upgradeSubscription%60%20endpoint%20always%20sets%20%60quantity%60%20on%20the%20base%20price%20line%20item%2C%20with%20no%20conditional%20logic%20to%20omit%20it%20for%20metered%20usage%20plans%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts%23L1079-L1082%29.%20This%20is%20the%20same%20issue%20reported%20in%20%234025.%0A%0AThe%20root%20cause%20is%20in%20%60packages/stripe/src/routes.ts%60%2C%20where%20the%20checkout%20session%20line%20items%20are%20constructed%3A%0A%0A%60%60%60typescript%0Aline_items%3A%20%5B%0A%20%20...%28%21isSeatOnlyPlan%0A%20%20%20%20%3F%20%5B%7B%20price%3A%20priceIdToUse%2C%20quantity%3A%20isAutoManagedSeats%20%3F%201%20%3A%20ctx.body.seats%20%7C%7C%201%20%7D%5D%0A%20%20%20%20%3A%20%5B%5D%29%2C%0A%20%20...%28isAutoManagedSeats%20%3F%20%5B%7B%20price%3A%20plan.seatPriceId%2C%20quantity%3A%20memberCount%20%7D%5D%20%3A%20%5B%5D%29%2C%0A%20%20...%28plan.lineItems%20%3F%3F%20%5B%5D%29%2C%0A%5D%0A%60%60%60%0A%0AThe%20base%20price%20item%20%2A%2Aalways%2A%2A%20includes%20%60quantity%60%2C%20which%20Stripe%20rejects%20for%20metered%20usage%20prices.%0A%0A%2A%2APossible%20workaround%3A%2A%2A%20You%20can%20try%20leveraging%20the%20%60lineItems%60%20array%20on%20your%20plan%20config%20while%20making%20the%20plan%20a%20%22seat-only%22%20plan%20%28setting%20%60seatPriceId%60%20equal%20to%20%60priceId%60%29.%20When%20%60isSeatOnlyPlan%60%20is%20true%2C%20the%20base%20price%20item%20is%20skipped%2C%20and%20you%20can%20provide%20your%20metered%20price%20via%20%60lineItems%60%20without%20a%20quantity%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts%23L1077-L1091%29%3A%0A%0A%60%60%60typescript%0A%7B%0A%20%20name%3A%20%22metered-plan%22%2C%0A%20%20priceId%3A%20%22price_dummy%22%2C%0A%20%20seatPriceId%3A%20%22price_dummy%22%2C%20//%20Makes%20it%20a%20seat-only%20plan%2C%20skipping%20the%20base%20price%20item%0A%20%20lineItems%3A%20%5B%0A%20%20%20%20%7B%20price%3A%20%22price_your_metered_id%22%20%7D%20//%20No%20quantity%20%E2%80%94%20Stripe%20allows%20this%20for%20metered%20prices%0A%20%20%5D%0A%7D%0A%60%60%60%0A%0AThat%20said%2C%20this%20is%20a%20hack%20and%20may%20not%20work%20cleanly%20with%20the%20upgrade%20logic.%20A%20proper%20fix%20would%20involve%20adding%20a%20%60metered%60%20flag%20to%20%60StripePlan%60%20and%20conditionally%20omitting%20%60quantity%60%20on%20the%20base%20price%20item%20%5B%5B3%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/types.ts%29.%20The%20change%20is%20fairly%20straightforward%20in%20%60routes.ts%60%20%5B%5B4%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts%23L1081%29%3A%0A%0A%60%60%60diff%0A-%20%7B%20price%3A%20priceIdToUse%2C%20quantity%3A%20isAutoManagedSeats%20%3F%201%20%3A%20ctx.body.seats%20%7C%7C%201%20%7D%0A%2B%20%7B%20price%3A%20priceIdToUse%2C%20...%28plan.metered%20%3F%20%7B%7D%20%3A%20%7B%20quantity%3A%20isAutoManagedSeats%20%3F%201%20%3A%20ctx.body.seats%20%7C%7C%201%20%7D%29%20%7D%0A%60%60%60)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=join-discord)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/8920)
Author
Owner

@zhouzi commented on GitHub (Apr 2, 2026):

@dosu I am happy to go with the workaround for now but are there any undesirable side effects of tricking better-auth into thinking it's a seat only plan?

<!-- gh-comment-id:4179067410 --> @zhouzi commented on GitHub (Apr 2, 2026): @dosu I am happy to go with the workaround for now but are there any undesirable side effects of tricking better-auth into thinking it's a seat only plan?
Author
Owner

@dosubot[bot] commented on GitHub (Apr 2, 2026):

Good question — after digging deeper, I actually want to walk back that workaround. The seat-only trick has a critical side effect that would likely break metered billing entirely.

When seatPriceId === priceId and the subscription is for an organization, Better Auth enables automatic member count synchronization [1]. This means:

  • Every time a member is added/removed from the organization, the plugin calls stripe.subscriptions.update() and sets the subscription item's quantity to the current member count [2].
  • All webhook handlers (checkout.session.completed, customer.subscription.updated, etc.) resolve and store quantities as "seats" based on member count [3].
  • Plan upgrades/downgrades also force the quantity to memberCount [4].

For a metered usage plan, this would mean Stripe either rejects the quantity update (since metered items don't accept quantity), or you'd get constant errors from the member sync hooks trying to set quantities on your metered subscription item.

Additionally, the workaround requires the organization plugin — the system validates this and logs an error if seatPriceId is set without it [5]. So it's not something you can use for user-level subscriptions at all.

Bottom line: The seat-only workaround isn't safe for metered usage. The most reliable path right now is either:

  1. Handle metered subscriptions via the Stripe SDK directly, using Better Auth only for auth — this is what the maintainers recommended in #4282 [6].
  2. Patch the plugin locally to conditionally omit quantity for metered plans. The change is small — in routes.ts around line 1081 [7], wrap the quantity in a conditional based on a metered flag or similar.

Sorry for the misleading suggestion earlier — the side effects run deeper than I initially accounted for.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:4179124680 --> @dosubot[bot] commented on GitHub (Apr 2, 2026): <!-- Answer --> Good question — after digging deeper, I actually want to **walk back that workaround**. The seat-only trick has a critical side effect that would likely break metered billing entirely. When `seatPriceId === priceId` and the subscription is for an organization, Better Auth enables **automatic member count synchronization** [[1]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/index.ts#L176-L254). This means: - Every time a member is added/removed from the organization, the plugin calls `stripe.subscriptions.update()` and sets the subscription item's `quantity` to the current member count [[2]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/index.ts#L238-L245). - All webhook handlers (`checkout.session.completed`, `customer.subscription.updated`, etc.) resolve and store quantities as "seats" based on member count [[3]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/hooks.ts). - Plan upgrades/downgrades also force the quantity to `memberCount` [[4]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts#L705-L714). For a metered usage plan, this would mean Stripe either rejects the quantity update (since metered items don't accept quantity), or you'd get constant errors from the member sync hooks trying to set quantities on your metered subscription item. Additionally, the workaround **requires the organization plugin** — the system validates this and logs an error if `seatPriceId` is set without it [[5]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/index.ts#L61-L83). So it's not something you can use for user-level subscriptions at all. **Bottom line:** The seat-only workaround isn't safe for metered usage. The most reliable path right now is either: 1. **Handle metered subscriptions via the Stripe SDK directly**, using Better Auth only for auth — this is what the maintainers recommended in #4282 [[6]](https://github.com/better-auth/better-auth/issues/4282#issuecomment-4148041133). 2. **Patch the plugin locally** to conditionally omit `quantity` for metered plans. The change is small — in `routes.ts` around line 1081 [[7]](https://github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts#L1079-L1082), wrap the quantity in a conditional based on a `metered` flag or similar. Sorry for the misleading suggestion earlier — the side effects run deeper than I initially accounted for. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=brand-link).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=6d8d52a8-178c-4723-a06c-f86e25ac9875&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=6d8d52a8-178c-4723-a06c-f86e25ac9875&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=6d8d52a8-178c-4723-a06c-f86e25ac9875&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=6d8d52a8-178c-4723-a06c-f86e25ac9875&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=6d8d52a8-178c-4723-a06c-f86e25ac9875&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=6d8d52a8-178c-4723-a06c-f86e25ac9875&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=6d8d52a8-178c-4723-a06c-f86e25ac9875&feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=chat-badge)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Good%20question%20%E2%80%94%20after%20digging%20deeper%2C%20I%20actually%20want%20to%20%2A%2Awalk%20back%20that%20workaround%2A%2A.%20The%20seat-only%20trick%20has%20a%20critical%20side%20effect%20that%20would%20likely%20break%20metered%20billing%20entirely.%0A%0AWhen%20%60seatPriceId%20%3D%3D%3D%20priceId%60%20and%20the%20subscription%20is%20for%20an%20organization%2C%20Better%20Auth%20enables%20%2A%2Aautomatic%20member%20count%20synchronization%2A%2A%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/index.ts%23L176-L254%29.%20This%20means%3A%0A%0A-%20Every%20time%20a%20member%20is%20added/removed%20from%20the%20organization%2C%20the%20plugin%20calls%20%60stripe.subscriptions.update%28%29%60%20and%20sets%20the%20subscription%20item%27s%20%60quantity%60%20to%20the%20current%20member%20count%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/index.ts%23L238-L245%29.%0A-%20All%20webhook%20handlers%20%28%60checkout.session.completed%60%2C%20%60customer.subscription.updated%60%2C%20etc.%29%20resolve%20and%20store%20quantities%20as%20%22seats%22%20based%20on%20member%20count%20%5B%5B3%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/hooks.ts%29.%0A-%20Plan%20upgrades/downgrades%20also%20force%20the%20quantity%20to%20%60memberCount%60%20%5B%5B4%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts%23L705-L714%29.%0A%0AFor%20a%20metered%20usage%20plan%2C%20this%20would%20mean%20Stripe%20either%20rejects%20the%20quantity%20update%20%28since%20metered%20items%20don%27t%20accept%20quantity%29%2C%20or%20you%27d%20get%20constant%20errors%20from%20the%20member%20sync%20hooks%20trying%20to%20set%20quantities%20on%20your%20metered%20subscription%20item.%0A%0AAdditionally%2C%20the%20workaround%20%2A%2Arequires%20the%20organization%20plugin%2A%2A%20%E2%80%94%20the%20system%20validates%20this%20and%20logs%20an%20error%20if%20%60seatPriceId%60%20is%20set%20without%20it%20%5B%5B5%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/index.ts%23L61-L83%29.%20So%20it%27s%20not%20something%20you%20can%20use%20for%20user-level%20subscriptions%20at%20all.%0A%0A%2A%2ABottom%20line%3A%2A%2A%20The%20seat-only%20workaround%20isn%27t%20safe%20for%20metered%20usage.%20The%20most%20reliable%20path%20right%20now%20is%20either%3A%0A%0A1.%20%2A%2AHandle%20metered%20subscriptions%20via%20the%20Stripe%20SDK%20directly%2A%2A%2C%20using%20Better%20Auth%20only%20for%20auth%20%E2%80%94%20this%20is%20what%20the%20maintainers%20recommended%20in%20%234282%20%5B%5B6%5D%5D%28https%3A//github.com/better-auth/better-auth/issues/4282%23issuecomment-4148041133%29.%0A2.%20%2A%2APatch%20the%20plugin%20locally%2A%2A%20to%20conditionally%20omit%20%60quantity%60%20for%20metered%20plans.%20The%20change%20is%20small%20%E2%80%94%20in%20%60routes.ts%60%20around%20line%201081%20%5B%5B7%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/4742f349dcae607fe5c79a32196ab8c03977500b/packages/stripe/src/routes.ts%23L1079-L1082%29%2C%20wrap%20the%20quantity%20in%20a%20conditional%20based%20on%20a%20%60metered%60%20flag%20or%20similar.%0A%0ASorry%20for%20the%20misleading%20suggestion%20earlier%20%E2%80%94%20the%20side%20effects%20run%20deeper%20than%20I%20initially%20accounted%20for.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=join-discord)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/8920)
Author
Owner

@bytaesu commented on GitHub (Apr 3, 2026):

Hi @zhouzi,

I can't check your auth config, but you can use a basic (non-usage-based) priceId and add metered billing via lineItems. If you want pure metered billing, set the base price to free for now.

I'll also look into supporting pure metered billing directly.

<!-- gh-comment-id:4181428283 --> @bytaesu commented on GitHub (Apr 3, 2026): Hi @zhouzi, I can't check your auth config, but you can use a basic (non-usage-based) priceId and add metered billing via lineItems. If you want pure metered billing, set the base price to free for now. I'll also look into supporting pure metered billing directly.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28559