#mrx_save-paymentmethods-serverside

1 messages ยท Page 1 of 1 (latest)

dawn iceBOT
round schoonerBOT
#

Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.

dawn iceBOT
#

๐Ÿ‘‹ Welcome to your new thread!

โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.

โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.

๐Ÿ”— This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1267496310655811595

๐Ÿ“ Have more to share? Add more details, code, screenshots, videos, etc. below.

narrow bane
#
    {
        $payment_method_id = data_get($request,'id');
        $customer_email = data_get($request,'billing_details.email');

        Log::info("---------------------------------");
        Log::info(json_encode($request));

        // Create Customer
        $customer = Customer::create([
            'email' => $customer_email,
            'payment_method' => $payment_method_id,
        ]);
        Log::channel('stripe')->info($customer);

        // Create Payment Intent
        $paymentIntent = PaymentIntent::create([
            'customer' => $customer->id,
            'amount' => 4900,
            'currency' => 'usd',
            'payment_method' => $payment_method_id,
            'off_session' => true,
            'confirm' => true,
        ]);
        Log::channel('stripe')->info($paymentIntent);

        if ($paymentIntent['status'] === PaymentIntentStatus::Succeeded->value) {

            $data = [
                'customer_id' => $customer->id,
                'payment_method_id' => $payment_method_id,
            ];

            return new ServiceResponse(true, $data, 'Payment Succeeded');
        } else {
            return new ServiceResponse(true, null, 'Payment has status ' . $paymentIntent['status']);
        }
    }

#

This is the code when I charge the first payment

void thistle
#

If you are collecting payment details for a future payment without an initial, immediate payment use a Setup Intent

narrow bane
#

So in my case(if you can check the code pls), I do not need to create a Setup Intent ?

void thistle
#

Well where is payment_methoid_id coming from? How are you creating that pm_xxx?

narrow bane
#

yes

#

front end sends an object of payment method

#
    private function chargeCustomer(Issue $issue): void
    {
        $customer = Customer::retrieve($issue->customer_id);
        Log::info("Customer object");
        Log::info($customer);

        $paymentIntent = PaymentIntent::create([
            'customer' => $customer->id,
            'amount' => $this->getAmount($issue),
            'currency' => 'usd',
            'payment_method' => $issue->payment_method_id,
            'off_session' => true,
            'confirm' => true
        ]);

        Log::info("Payment Intent");
        Log::info($paymentIntent);

        if ($paymentIntent['status'] == 'succeeded') {
            Log::info("Charge successful for customer {$paymentIntent?->id} with charge ID {$paymentIntent->id}");
        } else {
            Log::warning("Charge not successful for customer {$paymentIntent->id}");

        }
    }

This is the code that is used for the second payment after 60 days

void thistle
#

But how are you creating the pm_xxx object?

narrow bane
#
  const { paymentMethod, error } = await stripe.value.createPaymentMethod({
    type: 'card',
    card: cardNumberElement.value,
    billing_details: {
      name: state.card.name,
      email: state.email,
    },
  })

  if (error) {
    state.error = error.message
    state.submitting = false
    return
  }

  if (paymentMethod) {
    try {
      await PaymentsService.complete({
        stripe: {
          ...paymentMethod,
        },
        issue_id: state.currentIssue?.id,
      })
    }
    catch (error) {
      handleError(error)
    }
#

pm_xxx object is created on front end

void thistle
#

Yeah you shouldn't really be creating Payment Methods that way. That code should be replaced by a Setup Intent

narrow bane
#

The code for the second payment right?

#

or Both?

void thistle
narrow bane
#

I am confused, why shouldn't a Payment Method be created on front end. Since I want to handle both payments on the server side. Does the payment method expire and I can't use it after 60 days?
I pretty much followed this: https://docs.stripe.com/payments/finalize-payments-on-the-server?platform=web&type=setup&lang=php#charge-saved-payment-method

Build an integration where you render the Payment Element before you create a PaymentIntent or SetupIntent, then confirm the Intent from your server.

void thistle
#

Because that endpoint simply tokenises the card data, it doesn't validate that it's actually valid nor does it carry out any potential authentication that may be required by the bank. The Setup Intent will optimise all that for any future off-session payments

void thistle
narrow bane
#

Can you please give me some instructions I should follow, on the information I have given?

void thistle
#

As I said, your flow is fine. It'll probably work for most cases, but the issue you will undoubtedly encounter is when you try to charge some of the cards off-session (via a Payment Intent) the bank/issue will likely request 3DS for the customer to authenticate the payment. At that point though they're not in the checkout UI as it's an off-session payment so it'll just fail in Stripe and you'll need to bring your customer back on-session

#

However if you generate the pm_xxx object via a Setup Intent (as linked above) then that automatically optimises for those scenarios by doing the 3DS/authentication upfront when the customer is saving their card, in practice reducing the changes the bank asks for authentication for your off-session payment

dawn iceBOT
narrow bane
#

So Setup Intent is used only to create a payment method, and later use it for payment intents?

hardy cipher
#

Hi ๐Ÿ‘‹

my colleague had to step away so I'm taking over. The Setup Intent flow my colleague linked you to is how we recommend saving payment methods and attaching them to Customers so that they are optimized for future usage.

narrow bane
#

Can you please confirm if my flow is correct:

  1. Create Setup Intent on server
  2. use confirmCardSetup(with client secret send from Server) on Front-End
  3. Send Setupintent returned by confirmCardSetup to server
  4. Create Payment Intent for first payment based on payment method from setup intent
  5. Create Payment Intent for second payment after 60 days
hardy cipher
#

For step 2. what Stripe element are you using?

narrow bane
#
// Front-end: Create and confirm Setup Intent
const { setupIntent, error } = await stripe.confirmCardSetup(setupIntentClientSecret, {
  payment_method: {
    card: cardElement,
    billing_details: {
      name: 'Cardholder Name',
      email: 'cardholder@example.com',
    },
  },
});

if (error) {
  // Handle error
} else {
  // Send setupIntent.payment_method to your server
  const paymentMethodId = setupIntent.payment_method;
  await PaymentsService.completeSetup({
    stripe: {
      paymentMethodId: paymentMethodId,
    },
    issue_id: state.currentIssue?.id,
  });
}
hardy cipher
#

Okay you are using a Card Element. So that JS method makes sense

#

Is the Payment Intent happening on Step 4 occurring immediately after the setup intent?

narrow bane
#

Yes, first payment should happen immediately

hardy cipher
#

In that case, why don't you save the Payment Method at the same time as you make the first payment? It would cut down on the amount of API calls you need to make.

narrow bane
#

I have been tasked to complete all payments on the server. I think on the link you provided, the payment is supposed to be completed on the client side

hardy cipher
narrow bane
hardy cipher
#

Not at all

#

You do not need to create and confirm any Setup Intents if you use the setup_future_usage parameter when creating the Payment Intent

narrow bane
#

thank for your time to both you and your colleague

hardy cipher
#

Sure thing, we're happy to help ๐Ÿ™‚

dawn iceBOT