Changeset View
Changeset View
Standalone View
Standalone View
src/app/Providers/Payment/Mollie.php
Show First 20 Lines • Show All 332 Lines • ▼ Show 20 Lines | public function webhook(): int | ||||
$payment = Payment::find($payment_id); | $payment = Payment::find($payment_id); | ||||
if (empty($payment)) { | if (empty($payment)) { | ||||
// Mollie recommends to return "200 OK" even if the payment does not exist | // Mollie recommends to return "200 OK" even if the payment does not exist | ||||
return 200; | return 200; | ||||
} | } | ||||
try { | |||||
// Get the payment details from Mollie | // Get the payment details from Mollie | ||||
// TODO: Consider https://github.com/mollie/mollie-api-php/issues/502 when it's fixed | // TODO: Consider https://github.com/mollie/mollie-api-php/issues/502 when it's fixed | ||||
$mollie_payment = mollie()->payments()->get($payment_id); | $mollie_payment = mollie()->payments()->get($payment_id); | ||||
$refunds = []; | $refunds = []; | ||||
if ($mollie_payment->isPaid()) { | if ($mollie_payment->isPaid()) { | ||||
// The payment is paid. Update the balance, and notify the user | // The payment is paid. Update the balance, and notify the user | ||||
if ($payment->status != self::STATUS_PAID && $payment->amount > 0) { | if ($payment->status != self::STATUS_PAID && $payment->amount > 0) { | ||||
$credit = true; | $credit = true; | ||||
$notify = $payment->type == self::TYPE_RECURRING; | $notify = $payment->type == self::TYPE_RECURRING; | ||||
} | } | ||||
// The payment has been (partially) refunded. | // The payment has been (partially) refunded. | ||||
// Let's process refunds with status "refunded". | // Let's process refunds with status "refunded". | ||||
if ($mollie_payment->hasRefunds()) { | if ($mollie_payment->hasRefunds()) { | ||||
foreach ($mollie_payment->refunds() as $refund) { | foreach ($mollie_payment->refunds() as $refund) { | ||||
if ($refund->isTransferred() && $refund->amount->value) { | if ($refund->isTransferred() && $refund->amount->value) { | ||||
$refunds[] = [ | $refunds[] = [ | ||||
'id' => $refund->id, | 'id' => $refund->id, | ||||
'description' => $refund->description, | 'description' => $refund->description, | ||||
'amount' => round(floatval($refund->amount->value) * 100), | 'amount' => round(floatval($refund->amount->value) * 100), | ||||
'type' => self::TYPE_REFUND, | 'type' => self::TYPE_REFUND, | ||||
'currency' => $refund->amount->currency | 'currency' => $refund->amount->currency | ||||
]; | ]; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// The payment has been (partially) charged back. | // The payment has been (partially) charged back. | ||||
// Let's process chargebacks (they have no states as refunds) | // Let's process chargebacks (they have no states as refunds) | ||||
if ($mollie_payment->hasChargebacks()) { | if ($mollie_payment->hasChargebacks()) { | ||||
foreach ($mollie_payment->chargebacks() as $chargeback) { | foreach ($mollie_payment->chargebacks() as $chargeback) { | ||||
if ($chargeback->amount->value) { | if ($chargeback->amount->value) { | ||||
$refunds[] = [ | $refunds[] = [ | ||||
'id' => $chargeback->id, | 'id' => $chargeback->id, | ||||
'amount' => round(floatval($chargeback->amount->value) * 100), | 'amount' => round(floatval($chargeback->amount->value) * 100), | ||||
'type' => self::TYPE_CHARGEBACK, | 'type' => self::TYPE_CHARGEBACK, | ||||
'currency' => $chargeback->amount->currency | 'currency' => $chargeback->amount->currency | ||||
]; | ]; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// In case there were multiple auto-payment setup requests (e.g. caused by a double | // In case there were multiple auto-payment setup requests (e.g. caused by a double | ||||
// form submission) we end up with multiple payment records and mollie_mandate_id | // form submission) we end up with multiple payment records and mollie_mandate_id | ||||
// pointing to the one from the last payment not the successful one. | // pointing to the one from the last payment not the successful one. | ||||
// We make sure to use mandate id from the successful "first" payment. | // We make sure to use mandate id from the successful "first" payment. | ||||
if ( | if ( | ||||
$payment->type == self::TYPE_MANDATE | $payment->type == self::TYPE_MANDATE | ||||
&& $mollie_payment->mandateId | && $mollie_payment->mandateId | ||||
&& $mollie_payment->sequenceType == Types\SequenceType::SEQUENCETYPE_FIRST | && $mollie_payment->sequenceType == Types\SequenceType::SEQUENCETYPE_FIRST | ||||
) { | ) { | ||||
$payment->wallet->setSetting('mollie_mandate_id', $mollie_payment->mandateId); | $payment->wallet->setSetting('mollie_mandate_id', $mollie_payment->mandateId); | ||||
} | } | ||||
} elseif ($mollie_payment->isFailed()) { | } elseif ($mollie_payment->isFailed()) { | ||||
// Note: I didn't find a way to get any description of the problem with a payment | // Note: I didn't find a way to get any description of the problem with a payment | ||||
\Log::info(sprintf('Mollie payment failed (%s)', $payment->id)); | \Log::info(sprintf('Mollie payment failed (%s)', $payment->id)); | ||||
// Disable the mandate | // Disable the mandate | ||||
if ($payment->type == self::TYPE_RECURRING) { | if ($payment->type == self::TYPE_RECURRING) { | ||||
$notify = true; | $notify = true; | ||||
$payment->wallet->setSetting('mandate_disabled', 1); | $payment->wallet->setSetting('mandate_disabled', 1); | ||||
} | } | ||||
} | } | ||||
DB::beginTransaction(); | DB::beginTransaction(); | ||||
// This is a sanity check, just in case the payment provider api | // This is a sanity check, just in case the payment provider api | ||||
// sent us open -> paid -> open -> paid. So, we lock the payment after | // sent us open -> paid -> open -> paid. So, we lock the payment after | ||||
// recivied a "final" state. | // recivied a "final" state. | ||||
$pending_states = [self::STATUS_OPEN, self::STATUS_PENDING, self::STATUS_AUTHORIZED]; | $pending_states = [self::STATUS_OPEN, self::STATUS_PENDING, self::STATUS_AUTHORIZED]; | ||||
if (in_array($payment->status, $pending_states)) { | if (in_array($payment->status, $pending_states)) { | ||||
$payment->status = $mollie_payment->status; | $payment->status = $mollie_payment->status; | ||||
$payment->save(); | $payment->save(); | ||||
} | } | ||||
if (!empty($credit)) { | if (!empty($credit)) { | ||||
self::creditPayment($payment, $mollie_payment); | self::creditPayment($payment, $mollie_payment); | ||||
} | } | ||||
foreach ($refunds as $refund) { | foreach ($refunds as $refund) { | ||||
$this->storeRefund($payment->wallet, $refund); | $this->storeRefund($payment->wallet, $refund); | ||||
} | } | ||||
DB::commit(); | DB::commit(); | ||||
if (!empty($notify)) { | if (!empty($notify)) { | ||||
\App\Jobs\PaymentEmail::dispatch($payment); | \App\Jobs\PaymentEmail::dispatch($payment); | ||||
} | } | ||||
} catch (\Mollie\Api\Exceptions\ApiException $e) { | |||||
\Log::warning(sprintf('Mollie api call failed (%s)', $e->getMessage())); | |||||
} | |||||
return 200; | return 200; | ||||
} | } | ||||
/** | /** | ||||
* Get Mollie customer identifier for specified wallet. | * Get Mollie customer identifier for specified wallet. | ||||
* Create one if does not exist yet. | * Create one if does not exist yet. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 183 Lines • Show Last 20 Lines |