#mrx_save-paymentmethods-serverside
1 messages ยท Page 1 of 1 (latest)
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.
- mrx_docs, 4 days ago, 16 messages
๐ 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.
{
$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
If you are collecting payment details for a future payment without an initial, immediate payment use a Setup Intent
So in my case(if you can check the code pls), I do not need to create a Setup Intent ?
Well where is payment_methoid_id coming from? How are you creating that pm_xxx?
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
But how are you creating the pm_xxx object?
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
Yeah you shouldn't really be creating Payment Methods that way. That code should be replaced by a Setup Intent
This one, should be replaced by: https://docs.stripe.com/payments/save-and-reuse
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
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
Yes, but that flow is for when your customer's on on-session and can handle any auth requests from the bank/issuer for the payment
Can you please give me some instructions I should follow, on the information I have given?
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
So Setup Intent is used only to create a payment method, and later use it for payment intents?
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.
Can you please confirm if my flow is correct:
- Create Setup Intent on server
- use confirmCardSetup(with client secret send from Server) on Front-End
- Send Setupintent returned by confirmCardSetup to server
- Create Payment Intent for first payment based on payment method from setup intent
- Create Payment Intent for second payment after 60 days
For step 2. what Stripe element are you using?
// 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,
});
}
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?
Yes, first payment should happen immediately
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.
We have a step by step guide for how to do this here
https://docs.stripe.com/payments/save-during-payment
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
In that case you woud use our Finalize on Server guide but include the `setup_future_usage: "off_sesson" when creating the payment intent. This is what saves the payment method in the flow I linked you to.
https://docs.stripe.com/payments/finalize-payments-on-the-server
So it would be like this flow, because I can;t see how can I cut the amount of API calls
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
thank for your time to both you and your colleague
Sure thing, we're happy to help ๐