Amount must be greater than zero, PayPal/Braintree payment - php

I have an app that sells events, some events have zero-dollar price. Thus, I receive this message from PayPal:
Amount must be greater than zero
However, if I remove the need to make a payment, one user can order all the tickets for an event. So, what's the way to make users go through the payment process without making a charge.
Bear in mind, it'd be great to make as little change to the process in terms of generating a Nonce from frontend and sending it to backend to make the charge it.
I'm using this payment scenario.
This is the function that returns the error message:
public function chargeNonce($nonce) {
$gateway = $this->createPayPalGateway();
$amount = $this->payment_params['number_of_tickets'] * $this->payment_params['event_price'];
$result = $gateway->transaction()->sale([
'amount' => $amount,
'paymentMethodNonce' => $nonce,
'options' => [
'submitForSettlement' => True,
'paypal' => [
'description' => "Billing Notification",// For email receipt
],
],
]);
dd($result);
return $result;
}

Related

Trying to charge before or after Subscription Schedule is created Stripe API

I am developing a wordpress website for a client.
He needs different types of packages. For most of these packages I developed a simple Stripe checkout webpage, using its documentation.
The problem is that I need this workflow:
first month x dollars
second month x dollars
after one year subscription y dollars
I've already done this using Subscription Schedule. But it needs a customer ofc. How can I charge before and after charging create this Subscription Schedule? I don't know how to deal with this, how to charge using Stripe checkout simple already built page or do I need to create one by myself, where user needs to add his card, pay, and get the customer_id?
function checkout3() {
// Set your secret key. Remember to switch to your live secret key in production.
// See your keys here: https://dashboard.stripe.com/apikeys
\Stripe\Stripe::setApiKey('sk_test_51e7DRPLRnISGb5vSFxnvvuDx1GzhlBIFeazcmpEevsUFf29jHXJ1YgE2xaJ1lGfzjtKzE8uoN0eR9Klaq00CnMFWvfB');
// The price ID passed from the front end.
// $priceId = $_POST['priceId'];
$priceId = 'price_1LPahmIne7DRPLRnFXV6Uz34';
$futureDate= strtotime(date('Y-m-d', strtotime('+1 year')));
$customer = \Stripe\Customer::create(
[
'description' => 'My First Test Customer (created for API docs at https://www.stripe.com/docs/api)',
]
);
$session = \Stripe\SubscriptionSchedule::create([
'customer' => $customer["id"],
'start_date' => 'now',
'end_behavior' => 'release',
'phases' => [
[
'items' => [
[
'price' => 'price_1LRF5CIne7DRPLRnwuLVE2pu',
'quantity' => 1,
],
],
//'end_date' => $futureDate,
'iterations' => 1,
],
[
'items' => [
[
'price' => 'price_1LRF5cIne7DRPLRngevvIZiw',
'quantity' => 1,
],
],
'iterations' => 1,
],
[
'items' => [
[
'price' => 'price_1LPujQIne7DRPLRnj3EOweJN',
'quantity' => 1,
],
],
],
],
]);
// Redirect to the URL returned on the Checkout Session.
// With vanilla PHP, you can redirect with:
//header("HTTP/1.1 303 See Other");
//header("Location: " . '$session->url');
}
So right now, the subscription schedule is added to the Stripe dashboard, the page it's keep loading infinitely, but without charging... How to deal with this?
public static function firebase_checkout3_func() {
$html = "";
$html .= "<form id='firebase-checkout' action='/wp-json/api/checkout2' method='POST'>
<button type='submit' id='checkout-button'>Începe acum</button>
</form>";
return $html;
}
Use Stripe hosted Checkout Page with setup mode to collect
a Customer's SetupIntent
Retrieve the Payment Method inside the SetupIntent
Set it as the customer's invoice_settings.default_payment_method
Create a Subscription Schedule with that Customer Id as normal

Stripe Checkout for recurring donations of variable amounts

I am trying to use Stripe Checkout to allow users to set up a monthly recurring donation for an amount of their choosing. When I set up the Session, it provides the correct inputs to the Stripe form, however when I look in Subscriptions in the back end of Stripe, nothing is created, and it seems to just take a single payment. Here is my code:
$checkout_values['success_url'] = $success_url;
$checkout_values['cancel_url'] = $cancel_url;
$checkout_values['payment_method_types'] = ['card','sepa_debit'];
$checkout_values['mode'] = 'subscription';
$checkout_values['metadata']['order_id'] = $order_id;
//Single line item for the dynamically created subscription and price info
$line_item = [
'price_data' => [
'recurring' => [
'interval' => 'month',
'interval_count' => 1
],
'currency' => $order->get_currency(),
'product_data' => [
'name' => $item->get_name()
],
'unit_amount' => $item_total
],
'quantity' => 1,
];
$checkout_values['line_items'][] = $line_item;
Maybe I need to create a subscription in Stripe and tie that in? In which case why doesn't it give me an error?
you don't need to explicitly create a subscription object. If you use Stripe Checkout, a subscription will be automatically created when your customer completed the payment flow in your checkout page.
You might want to firstly check if you are viewing the right mode (either Live or Test), and then take a look at the Dashboard Events to confirm subscription related events. You application can also listen to webhook events to get notified.

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.

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

Sale transaction to multiple users at once with Braintree along with PHP

Is this possible with Braintree(payment gateway) along with PHP to make a sale transaction for all the users at once.
Like i want to make sale transaction only when minimum number of users are subscribed.
Here is the which I am trying to use:
// Make a sale transaction for all the users at once.
for ($x = 0; $x <= count($users); $x++) {
$result = \Braintree\Transaction::sale(array(
'amount' => Session::get('price'),
'paymentMethodNonce' => $request->payment_method_nonce,
'customer' => [
'id' => $user->id,
],
'options' => [
'submitForSettlement' => True,
]
));
}
Nonces are one time use only, so the only way this would work is if you used payment method tokens, which you would have to return from each customer's result object.

Categories