Stripe: can't change plan for authenticated 3DS card? - php

I'm trying to allow customers to change their Stripe plan via the API, but 3D Secure test cards are failing even though they should be authenticated for future use.
My API call is as follows:
$stripe->subscriptions->update(
$subscription_id, [
'items' => [
[
'id' => $subscription->items->data[0]->id,
'price' => $new_plan_id,
],
],
]);
This works absolutely fine with a standard card number (e.g. 4242 4242 4242 4242)
However for 3DS cards it returns a 402 error:
{
"error": {
"code": "subscription_payment_intent_requires_action",
"message": "Payment for this subscription requires additional user action before it can be completed successfully. Payment can be completed using the PaymentIntent associated with `subscription.latest_invoice`. Additional information is available here: https://stripe.com/docs/billing/subscriptions/overview#requires-action",
"type": "card_error"
}
}
I'm using test card numbers which require authentication the first time (which is done when the subscription is created), but should succeed subsequent times according to this page https://stripe.com/docs/testing.
I've also tried the Javascript route as an alternative, in order to check the response of the API call and then trigger the stripe.ConfirmCardPayment() routine if authentication is required, but the fatal PHP error kills the script before it even gets to that point.
Any ideas how I could resolve?

Which card specifically are you using? What is the exact flow you're using for the initial setup?
If you're talking about the 3155 card, you need to explicitly set it up for future use with either a Setup Intent or using setup_future_usage with a payment intent. It is not automatically set up for future usage simply by authenticating the first time.
You may wish to use the 0446 card to simulate this setup, but I encourage you to ensure you've got the implementation ready for real world scenarios by actually going through the setup flow.

Related

implementing stripe with strong customer authentication

I am trying to create a word-press plugin with stripe that support strong customer authentication. charges with stripe is working fine for me. I have referred this link https://www.codexworld.com/stripe-payment-gateway-integration-php/ for creating payments. Any such reference link for sca implementation in stripe? Thanks in advance
I just moved my company over from the old Charges API to SCA. It wasn't pretty. I'm a back-end engineer so I'm only going to tackle the back-end components needed to make this work. If you want a fully customised front-end too, you're going to need to read the Stripe docs and do a lot more bounces around form submit -> enhanced validation -> collect more data -> submit more data. This workflow was too complicated and thankfully my company went with the Stripe checkout solution for this part.
I work for a SaaS company and we take money for room bookings, so we have non-fungible time dependent 'stock' items. Since we sell booking slots on behalf of our customers we also have Stripe connected accounts in play. Basically, all the nightmare corner cases you could wish for. Since the checkout session is live for 24 hours we have to allocate then collect - if you start the process and then go for lunch there is no guarantee that when the payment is processed the room you want will still be free for your timeslot.
My process looks like:
Back-end calls Stripe and starts a checkout session with Session::create(). Set your payment_method_types and line_items in this as well as your return URLs. Also send up a payment_intent_data. Mine looks like this:
'payment_intent_data' => [
'transfer_data' => [
'destination' => 'acct_ number of linked account',
],
'capture_method' => 'manual',
'description' => 'description of item',
'statement_descriptor' => 'description of item, max 22 chars',
],
Obviously if you don't have connected accounts then omit the transfer_data but definitely include both descriptors. If you are sure of your stock levels (eg: digital goods) you can change your capture_method to automatic.
On a return from Stripe I send the session key (sk_) to the front-end who then hand this over to Stripe's payment form. This is the point you'll need to do a lot more work if you want a totally custom front-end. Good luck.
When the checkout session is successful I have a webhook event on checkout.session.completed to call my API, as well as handling the URL returns from the front-end. I keep extra data in my database about the payment state so I only handle each return once.
Either return leads me back to the session - the ID is in the front-end links and the session can be retrieved from $session = $event->data->object in the webhook callback. You can then get the intent out of the session with $intent = PaymentIntent::retrieve($session->payment_intent);
At this point, I handle the various status codes in the PaymentIntent;
requires_payment_method
requires_confirmation
requires_action
canceled
requires_capture
succeeded
The one I'm really interested in is requires_capture where I then check to see if the resource is still free. If it is, I $intent->capture() to finalise the payment and create the booking for the user. You can handle each of them as needed by your business process.
If you are dealing with connected accounts you will also need to load the transfer item associated with the PaymentIntent Charge object and then load the Charge referenced in the destination_payment field (you can load charges with the py_ key). Then set the description and statement_descriptor fields and save the charge back so your connected customers know what the payment is for.

Stripe - How to create a PaymentMethod without attaching to an user?

Our user can enter a card to pay our services.
He/she can choose to save or to NOT save card for reusing.
When user is NOT saving card, we are creating a PaymentIntent passing
[
'amount' => floatval($this->cart->total_gross) * 100,
'currency' => 'EUR',
'payment_method' => $this->pm,
'off_session' => true,
'capture_method' => 'manual',
'confirm' => true,
];
This because it's a preauthorization of a payment that will be captured in 2-3 days.
Using this config we got the following error
Stripe\Exception\MissingParameterException - 400 - The provided PaymentMethod is already attached to another object. You cannot reuse PaymentMethods without attaching them to a Customer object first.
To be clear:
user enters a NEW CARD
choose to NOT save for future usage
stripe.js automatically handle the 3d secure card auth
the returned pm_... is sent to server with the amount and the instruction to NOT save card (so we do not create a stripe customer)
using the above config we call the \Stripe\PaymentIntent::create method
we got the error above
What is the meaning of this error? Why does it says that the PM is already attached to another object? Which? Of which kind?
I triple-checked my code and it's the ONLY api call we are making after receiving the pm from frontend.
And the frontend, previously, simply use a setupIntent to authorize the card using official stripe.js calls. So the pm, returned from stripe.js is sent to our server without doing nothing with it. And our server simply call the create method, and got this error every time.
Asking your help to diagnose and understand.
We're using latest official stripe-php versions
Thanks in advance
I finally received an official response on this problem.
Simply: we cannot save a card for of_session payments and then use the payment method in a later moment to capture the pre-authorized amount
So, we modified our flow
User choose or save a card and we inform the user that we must save the card to be able to reuse to complete the payment flow.

Braintree Unsuccessful credit card verification is not working for my code

I am trying to test braintree single payment. All works fine and the code makes a transactions which I can see in the sandbox and also from response code.
But the problem is when I try to use one their 'Unsuccessful credit card numbers' it still process the transactions and gives me success as a response.
Here is the code I am using for making transaction.
$result = \Braintree\Transaction::sale([
'amount' => $amount,
'paymentMethodNonce' => $nonce,
'options' => [
'submitForSettlement' => true
]
]);
This code never gives error unsuccessful credit card verification number.
I tried to use verifyCard=true in the options array and gives me error.
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact
support.
For you to have an unsuccessful credit card verification, it has to happen during a PaymentMethod::Create() call as :verify_card => true can't be passed in a Transaction.sale() call. Therefore any testing cards in this section, will only fail during the attempt to make a payment method with the verify parameter used, not during a transaction. If you want to only make a transaction and have it fail, try using a different amount that can trigger different failures as documented here: test amounts

Stripe : Specify Specific Card for Subscription

I have set up subscription based website that allows people to have multiple subscriptions. I decided to go with Stripe for payment and card processing. It took very little time to get it integrated into my Symfony2 project. I was able to create subscriptions, customers, and add cards within a couple of hours. Then I ran into an issue. If a customer has multiple cards, I wanted to be able to allow them to choose which card they wanted to use when they create a new subscription. It sounded easy. After 2 days and about 30 hours of combing through their documentation I have to say that I cannot figure out how to get this to work.
The way I have this set up is that when the customer creates a card I store the "card id" in my database along with the brand. This just makes it easy to load details on the server side when the page is being requested. The customer creating the new subscription sees their cards and choose which one they want to use for the new subscription. This is passed to my php script via AJAX to create the new subscription. However, when I try to use a specific card, I am getting a 400 error indicating that the "card id" is not a token. I know that it is not a token since the token was used to add the card to the customer account but how in the world do I specify the exact card that the customer wants to use?
NOTE: Using an a new token creates another instance of the card.Not an option.
PHP:
require_once('../stripe-php/init.php');
//Set Secret API Key
\Stripe\Stripe::setApiKey("sk_test_XXXXXXXXXXXXXXXXXXXXX");
//Retrieve Customer
$cu = \Stripe\Customer::retrieve($_POST['customer_id']);
//Create Subscription using saved customer "card id"
$createSubscription = $cu->subscriptions->create(array("plan" => $_POST['sub_option'], "source" => $card));
POSTED TO STRIPE:
plan: "500-2016"
source: "card_xxxxxxxxxxxxxxxxxxxxx"
STRIPE ERROR: TYPE 400
error:
type: "invalid_request_error"
message: "No such token: card_xxxxxxxxxxxxxxxxxxxxxxx"
param: "source"
I got a reply from Stripe support on this: it is not possible for one Customer to have subscriptions with different payment sources. All invoices for subscriptions are always billed to the current default_source for the Customer. So if you change the default as Giles Bennett suggested, you'll be changing it for all subscriptions, regardless of what the default was at time of creation.
If you need one user to have subscriptions with more than one source, you need to create multiple stripe Customer objects for that user, with a different default_source for each.
Since I have not received any input from SO or Stripe, I have somewhat came to the conclusion that this cannot be done. I found a similar question on a different forum that ended with the results being - No Response From Stripe - and that this cannot be done. Though the Stripe documentation does not hit on this subject it does appear that a Subscription can only be charged to the default card. There is no "Card" object for subscriptions as there is for a "Charge".
I realise this thread is quite old, but having come across it whilst trying to answer the same question myself, this solution may be of use to those who come along afterwards.
The Stripe API says that the "source" parameter for the API call to create a new subscription is optional - if ommitted, then it will default to the customer's default card. If included, then it can only be a token (ie. a new card) or a dictionary entry (again, for a new card, just not tokenised).
The solutionĀ could be to update the customer's default source first. Using Cartalyst through Laravel, as we are :
$customer = Stripe::customers()->update( "customer_id", [
'default_source' => "card_id"
]);
You can then proceed to add your subscription as normal - the newly-defaulted card will be assigned to it. If needs be (depending on your application) then you may also wish to save the previous default card ID to a variable first, to then allow you to set it back to being the default card after your new subscription.
I hope that helps.

payPal REST-API makePaymentUsingCC error

After cloning rest-api-sample-app-php and updating for my environment (db, client, secret, etc...) I can buy a pizza using the paypal payment option. I cannot using the credit card option, although I have a creditcard_id in the ppusers record.
The error happens in function makePaymentUsingCC($creditCardId, $total, $currency, $payment_desc)
I'm getting to makePaymentUsingCC and I can echo the params. This line fails (for lack of a better word) : $ccToken = new CreditCardToken(); I don't think the use statement use PayPal\Api\CreditCardToken; is finding the the file.
My main issue is not the pizza app, but my custom app is failing at the same point. So, if we can figure out the pizza app issue it will probably resolve mine too.
Oh, the error message from the pizza app screen is:
INVALID_RESOURCE_ID : The requested resource ID was not found
Thanks in advance!
A CreditCardToken is a reference to a tokenized credit card - essentially storing a card to re-use later without reentry, see https://developer.paypal.com/webapps/developer/docs/api/#store-a-credit-card
If you have stored a credit card in the vault, you can then use it for subsequent payments. If the credit card token used is not valid for your credentials then a INVALID_RESOURCE_ID is returned.

Categories