#CMarabate

1 messages · Page 1 of 1 (latest)

pulsar trailBOT
acoustic zephyr
granite moss
acoustic zephyr
#

Netlify Functions are essentially API calls, I have a function that I have a Stripe Webhook Endpoint pointed at and I was having a ton of issues trying to get it to work. If I showed you the code for the function do you think you would be able to take a look and see what I am doing wrong?

granite moss
#

So it's to accept Stripe Webhook event?

acoustic zephyr
#

Yeah the customer.subscription.updated event

#
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const ManagementClient = require('auth0').ManagementClient;

const management = new ManagementClient({
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  scope: 'read:users update:users',
});

// Get role name from product ID
function getRoleFromPlan(planId) {
  switch (planId) {
    case 'price_1MfslxFUDFxTEzZGP1NQSmEX':
      return 'Test Member';
    case 'price_1LAIw9FUDFxTEzZGYzM290b6':
      return 'Common Member';
    case 'price_1MecZKFUDFxTEzZGPhLWWGmC': // monthly
      return 'Uncommon Member (Monthly)';
    case 'price_1MecZKFUDFxTEzZGfAk1x67b': // yearly
      return 'Uncommon Member (Yearly)';
    case 'price_1MebTnFUDFxTEzZG0jNlXiUU': // monthly
      return 'Rare Member (Monthly)';
    case 'price_1MebTnFUDFxTEzZGXvCaprzh': // yearly
      return 'Rare Member (Yearly)';
    case 'price_1MecgCFUDFxTEzZG90uvzJIC': // monthly
      return 'Epic Member (Monthly)';
    case 'price_1MechKFUDFxTEzZGbOz5v5aX': // yearly
      return 'Epic Member (Yearly)';
    case 'price_1LAJ0tFUDFxTEzZGaNqoqBqi': // monthly
      return 'Legendary Member (Monthly)';
    case 'price_1MbqiVFUDFxTEzZGo87RhTHT': // yearly
      return 'Legendary Member (Yearly)';
    default:
      return null;
  }
}

exports.handler = async (event, context) => {
  try {
    const { subscriptionId, selectedProduct } = JSON.parse(event.body);
    if (event.type === 'customer.subscription.updated') {
      const data = JSON.parse(event.body);
      const subscriptionId = data.data.object.id;

      const subscription = await stripe.subscriptions.retrieve(subscriptionId);
      const customer = await stripe.customers.retrieve(subscription.customer);

      const userId = customer.metadata.auth0_user_id;
      const user = await management.getUser({ id: userId });

      const roles = user.app_metadata.roles || [];
      const roleName = getRoleFromPlan(selectedProduct.id);

      if (!roleName) {
        console.error(`Error: Invalid product ID ${selectedProduct}`);
        return { statusCode: 400, body: `Invalid product ID ${selectedProduct}` };
      }

      const updatedRoles = roles.filter(role => role !== roleName);

      await management.updateUser({ id: userId }, { app_metadata: { roles: updatedRoles } });

      await stripe.subscriptions.update(subscriptionId, { cancel_at_period_end: true });

      console.log(`Subscription canceled successfully for user ${userId}`);
      return { statusCode: 200 };
    } else {
      console.error(`Error: Invalid event type ${event.type}`);
      return { statusCode: 400, body: `Invalid event type ${event.type}` };
    }
  } catch (error) {
    console.error(error);
    return { statusCode: 500, body: error.message };
  }
};
#
  let handleUnsubscribe: ((user: { subscriptions: any[] }) => Promise<void>) | ((arg0: any) => void);
  handleUnsubscribe = async (user: { app_metadata: {} }) => {
    const userRoles = user['https://raidchamps.com/app_metadata'].roles;
    console.log(`userRoles`, userRoles);
    console.log(`userRoles[0]`, userRoles[0]);

    if (!userRoles || userRoles.length === 0) {
      // User is not subscribed to any plan
      return;
    }

    setLoading(true);

    // Get the subscription ID of the first active subscription
    const subscriptionId = getPlanFromRole(userRoles[0]);

    console.log(`subscriptionId`, subscriptionId);

    if (!subscriptionId) {
      // User does not have any active subscription
      return;
    }

    // Cancel the subscription in Stripe
    const response = await fetch('/.netlify/functions/handle-unsubscribe', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        event: event,
      }),
    });

    if (!response.ok) {
      // Failed to cancel the subscription
      console.error('Failed to cancel the subscription:', response);
      return;
    }

    setLoading(false);
  };
#
...
<button onClick={() => handleUnsubscribe(user)}>{loading ? 'Loading...' : 'Unsubscribe'}</button>
#

That should be the full picture of what I am trying and what I am experiencing.

#

And this is not my first time using a Stripe Webhook like this, I have one setup for the Subscribe/Upgrade button that works fine but that one uses the invoice.paid event.

granite moss
#

It's unclear to me where it is a Stripe webhook handler. I see handle-unsubscribe as a Netlify backend endpoint? And your request to that endpoint is failed

#

So we would need the log of that logic

acoustic zephyr
#

What logic? a log from the webhook? because it never gets triggered so there are no logs.

#

And this is all the log for the function tells me...

granite moss
#

/.netlify/functions/handle-unsubscribe

#

Request to this endpoint failed, correct? I see it in your screenshot

acoustic zephyr
#

Yeah

#

Well it triggerd the function but the webhook never ran

#

Because of the invalid event type error im guessing

granite moss
#

I am confused. That supposes to be your own fucntion, not the webhook handler

#

Webhook handler is the program you use to receive webhook from Stripe (ie. we send events to you), while this is a request from your frontend to your backend

acoustic zephyr
#

It is my own function but the webhook endpoint is pointing to it

#

Are you saying I need another function that I call that will handle the webhook somehow?

#

Why is this so freaking confusing. All I want is a way to let my users unsubscribe lol.

#

Been going at it for over a week and getting nowhere.

#

Am I going about it all wrong is there a simpler way to handle this?

granite moss
#

It is my own function but the webhook endpoint is pointing to it
This part I don't get. Webhook endpoint should be a separated function than your backend <> frontend flow

acoustic zephyr
#

I clearly do not understand webhooks but that is exactly what I am doing with this webhook endpoint and it works great...

granite moss
#

If you only need to let your customer unsubscribe, you don't need webhook AFAIK. Webhook is when you want to response to some events coming from Stripe

acoustic zephyr
#

Okay cool I knew I was doing something way wrong. So how should I allow customers to unsubscribe?

granite moss
acoustic zephyr
#

Hmm so exactly what I am doing except it does not need a webhook endpoint?

granite moss
#

Yep

acoustic zephyr
#

Doesn't really explain why it isn't working but that is good to know.

granite moss
#

Just by looking at the error, I think your logic stopped here

const { subscriptionId, selectedProduct } = JSON.parse(event.body);
    if (event.type === 'customer.subscription.updated') {
...

So probably you want to console.log this event

acoustic zephyr
#

Okay thanks!