#juice-subscription-clientsecret

1 messages · Page 1 of 1 (latest)

lusty creekBOT
vale shadow
#

Hi there

#

Yep tell me more about what is going on

#

Are you seeing a particular error?

remote jacinth
#

hey, there is no error i think i implemented the workflow wrong im not too sure

#

my objective is for new users to sign up for a subscription

#

and then collect their information when they sign up but i think what its doing is creating the customer and then attaching the subscription to them instead of asking for their payment information

#
  const { email = `test${Math.floor(Math.random() * 9999) + 1}@domain.com` } =
    req.body;

  const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
    apiVersion: '2020-08-27',
    typescript: true,
  });

  let { plan } = req.body;
  let { name } = req.body;

  let sub_plan = '';

  if (plan == 'Starter') {
    sub_plan = 'price_1MmjhwAOi6IdtOdNWnMWqgkz';
  } else if (plan == 'Premium') {
    sub_plan = 'price_1MmjiDAOi6IdtOdNValIB02i';
  } else {
    sub_plan = 'price_1MmjhwAOi6IdtOdNWnMWqgkz';
  }

  const customer = await stripe.customers.create({
    email,
    name,
  });

  const ephemeralKey = await stripe.ephemeralKeys.create(
    { customer: customer.id },
    { apiVersion: '2020-08-27' }
  );
  const PRICE_ID = sub_plan;

  const subscription = await stripe.subscriptions.create({
    customer: customer.id,
    items: [{ price: PRICE_ID }],
    metadata: { email, name, plan },
    trial_period_days: 3,
  });

  if (typeof subscription.pending_setup_intent === 'string') {
    const setupIntent = await stripe.setupIntents.retrieve(
      subscription.pending_setup_intent
    );

    return res.json({
      setupIntent: setupIntent.client_secret,
      ephemeralKey: ephemeralKey.secret,
      customer: customer.id,
    });
  } else {
    throw new Error(
      'Expected response type string, but received: ' +
        typeof subscription.pending_setup_intent
    );
  }
});```
#

this is the server side function that i use, i added trial days because that prompted the user to enter their credit card information but i also believe thats the wrong way

#
    initialisePaymentSheet();
  }, []);

  const initialisePaymentSheet = async () => {
    const { setupIntent, ephemeralKey, customer } =
      await fetchPaymentSheetParams();

    const { error } = await initPaymentSheet({
      customerId: customer,
      customerEphemeralKeySecret: ephemeralKey,
      setupIntentClientSecret: setupIntent,
      merchantDisplayName: 'KOLO',
      allowsDelayedPaymentMethods: true,
      returnURL: 'https://kolo-server.herokuapp.com/paymentsuccess',
    });
    if (error) {
      Alert.alert(`Error code: ${error.code}`, error.message);
    } else {
      setReady(true);
    }
  };

  const fetchPaymentSheetParams = async () => {
    const response = await fetch(
      'https://kolo-server.herokuapp.com/payment-sheet-subscription',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          email: formData.email,
          name: formData.name,
          plan: formData.subscriptionPackage,
        }),
      }
    );

    const { setupIntent, ephemeralKey, customer } = await response.json();

    return {
      setupIntent,
      ephemeralKey,
      customer,
    };
  };``` this is the react native part
vale shadow
#

Okay let's back up

#

Before looking at the code

#

You want to take Subscription payments using Payment Sheet, correct?

#

Is there going to be a trial flow or no?

remote jacinth
#

no trial

#

they press checkout, fill in their credit card information and get charged every month, thats all

vale shadow
#

Okay then yeah you don't want that nor to deal with SetupIntents at all

remote jacinth
#

hmm then i followed the wrong thing i guess

vale shadow
#

When the customer presses your checkout button, you call your backend and create a Subscription

#

Then you pass the latest_invoice.payment_intent.client_secret to your front end

#

Along with the Ephemeral key and Customer ID

#

Then you init PaymentSheet with those 3 things from your backend

remote jacinth
#

is there documentation with examples for this? i think i have an idea but i just want to make sure i do it right

#

so i init the payment sheet after i create the subscription and get those values back from the backend?

vale shadow
#

It is basically just the flow here: https://stripe.com/docs/payments/save-during-payment except that instead of creating the PaymentIntent directly, you create a Subscription which will then create an Invoice and that Invoice will have an associated PaymentIntent

remote jacinth
#

let me try to see

vale shadow
#

Yep try it out and let me know if you run into issues

remote jacinth
#

sorry i have a question, how do i init the payment sheet with the latest invoice, i cant seem to find it on the docs? And do i have to return anything from the subscription i created? this is the code so far ``` const customer = await stripe.customers.create({
email,
name,
});

const ephemeralKey = await stripe.ephemeralKeys.create(
{ customer: customer.id },
{ apiVersion: '2020-08-27' }
);
const PRICE_ID = sub_plan;

const subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [{ price: PRICE_ID }],
metadata: { email, name, plan },
});

return res.json({
latest_invoice: latest_invoice.paymentIntent.client_secret,
ephemeralKey: ephemeralKey.secret,
customer: customer.id,
});
});```

#
    const { latest_invoice, ephemeralKey, customer } =
      await fetchPaymentSheetParams();

    const { error } = await initPaymentSheet({
      customerId: customer,
      customerEphemeralKeySecret: ephemeralKey,
      merchantDisplayName: 'KOLO',
      allowsDelayedPaymentMethods: true,
      returnURL: 'https://kolo-server.herokuapp.com/paymentsuccess',
    });
    if (error) {
      Alert.alert(`Error code: ${error.code}`, error.message);
    } else {
      setReady(true);
    }
  };

  const fetchPaymentSheetParams = async () => {
    const response = await fetch(
      'https://kolo-server.herokuapp.com/payment-sheet-subscription',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          email: formData.email,
          name: formData.name,
          plan: formData.subscriptionPackage,
        }),
      }
    );

    const { latest_invoice, ephemeralKey, customer } = await response.json();

    return {
      latest_invoice,
      ephemeralKey,
      customer,
    };
  };```
#

I appreciate the help, im still learning how to make it work with react native

vale shadow
#

Should be latest_invoice.payment_intent.client_secret on your backend

#

Also I forgot to add that you need to expand latest_invoice.payment_intent to access this

#

That has an example of what your backend code should look like

remote jacinth
#

ok one second i will do it

#

im getting an error that says i must provide a paymentIntentClientSecret, i did pass it in here

#

is it a different value?

vale shadow
#

That looks right. You are seeing that error on your frontend though, right?

remote jacinth
#

yup

vale shadow
#

Okay have you logged out the clientsecret after receiving it from your backend?

remote jacinth
#

do you mean log it to console?

#

one second i forgot to change to a test product, it might be that

vale shadow
#

Yes, you should add logs throughout your code and they will show up in Metro for your frontend

lusty creekBOT
remote jacinth
#

i see i get undefined for the clientsecret

wintry pagoda
#

Okay so now the next step is to debug why! Does your Subscription have a latest_invoice? Does that one have a PaymentIntent? etc.

#

as the developer you can debug this in second by adding clear logs

#

juice-subscription-clientsecret

remote jacinth
#

yea im adding logs give me a second ill try to fix it

wintry pagoda
#
    customer: customer.id,
    items: [{ price: PRICE_ID }],
    metadata: { email, name, plan },
  });
``` this is your code, you never expanded anything, so you'll get `latest_invoice: 'in_123'` back. So there's no way to look at the underlying Invoice's PaymentIntent

You need to carefully read our docs on our Expand feature <https://stripe.com/docs/expand> first and then properly expand the object(s) your code needs
remote jacinth
#

oh no i updated my code,

#

i did expand the latest invoice

wintry pagoda
#

ah gotcha, so yeah my guess is either you didn't expand like you thought, or maybe the first Invoice is below the minimum and so there would be no PaymentIntent

remote jacinth
#

yea its 10 euros, should be above the minimum, maybe i didnt expand properly, im going through the expand docs

#

i see what it could be, the customerId was a constant i had before, i needed to change that to customer.id

#

let me try to run it again

#

it works 😄 thank you very much

#

it did not set the credit card as default though, is that going to be a problem when it tries to withdraw the payment or does it go for any available payment method on the customers profile?

#

i thought this option auto set it as default

wintry pagoda
#

yes that option should do the right thing

#

Do you have a clear example I can look at?

remote jacinth
#

i see here that its going to charge it

#

i guess it just doesnt show up as default on the stripe dashboard

wintry pagoda
#

Sorry it's just a picture, I have no idea what you need from me yet

#

If you can provide an exact subscription id though I can look

remote jacinth
#

i think its working, i thought the payment method would be listed as default on the costumer thats all, the tests are all good its working, thank you for the help today

#

i appreciate it

#

have a good day

wintry pagoda
#

Ah so the PM is the default on the Subscription with that feature, not the Customer

#

We want to support making it the default on the Customer in the future though

remote jacinth
#

i see, thats great to know thanks