Closed jponsen closed 4 years ago
There's no simple call for this yet. But there are two ways to accomplish the use case you describe here. (Note that an iDEAL payment results in a directdebit
mandate.)
If you want to verify the new card through Mollie's checkout and are ok with a minimum payment amount, you can use the FirstPaymentBuilder with either an AddGenericOrderItem action or an AddBalance action.
Alternatively you can create a new direct debit mandate for the customer using the Mollie mandates API. For directdebit
this can even be done without having the customer go through checkout. Make sure to update the new mandateId
on the billable model.
@sandervanhooft Thanks for your detailled reply. I just noticed I didn't mention that we also need the possibility to change between directdebit
and creditcard
. What is the way to go for this?
Is there any way using this package (or another way) to retrieve a new mandate for the other payment method? Since we don't know the Users credit card details I should probably redirect the user to the Mollie checkout screen. But what if the user already have a subscription they paid for (and are we able to lower the first payment price to €0.01 if this step needs to be done)?
It's a common use case. But it can also get a bit complicated as you are hinting here 😄 .
Adding a default method for this is on the roadmap. The Spark-Mollie launch (May 28th) comes first though - and I'm still recovering from what probably was covid-19.
Personally I'm always redirecting the customer through Mollie's checkout to be sure that the new card is verified. In many cases it is more customer-friendly to add the minimum payment amount to the customer's balance.
Here's an excerpt of one of my implementations. If it's helpful to you, consider converting it into a pull request.
<?php
use Laravel\Cashier\FirstPayment\Actions\AddBalance;
use Laravel\Cashier\FirstPayment\Actions\AddGenericOrderItem;
use Laravel\Cashier\FirstPayment\FirstPaymentBuilder;
use Money\Money;
class UpdateMolliePaymentMethod
{
protected $shouldAddFeeToBalance = false;
public function handle($billable, array $data)
{
$payment = (new FirstPaymentBuilder($billable, $this->getFirstPaymentOptions($billable)))
->setRedirectUrl('/payment-method')
->inOrderTo($this->getPaymentActions($billable, $data))
->create();
$payment->redirectUrl = url('/redirects/mollie/update-payment-method/' . $payment->id);
$payment->update();
return response([
'data' => [
'checkoutUrl' => $payment->getCheckoutUrl(),
],
]);
}
protected function getFirstPaymentOptions($billable)
{
return [
// set any options specific to your use case here
];
}
protected function getPaymentActions($billable, $data)
{
if($this->shouldAddFeeToBalance) {
return $this->getAddToBalanceActions($billable, $data);
}
// VAT is involved if the payment is not used for adding balance
$subtotal = $this->subtotalForTotalIncludingTax(
mollie_array_to_money(config('cashier.first_payment.amount')),
$billable->taxPercentage() * 0.01
);
return [ new AddGenericOrderItem($billable, $subtotal, __("Payment method updated")) ];
}
protected function getAddToBalanceActions($billable, $data)
{
return [
new AddBalance(
$billable,
mollie_array_to_money(config('cashier.first_payment.amount')),
__("Payment method updated")
)
];
}
protected function subtotalForTotalIncludingTax(Money $total, float $taxPercentage)
{
$vat = $total->divide(1 + $taxPercentage)->multiply($taxPercentage);
return $total->subtract($vat);
}
}
Closing this for now, let me now if it should be reopened.
Hi , i am newbie for that package but i enjoy it , and for my project i need to update payment method . Can someone help me to do it pls , i dont understand how you can do it with the class quoted above . It will be vey nice , thank you in advance !
@blankflo At what point do you get stuck?
<?php
use Laravel\Cashier\FirstPayment\Actions\AddBalance;
use Laravel\Cashier\FirstPayment\Actions\AddGenericOrderItem;
use Laravel\Cashier\FirstPayment\FirstPaymentBuilder;
use Money\Money;
class UpdateMolliePaymentMethod
{
protected $shouldAddFeeToBalance = false;
public function handle($billable, array $data)
{
$payment = (new FirstPaymentBuilder($billable, $this->getFirstPaymentOptions($billable)))
->setRedirectUrl('/payment-method')
->inOrderTo($this->getPaymentActions($billable, $data))
->create();
$payment->redirectUrl = url('/redirects/mollie/update-payment-method/' . $payment->id);
$payment->update();
return response([
'data' => [
'checkoutUrl' => $payment->getCheckoutUrl(),
],
]);
}
protected function getFirstPaymentOptions($billable)
{
return [
// set any options specific to your use case here
];
}
protected function getPaymentActions($billable, $data)
{
if($this->shouldAddFeeToBalance) {
return $this->getAddToBalanceActions($billable, $data);
}
// VAT is involved if the payment is not used for adding balance
$subtotal = $this->subtotalForTotalIncludingTax(
mollie_array_to_money(config('cashier.first_payment.amount')),
$billable->taxPercentage() * 0.01
);
return [ new AddGenericOrderItem($billable, $subtotal, __("Payment method updated")) ];
}
protected function getAddToBalanceActions($billable, $data)
{
return [
new AddBalance(
$billable,
mollie_array_to_money(config('cashier.first_payment.amount')),
__("Payment method updated")
)
];
}
protected function subtotalForTotalIncludingTax(Money $total, float $taxPercentage)
{
$vat = $total->divide(1 + $taxPercentage)->multiply($taxPercentage);
return $total->subtract($vat);
}
}
I dont understand how to update payment method with you're implementation . I am sorry but i'am a beginner . I have "billing page" and the customers can change their payment method with a button . My first idea was to revoke the mandate with mollie api and then make a first payment but the customer is still on active subscriptions , and the funtion return a subscription .
Btw , awesome work for the package !
The above handle
method is for a SPA (derived from Spark in fact).
If you're using Blade, you could do something like this:
public function handle($billable, array $data)
{
$payment = (new FirstPaymentBuilder($billable, $this->getFirstPaymentOptions($billable)))
->setRedirectUrl('/payment-method')
->inOrderTo($this->getPaymentActions($billable, $data))
->create();
$payment->redirectUrl = url('/redirects/mollie/update-payment-method/' . $payment->id);
$payment->update();
return redirect($payment->getCheckoutUrl());
}
~Make sure to add the /payment-method
route and a matching webhook controller that takes care of updating the mandate id on the billable (user) model in case of a successful payment.~
Ok thank you . So in fact , I have to do a controller which can grab new mandate ID and update it on the billable . Then because mollie is asynchronous i have to do a route to redirect when the payment is succesful ? How can i call the function above ? in my webhook Controller ?
Sorry, I made a mistake above. No need to set up another route / webhook.
Call the handle
method described above when your "update" button gets clicked, the customer will then get redirected to Mollie's checkout. Once the payment gets status "paid" the mandateId will automatically get updated on the billable model.
Ok it's more clear now . Last questions , i call the handle function without parameters because they are define by default in the construct of TheFirstPaymentBuilder class ? Thank you for you're help and the work . I will test the solution now . I will publish the solution in the issue who i have created for everyone see it . Have a nice day .
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Hash;
use App\Models\User;
use Laravel\Cashier\FirstPayment\Actions\AddBalance;
use Laravel\Cashier\FirstPayment\Actions\AddGenericOrderItem;
use Laravel\Cashier\FirstPayment\FirstPaymentBuilder;
use Money\Money;
class UpdateMolliePayment extends Controller{
public function handle()
{
$billable = Auth::user();
$payment = (new FirstPaymentBuilder($billable))
->setRedirectUrl('/billing')
->inOrderTo($this->getAddToBalanceActions($billable))
->create();
$payment->update();
return redirect($payment->getCheckoutUrl());
}
protected function getAddToBalanceActions($billable)
{
return [
new AddBalance(
$billable,
mollie_array_to_money(config('cashier.first_payment.amount')),
__("Payment method updated")
)
];
}
}
I cant test in live for now but it seems working , what do you think about it @sandervanhooft ?
Looks good to me!
Thank you very much for your help ! If it's works in live mode , i will say it here . Have a good evening .
Hi,
I'm currently working on a project where we need to have the possibility to swap the issuer of the payment. Example:
Customer has registered and used iDeal with Rabobank (issuer) for first payment, and wants to switch to ING after one month.
Is there any way to switch this using a built in function? When selecting a new issuer and calling the
newSubscription
method, aSubscription
is returned instead of aRedirectToCheckoutResponse
(which is needed to retrieve a new mandate).Any help would be appreciated!
Thanks.