Stripe : No card attached to customer after a checkout session payment - php

I'm using Stripe with PHP, I want to create subscription to customer after a payment made with Stripe Checkout Session.
But, the problem is that when we do a one shot payment with Stripe Checkout Session, Stripe does not attach the source (Credit card) to the customer, and when I try to subscribe this customer to a plan, Stripe return me the error :
This customer has no attached payment source or default payment method.
I don't understand why Stripe don't attach the card to the customer with one shot payment because Stripe do it when we use a Checkout Session with Subscription.
How I create checkout session :
$checkout_session = Session::create([
'payment_method_types' => ['card'],
'mode' => 'payment',
'customer' => 'cus_xxxxxxxx',
'line_items' => [[
'price' => 'price_xxxxx', // Oneshot payment
'quantity' => 1,
]]
]);
How I try to subscribe customer (After successful payment) :
$subscription = Subscription::create([
'customer' => 'cus_xxxxxxxx',
'items' => [
['price' => 'price_XXXXXXX'] // Recurrent payment
],
'trial_from_plan' => true,
]);
Thank you for your help

When accepting a one-time payment with Checkout, it won't create a Customer or save card details by default. What you have to do is configure Checkout to do this for you during the payment. This is covered in their documentation here.
What you need to do is set the payment_intent_data[setup_future_usage] parameter like this:
$checkout_session = Session::create([
'payment_method_types' => ['card'],
'mode' => 'payment',
'customer' => 'cus_xxxxxxxx',
'line_items' => [[
'price' => 'price_xxxxx', // Oneshot payment
'quantity' => 1,
]],
'payment_intent_data' => [
'setup_future_usage' => 'off_session',
]
]);
After that, you will have a Customer cus_123 and a PaymentMethod pm_123 that you can re-use. That PaymentMethod will not be the default one though so you need to make sure that you pass its id in the default_payment_method parameter when you create the Subscription like this:
$subscription = Subscription::create([
'customer' => 'cus_xxxxxxxx',
'items' => [
['price' => 'price_XXXXXXX'] // Recurrent payment
],
'trial_from_plan' => true,
'default_payment_method' => 'pm_12345',
]);
Also note that you can start a Subscription directly from Checkout as documented here.

Related

Stripe Create Invoice and auto charge

I am creating an invoice and auto charging a customer via their PHP package. The issue is though the invoice is created but the card on file is not charged, it says fail. This is odd to me because if I go into the dashboard online, find the invoice the is listed as "failed" and manually click the "retry charge" it works. Why would this be? Its clearly creating the invoice correctly and trying to charge the card on file but the card seems to only clear on ALL of these invoices if I go in and manually retry the charge.
$stripe = new \Stripe\StripeClient(config('XXXXXXXXXXXXXXXXX'));
$item = $stripe->invoiceItems->create([
'customer' => $subscription->stripe_customer,
'amount' => $total,
'currency' => 'usd',
'description' => 'Erro Coffee Subscription ' . $subscription_type->bags
]);
$invoice = $stripe->invoices->create([
'customer' => $subscription->stripe_customer,
'collection_method' => 'charge_automatically',
'auto_advance' => true,
"metadata" => [
"order_type" => "subscription",
"subscription_refill" => "true",
"subscription_id" => $subscription->id
]
]);

Stripe sca subscription php and charge automatically

I have implemented the Stripe SCA migration
my serve side code to create payment intent is as follows
$intent = \Stripe\PaymentIntent::create([
'payment_method' => $requestData['payment_method_id'],
'amount' => $requestData->amount,
'currency' => $requestData->currencyIso,
'payment_method_types' => ['card'],
'confirmation_method' => "manual",
'confirm' => true,
'setup_future_usage'=>"off_session",
]);
return $intent;
And my js code is as below to create card payment and handle response
stripe.createPaymentMethod('card', cardElement, {
billing_details: {name: cardholderName.value}
})
and handle the response:
function handleServerResponse(response) {
if (response.error) {
} else if (response.requires_action) {
stripe.handleCardAction(
response.payment_intent_client_secret
).then(function(result) {
if (result.error) {
} else {
// The card action has been handled
// The PaymentIntent can be confirmed again on the server
fetch('https://test.com/server.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payment_method_id: result.paymentMethod.id,
amount: amount
})
}).then(function(confirmResult) {
console.log(confirmResult);
return confirmResult.json();
}).then(handleServerResponse);
}
});
}
}
}
$plan = Plan::create([
'currency' => $currency,
'interval' => 'month',
'product' => $product->id,
'nickname' => 'Payment Plan for order - ' . $order_id,
'amount' => $price,
'metadata' => [
'order_number' => $order_id,
'bgt_customer_number' => $customer_id,
],
]);
$schedule = SubscriptionSchedule::create([
'customer' => $customer->id,
'start_date' => 'now',
'end_behavior' => 'cancel',
'metadata' => [
'local_id' => $order_id,
'local_account_id' => $account_id,
'local_currency_id' => $currency_id,
'local_user_id'=> $user_id,
'local_installments_total' => $plan_length,
'bgt_customer_number' => $customer_id,
'order_number' => $order_id,
'invoice_description' => 'Payment Plan for order - ' . $order_id
],
'phases' => [
[
'items' => [
[
'plan' => $plan->id,
],
],
'collection_method'=> 'charge_automatically',
'iterations'=>$plan_length
],
],
]);
$metadata = [
'installments_paid' => '0',
'installments_total' => $plan_length,
'local_id' =>$order_id,
'local_account_id' =>$account_id,
'local_currency_id' =>$currency_id,
'subscription_schedule_id' => $schedule->id
];
$subscription_id=$schedule->subscription;
$result=Subscription::update($subscription_id,
[
'metadata' => $metadata,
'default_payment_method' => $paymentMethodParams['payment_method'],
'proration_behavior' =>'always_invoice',
//Create only after adding payment method id in customer
]
);
I am getting the SCA modal for sca cards and workflow is correct.
But what concerns me is testing the subscription created using sca
I tested using 4000000000003220 amd also 424242424242424.. and subscription is created with 3 installments.
The subscription is created with correct installments:
But what concerns me is the subscription first installment is not being charged immediately.
Invoice shows as:
This draft invoice was generated by a subscription. It can be edited
until it's automatically finalized in 1 hour.
.
When I try to complete the charge from stripe dashboard (as a part of test) it shows
The invoice was finalized, but the customer needs to complete 3D
Secure authentication for the payment to succeed. This additional
step is required by their bank to prevent fraud. 3D Secure emails are
disabled in your settings, so you'll need to notify the customer
manually
The invoice failed to capture.
Question:
1: I wanted the first installment or schedule to happen.
How do I accomplish this in staging env. Am I missing any points here in creating subscription or SCA methods?
2: What is really 'confirmation_method' => "manual". Really confused.
When using Stripe Subscriptions you typically won't be creating Payment Intents yourself. Instead you'll be creating and interacting with Billing objects like Subscriptions and Invoices. The Invoices will create the Payment Intents for you. Stripe has a guide that walks you through taking payment information and creating a Subscription. If you read through and follow that guide you should be able to build what you want.
To answer your specific questions:
Question: 1: I wanted the first installment or schedule to happen. How do I accomplish this in staging env. Am I missing any points here in creating subscription or SCA methods?
This is likely because you're creating a Payment Intent and paying that which is unrelated to the Payment Intent belonging to the Invoice of your Subscription.
2: What is really 'confirmation_method' => "manual". Really confused.
The confirmation_method property controls how a Payment Intent can be confirmed. However, if you're using Subscriptions and want to support SCA you should ignore this property and instead follow the guide linked above.

PHP Stripe: You can not pass `payment_intent_data` in `subscription` mode

I'm trying to create a subscription for the merchant's users but facing "You can not pass payment_intent_data in subscription mode" error. With regular payments, it works well, but subscriptions aren't working.
Here is an example of what I want to do: John has an e-commerce shop based on recurring billing. Matthew is John's customer and wants to purchase a subscription from John. How can I easily take fees and transfer money to John's connect account while using "Stripe Checkout"?
$session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => [[
'price' => $priceIntent->id,
'quantity' => 1,
]],
'customer' => Auth::User() -> stripe_code,
'mode' => 'subscription',
'payment_intent_data' => [
'application_fee_amount' => $total_fees,
'transfer_data' => [
'destination' => $merchantId,
],
],
'success_url' => env('APP_URL') . '/order/success/{CHECKOUT_SESSION_ID}',
'cancel_url' => env('APP_URL') . '/order/cancel/',
]);
Thanks!
Basically, you can't use payment_intent_data on a Checkout Session in subscription mode, since the subscription creates invoices instead of PaymentIntents.
To do this, you need to use the subscription_data hash: https://stripe.com/docs/api/checkout/sessions/create?lang=php#create_checkout_session-subscription_data-application_fee_percent and specify the merchant account.
Example of the API call with merchant details:
$session = \Stripe\Checkout\Session::create([
'subscription_data' => [
'application_fee_percent' => $fees_percent,
],
],array("stripe_account" => "acct_xxxxxxxxx"));
Also, don't forget to pass all the other required variables in the call.
Cheers :)

How to authorize with Stripe 3d Secure while Subscription type is Trailing

I want the 3d-secure modal authorization check with subscription type = trialing.
I am following this link to setup stripe subscription. When I create subscription without 'trial_period_days' the 3d-secure authorization modal pops-up as subscription status becomes 'incomplete'.
But when i pass >trial_period_days and 'payment_behavior' => 'allow_incomplete', modal don't work as subscription status becomes 'active'.
How can i show authorization modal when subscription is trialing?
I have seen this link https://stripe.com/docs/payments/3d-secure#manual-three-ds too, but no progress.
Suggest me a way to implement this.
Here is my code:
public function createCustomer($token) {
\Stripe\Stripe::setApiKey(secretKey);
$customer = \Stripe\Customer::create([
'email' => 'any_email#domain.com',
'source' => $token,
]);
return $this->createSubscription($customer, $token);
}
public function createSubscription($customer, $token) {
$plan_id = $this->getPlanId();
$payment_intent = $this->createSetupIntent($customer->id, $token);
$subscription = \Stripe\Subscription::create([
'customer' => $customer->id,
'items' => [
[
'plan' => $plan->id,
],
],
'trial_period_days' => 14,
'expand' => ['latest_invoice.payment_intent'],
'payment_behavior' => 'allow_incomplete',
]);
return [
'subscription' => $subscription,
'payment_intent' => $payment_intent
];
}
public function createSetupIntent($customer_id, $token) {
$client = new Client();
$url = "https://api.stripe.com/v1/setup_intents";
$response = $client->request('POST', $url, [
'auth' => ['sk_test_key', ''],
'form_params' => [
'customer' => $customer_id,
'payment_method_types' => ["card"],
'payment_method_options' => [
"card" => [
"request_three_d_secure" => "any"
]
]
],
'timeout' => 10.0
]);
$setup_intent = $response->getBody()->getContents();
return json_decode($setup_intent, true);
}
I expect 3d-secure authorization check modal too when i set subscription to trialing.
What you are describing is a scenario in Stripe doc
Basically when you create a subscription with trial period, since there is no immediate payment occurs, there will be no 3DS authentication needed.
The authentication is delayed until the trial period end.
To ask the user for authentication so that when the trial end there will be no need for 3DS authentication, when a subscription is created with trial period, The subscription will have a pending_setup_intent attribute
You could use that pending_setup_intent to ask the user to complete the authentication. You don't have to create a setup Intent explicitly. What you can do is to check the status within the subscription.
If subscription is in trialing, check if there is a pending_setup_intent, if so, pass to the pending_setup_intent.client_secret to the frontend where your customer is subscribing to your product, and call Stripe.js handleCardSetup
stripe.handleCardSetup(psi.client_secret)
.then(siResult => {
log({siResult});
}).catch(err => {
log({siErr: err});
});
When the card is setup and the trial ends, there will be less likely that the charge will need to go through 3DS authentication again.
You could use Stripe Test Card, 4000002500003155 is good for this testing.
You could simulate the trial end by updating the subscription with
trial_end: "now"
off_session: true // This is needed because by default, subscription update is considered on_session
Hope the above helps

Braintree marketplace - What happens if Master Merchant do charge client at time time of sale, but has to pay to submerchant

I have implemented PHP braintree API in a project, I want to use Marketplace api for the same.
Now, we have promotional events and we do not charge client, but we have to pay amount to sub-merchant who has delivered goods.
So below is the code to add service fees, which is clear that at the time of sale we have to add sub-merchant id for merchantAccountId, amount will be payment charged from client, what is paymentMethodNonce?
$result = Braintree_Transaction::sale(array(
'merchantAccountId' => 'provider_sub_merchant_account',
'amount' => '10.00',
'paymentMethodNonce' => 'nonce-from-the-client',
'serviceFeeAmount' => "1.00"
));
Another query is, at the time of sale we have to pass credit card details of the client? What if client is already in vault ?
Below is another code from Braintree document with creditCard details
$result = Braintree_Transaction::sale(
array(
'amount' => "100",
'merchantAccountId' => "blue_ladders_store",
'creditCard' => array(
'number' => "4111111111111111",
'expirationDate' => "12/20",
),
'options' => array(
'submitForSettlement' => true,
'holdInEscrow' => true,
),
'serviceFeeAmount' => "10.00"
)
);
If we do not add creditCard number and have to pay sub-merchant then how can that be done.
Thanks

Categories