Nishil Faldu
Nishil Faldu•10mo ago

Action, Internal, Query and Mutation

export const storeStripeCustomerId = action({
args: {},
handler: async ctx => {
const stripe = new Stripe(
process.env.STRIPE_SECRET_KEY! ?? "",
{
apiVersion: "2023-10-16",
typescript: true,
}
);

const user = await ctx.runQuery(internal.users.getUserInternalQuery);
if (!user) {
throw new Error("User not found");
}
console.log("bla bla bla run now");
console.log(user, "user");

if(!user.stripeId) {
const customer = await stripe.customers.create({
email: user.email,
name: user.firstName + " " + user.lastName,
});

console.log("let me create one");

// await ctx.db.patch(user._id, {
// stripeId: customer.id,
// });
const stripeId : string
= await ctx.runMutation(internal.users.storeStripeId, { userId: user._id, stripeId: customer.id });

return stripeId;
}

return user.stripeId;
},
});
export const storeStripeCustomerId = action({
args: {},
handler: async ctx => {
const stripe = new Stripe(
process.env.STRIPE_SECRET_KEY! ?? "",
{
apiVersion: "2023-10-16",
typescript: true,
}
);

const user = await ctx.runQuery(internal.users.getUserInternalQuery);
if (!user) {
throw new Error("User not found");
}
console.log("bla bla bla run now");
console.log(user, "user");

if(!user.stripeId) {
const customer = await stripe.customers.create({
email: user.email,
name: user.firstName + " " + user.lastName,
});

console.log("let me create one");

// await ctx.db.patch(user._id, {
// stripeId: customer.id,
// });
const stripeId : string
= await ctx.runMutation(internal.users.storeStripeId, { userId: user._id, stripeId: customer.id });

return stripeId;
}

return user.stripeId;
},
});
the hook (for some reason), given below runs twice and in the process it generates 2 stripe customers which should ideally be impossible considering I check for if(!user.stripeId) this which surprisingly is true both the times which means stripeId is "". Surprisingly when I check the logs on convex dashboard, the log statement runMutation prints first and before the log statement of storeStripeCustomerId. Would really appreciate someone's help here as I am very confused!
3 Replies
Nishil Faldu
Nishil FalduOP•10mo ago
I am running the above in a hook like this on the client side
export default function useStoreStripeCustomerEffect() {
// const { isAuthenticated } = useConvexAuth();
const [stripeCustomerId, setStripeCustomerId] = useState<string | null>(null);

const storeStripeCustomerId = useAction(api.stripe.storeStripeCustomerId);

useEffect(() => {
// if (!isAuthenticated) { return; }

async function createStoreStripeCustomer() {
const id = await storeStripeCustomerId();
setStripeCustomerId(id);
console.log("blaa 1");
}

createStoreStripeCustomer();

return () => setStripeCustomerId(null);
}, [storeStripeCustomerId]);

return stripeCustomerId;
}
export default function useStoreStripeCustomerEffect() {
// const { isAuthenticated } = useConvexAuth();
const [stripeCustomerId, setStripeCustomerId] = useState<string | null>(null);

const storeStripeCustomerId = useAction(api.stripe.storeStripeCustomerId);

useEffect(() => {
// if (!isAuthenticated) { return; }

async function createStoreStripeCustomer() {
const id = await storeStripeCustomerId();
setStripeCustomerId(id);
console.log("blaa 1");
}

createStoreStripeCustomer();

return () => setStripeCustomerId(null);
}, [storeStripeCustomerId]);

return stripeCustomerId;
}
lee
lee•10mo ago
in dev mode, React runs useEffects twice (there are lots of blog posts and memes about this 😛). And convex doesn't have transactionality guarantees for Actions, which explains why the action is running twice and why getUserInternalQuery is returning a user with no stripeId in both actions.
Michal Srb
Michal Srb•10mo ago
To fix this, don't call an action from the client. Instead call a mutation, write a document into the database to track the state, and schedule an action to call stripe. You might want to use https://stack.convex.dev/retry-actions to make sure the stripe action succeeds.
Automatically Retry Actions
Learn how to automatically retry actions in Convex while also learning a little about scheduling, system tables, and function references.

Did you find this page helpful?