Stripe creating subscription: Unauthorized 401

Hello, I'm running into an issue with Stripe Subscriptions whereas I cannot generate a new subscription for an user. I'm using @better-auth/strip and receive a 401 Unauthorized when I'm creating a subscription on the client. The only weird thing AFAIK is that I use the convexAdapter, so I cannot run migrations for stripe myself (but this seems unrelated to the unauthorized). I've checked the .env.local keys a dozen times. I'm running a Tanstack Start app. auth.tsx
import { betterAuth } from "better-auth";
import { APP_NAME, DEFAULT_AVATAR, DEFAULT_LANGUAGE } from "@/config/constants";
import { ConvexHttpClient } from "convex/browser";
import { convexAdapter } from "@better-auth-kit/convex";
import { stripe } from "@better-auth/stripe";
import Stripe from "stripe";

const convexClient = new ConvexHttpClient(import.meta.env.VITE_CONVEX_URL);
const stripeClient = new Stripe(import.meta.env.VITE_STRIPE_SECRET_KEY);

export const AUTH_PROVIDERS = {
CREDENTIAL: "credential",
APPLE: "apple",
GOOGLE: "google",
} as const;

export const auth = betterAuth({
appName: APP_NAME,
secret: import.meta.env.VITE_BETTER_AUTH_SECRET,
baseURL: import.meta.env.VITE_BETTER_AUTH_URL,
database: convexAdapter(convexClient),
plugins: [
stripe({
stripeClient,
stripeWebhookSecret: import.meta.env.VITE_STRIPE_WEBHOOKS_ENDPOINT_SECRET,
createCustomerOnSignUp: true,
subscription: {
enabled: true,
plans: [
{
name: "monthly", // the name of the plan, it'll be automatically lower cased when stored in the database
priceId: import.meta.env.VITE_STRIPE_MONTHLY_PRICE_ID, // the price id from stripe
},
{
name: "yearly",
priceId: import.meta.env.VITE_STRIPE_YEARLY_PRICE_ID,
},
],
},
}),
],
import { betterAuth } from "better-auth";
import { APP_NAME, DEFAULT_AVATAR, DEFAULT_LANGUAGE } from "@/config/constants";
import { ConvexHttpClient } from "convex/browser";
import { convexAdapter } from "@better-auth-kit/convex";
import { stripe } from "@better-auth/stripe";
import Stripe from "stripe";

const convexClient = new ConvexHttpClient(import.meta.env.VITE_CONVEX_URL);
const stripeClient = new Stripe(import.meta.env.VITE_STRIPE_SECRET_KEY);

export const AUTH_PROVIDERS = {
CREDENTIAL: "credential",
APPLE: "apple",
GOOGLE: "google",
} as const;

export const auth = betterAuth({
appName: APP_NAME,
secret: import.meta.env.VITE_BETTER_AUTH_SECRET,
baseURL: import.meta.env.VITE_BETTER_AUTH_URL,
database: convexAdapter(convexClient),
plugins: [
stripe({
stripeClient,
stripeWebhookSecret: import.meta.env.VITE_STRIPE_WEBHOOKS_ENDPOINT_SECRET,
createCustomerOnSignUp: true,
subscription: {
enabled: true,
plans: [
{
name: "monthly", // the name of the plan, it'll be automatically lower cased when stored in the database
priceId: import.meta.env.VITE_STRIPE_MONTHLY_PRICE_ID, // the price id from stripe
},
{
name: "yearly",
priceId: import.meta.env.VITE_STRIPE_YEARLY_PRICE_ID,
},
],
},
}),
],
Solution:
```typescript subscription: defineTable({ id: v.string(), // <-- this was the problem plan: v.string(), referenceId: v.id("user"),...
Jump to solution
28 Replies
RadiantFrog
RadiantFrogOP•3w ago
auth-client.ts
export const { signIn, signUp, deleteUser, signOut, useSession, subscription } = createAuthClient({
baseURL: import.meta.env.VITE_BETTER_AUTH_URL,
plugins: [
inferAdditionalFields<typeof auth>(),
stripeClient({
subscription: true, //if you want to enable subscription management
}),
],
});
export const { signIn, signUp, deleteUser, signOut, useSession, subscription } = createAuthClient({
baseURL: import.meta.env.VITE_BETTER_AUTH_URL,
plugins: [
inferAdditionalFields<typeof auth>(),
stripeClient({
subscription: true, //if you want to enable subscription management
}),
],
});
Client code:
const createStripeCheckoutSession = async ({
userId,
isYearly,
}: { userId: string; isYearly: boolean }) => {
setIsPending(true);
const { error } = await subscription.upgrade({
referenceId: userId,
plan: isYearly ? "yearly" : "monthly",
successUrl: "/dashboard?subscriptionSuccess=true",
cancelUrl: "/dashboard?subscriptionCanceled=true",
});

if (error) {
showNotification({
type: "error",
message: "Error creating checkout session",
description: error.message,
});
console.error("Error creating checkout session:", error);
}
setIsPending(false);
};
const createStripeCheckoutSession = async ({
userId,
isYearly,
}: { userId: string; isYearly: boolean }) => {
setIsPending(true);
const { error } = await subscription.upgrade({
referenceId: userId,
plan: isYearly ? "yearly" : "monthly",
successUrl: "/dashboard?subscriptionSuccess=true",
cancelUrl: "/dashboard?subscriptionCanceled=true",
});

if (error) {
showNotification({
type: "error",
message: "Error creating checkout session",
description: error.message,
});
console.error("Error creating checkout session:", error);
}
setIsPending(false);
};
Ping
Ping•3w ago
Hey, are you sure you're logged before creating a subscription?
RadiantFrog
RadiantFrogOP•3w ago
Hey @Ping, yes I'm logged into my user with better-auth
Ping
Ping•3w ago
Does the error give you any other details besides that it's "Unauthorized 401"?
RadiantFrog
RadiantFrogOP•3w ago
This is the entire error
Pro.tsx:56
POST https://dev.progressmade.ai/api/auth/subscription/upgrade 401 (Unauthorized)
Pro.tsx:69 Error creating checkout session:
{code: 'UNAUTHORIZED', message: 'Unauthorized', status: 401, statusText: ''}
Pro.tsx:56
POST https://dev.progressmade.ai/api/auth/subscription/upgrade 401 (Unauthorized)
Pro.tsx:69 Error creating checkout session:
{code: 'UNAUTHORIZED', message: 'Unauthorized', status: 401, statusText: ''}
I dont think it has to do with the convex adapter
Ping
Ping•3w ago
Yeah, it's very unlikely. :) By the way, I'm the creator of the Convex adapter, if you run into any issues don't hesitate to just ping me :)
RadiantFrog
RadiantFrogOP•3w ago
haha nice 🙂 I know 🙂
Ping
Ping•3w ago
Can you test without passing referenceId in your subscription.upgrade call?
RadiantFrog
RadiantFrogOP•3w ago
ah the error changed to a 500
# SERVER_ERROR: Error: [Request ID: 91870f8125d70b9e] Server Error
Uncaught Error: Error {} is not a supported Convex type.
at convexToJsonInternal (../../node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/convex/src/values/value.ts:339:6)
at convexToJson (../../node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/convex/src/values/value.ts:417:0)
# SERVER_ERROR: Error: [Request ID: 91870f8125d70b9e] Server Error
Uncaught Error: Error {} is not a supported Convex type.
at convexToJsonInternal (../../node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/convex/src/values/value.ts:339:6)
at convexToJson (../../node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/convex/src/values/value.ts:417:0)
Ping
Ping•3w ago
Maybe there's an error in your logs now? 👀 Well! That sure seems more like a Convex adapter issue xD
RadiantFrog
RadiantFrogOP•3w ago
yep XD I would've been perfect if it was plug and play 😛
Ping
Ping•3w ago
Can you enable debug logs from the convex adapter options?
RadiantFrog
RadiantFrogOP•3w ago
[convex-adapter] findOne result {
result: {
avatar: 'optimistic',
emailVerified: false,
language: 'en',
name: 'Ora Sipes',
plan: 'free',
proStatus: 'inactive',
providerId: 'credential',
stripeCustomerId: 'cus_S4P07Pv937bQE7',
updatedAt: 2025-04-04T19:46:52.787Z,
createdAt: 2025-04-04T19:46:53.136Z,
id: 'jh7fbj364wvp9vg09jzwrx6d5n7dc7hb'
},
duration: '398ms'
}
[convex-adapter] findMany {
model: 'subscription',
where: [
{ field: 'referenceId', value: 'jh7fbj364wvp9vg09jzwrx6d5n7dc7hb' }
],
limit: undefined,
offset: undefined,
sortBy: undefined
}
[convex-adapter] findMany queryString {
queryString: 'q.eq(q.field("referenceId"), "jh7fbj364wvp9vg09jzwrx6d5n7dc7hb")'
}
[convex-adapter] findMany result { result: [], duration: '164ms' }
[convex-adapter] create {
model: 'subscription',
values: {
plan: 'yearly',
stripeCustomerId: 'cus_S4P07Pv937bQE7',
status: 'incomplete',
referenceId: 'jh7fbj364wvp9vg09jzwrx6d5n7dc7hb',
seats: 1
},
select: undefined
}
# SERVER_ERROR: Error: [Request ID: 69a0ed3810ddc610] Server Error
Uncaught Error: Error {} is not a supported Convex type.
[convex-adapter] findOne result {
result: {
avatar: 'optimistic',
emailVerified: false,
language: 'en',
name: 'Ora Sipes',
plan: 'free',
proStatus: 'inactive',
providerId: 'credential',
stripeCustomerId: 'cus_S4P07Pv937bQE7',
updatedAt: 2025-04-04T19:46:52.787Z,
createdAt: 2025-04-04T19:46:53.136Z,
id: 'jh7fbj364wvp9vg09jzwrx6d5n7dc7hb'
},
duration: '398ms'
}
[convex-adapter] findMany {
model: 'subscription',
where: [
{ field: 'referenceId', value: 'jh7fbj364wvp9vg09jzwrx6d5n7dc7hb' }
],
limit: undefined,
offset: undefined,
sortBy: undefined
}
[convex-adapter] findMany queryString {
queryString: 'q.eq(q.field("referenceId"), "jh7fbj364wvp9vg09jzwrx6d5n7dc7hb")'
}
[convex-adapter] findMany result { result: [], duration: '164ms' }
[convex-adapter] create {
model: 'subscription',
values: {
plan: 'yearly',
stripeCustomerId: 'cus_S4P07Pv937bQE7',
status: 'incomplete',
referenceId: 'jh7fbj364wvp9vg09jzwrx6d5n7dc7hb',
seats: 1
},
select: undefined
}
# SERVER_ERROR: Error: [Request ID: 69a0ed3810ddc610] Server Error
Uncaught Error: Error {} is not a supported Convex type.
the subscription table is not getting new entries
Ping
Ping•3w ago
Hmm. This.. is very strange.
RadiantFrog
RadiantFrogOP•3w ago
yes, the create is in the logging but the table remains empty
Ping
Ping•3w ago
The data which the create log shows seem to be some very basic data too, nohing complex or anything... Makes no sense why it would break...
RadiantFrog
RadiantFrogOP•3w ago
No rush, I can log it on github if you want
Ping
Ping•3w ago
Yeah please create an issue on my repo I think I'll have to reproduce a demo and test myself. I'll test this when I wake, it's 6am rn 😅
RadiantFrog
RadiantFrogOP•3w ago
GitHub
Convex Adapter and Stripe plugin not working: Error {} is not a sup...
I'm receiving the following error when creating a Stripe subscription: # SERVER_ERROR: Error: [Request ID: 91870f8125d70b9e] Server Error Uncaught Error: Error {} is not a supported Convex type...
RadiantFrog
RadiantFrogOP•3w ago
Thanks for the help!
4/4/2025, 10:07:30 PM [CONVEX M(betterAuth:insert)] Uncaught Error: Failed to insert or update a document in table "subscription" because it does not match the schema: Object is missing the required field `id`. Consider wrapping the field validator in `v.optional(...)` if this is expected.
4/4/2025, 10:07:30 PM [CONVEX M(betterAuth:insert)] Uncaught Error: Failed to insert or update a document in table "subscription" because it does not match the schema: Object is missing the required field `id`. Consider wrapping the field validator in `v.optional(...)` if this is expected.
Im just seeing this error logging right now in the
convex dex
convex dex
Ping
Ping•3w ago
Do you have a schema defined for subscription?
RadiantFrog
RadiantFrogOP•3w ago
yes hold on, I'm just checking it yup it works.
RadiantFrog
RadiantFrogOP•3w ago
No description
RadiantFrog
RadiantFrogOP•3w ago
without the schema
Solution
RadiantFrog
RadiantFrog•3w ago
subscription: defineTable({
id: v.string(), // <-- this was the problem
plan: v.string(),
referenceId: v.id("user"),
stripeCustomerId: v.optional(v.string()),
stripeSubscriptionId: v.optional(v.string()),
status: v.string(),
periodStart: v.optional(v.string()),
periodEnd: v.optional(v.string()),
cancelAtPeriodEnd: v.optional(v.boolean()),
seats: v.optional(v.number()),
trialStart: v.optional(v.string()),
trialEnd: v.optional(v.string()),
}),
subscription: defineTable({
id: v.string(), // <-- this was the problem
plan: v.string(),
referenceId: v.id("user"),
stripeCustomerId: v.optional(v.string()),
stripeSubscriptionId: v.optional(v.string()),
status: v.string(),
periodStart: v.optional(v.string()),
periodEnd: v.optional(v.string()),
cancelAtPeriodEnd: v.optional(v.boolean()),
seats: v.optional(v.number()),
trialStart: v.optional(v.string()),
trialEnd: v.optional(v.string()),
}),
RadiantFrog
RadiantFrogOP•3w ago
Sorry for the trouble. But now you know it works! convex adapter and stripe plugin work well, if you're not misconfiguring it ;O
Ping
Ping•3w ago
No not your fault at all! This is actually my fault 👀 adapters are meant to provide an id during row creation if the data didn't already provide one.. When creating the convex adapter, it was a while ago while I was still somewhat new to BA as well... so there are definitely pitholes which I'm sure I didn't take into account. The good news is that all of those pitholes will be fixed from my PR in better-auth to introduce a createAdapter helper. Anyway, details aside, I'm glad it's solved!
RadiantFrog
RadiantFrogOP•3w ago
Ah okay. Pretty amazing experience with integrating it for such a new library.

Did you find this page helpful?