Changeset View
Standalone View
src/app/Http/Controllers/API/PaymentsController.php
<?php | <?php | ||||
namespace App\Http\Controllers\API; | namespace App\Http\Controllers\API; | ||||
use App\Payment; | use App\Transaction; | ||||
use App\Wallet; | use App\Wallet; | ||||
use App\Http\Controllers\Controller; | use App\Http\Controllers\Controller; | ||||
use Illuminate\Http\Request; | use Illuminate\Http\Request; | ||||
use Illuminate\Support\Facades\Auth; | use Illuminate\Support\Facades\Auth; | ||||
use Illuminate\Support\Facades\Validator; | use Illuminate\Support\Facades\Validator; | ||||
class PaymentsController extends Controller | class PaymentsController extends Controller | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | class PaymentsController extends Controller | ||||
* Update payment status (and balance). | * Update payment status (and balance). | ||||
* | * | ||||
* @param \Illuminate\Http\Request $request The API request. | * @param \Illuminate\Http\Request $request The API request. | ||||
* | * | ||||
* @return \Illuminate\Http\Response The response | * @return \Illuminate\Http\Response The response | ||||
*/ | */ | ||||
public function webhook(Request $request) | public function webhook(Request $request) | ||||
{ | { | ||||
$db_payment = Payment::find($request->id); | $db_payment = Transaction::where('mollie_id', $request->id)->first(); | ||||
// 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 | ||||
if (empty($db_payment)) { | if (empty($db_payment)) { | ||||
return response('Success', 200); | return response('Success', 200); | ||||
} | } | ||||
// Get the payment details from Mollie | // Get the payment details from Mollie | ||||
$payment = mollie()->payments()->get($request->id); | $payment = mollie()->payments()->get($request->id); | ||||
if (empty($payment)) { | if (empty($payment)) { | ||||
return response('Success', 200); | return response('Success', 200); | ||||
} | } | ||||
if ($payment->isPaid()) { | if ($payment->isPaid()) { | ||||
if (!$payment->hasRefunds() && !$payment->hasChargebacks()) { | if (!$payment->hasRefunds() && !$payment->hasChargebacks()) { | ||||
// The payment is paid and isn't refunded or charged back. | // The payment is paid and isn't refunded or charged back. | ||||
// Update the balance, if it wasn't already | // Update the balance, if it wasn't already | ||||
if ($db_payment->status != 'paid') { | if ($db_payment->mollie_status != 'paid') { | ||||
$db_payment->wallet->credit($db_payment->amount); | $db_payment->wallet->credit($db_payment->amount); | ||||
Transaction::create( | |||||
machniak: We create transaction whenever we do credit/debit a wallet. I propose to extend credit() and… | |||||
vanmeeuwenAuthorUnsubmitted Done Inline ActionsI'm actually thinking of extending the transaction table with a 'type' string, such that we can also transcribe 'User account jane@kolab.org was created (by john@kolab.org)', etc. vanmeeuwen: I'm actually thinking of extending the transaction table with a 'type' string, such that we can… | |||||
[ | |||||
'wallet_id' => $db_payment->wallet->id, | |||||
'amount' => $db_payment->amount, | |||||
'description' => "Added credit: {$db_payment->amount}" | |||||
machniakUnsubmitted Not Done Inline ActionsI'd expect this description more explanatory, and amount is redundant there. machniak: I'd expect this description more explanatory, and amount is redundant there. | |||||
vanmeeuwenAuthorUnsubmitted Done Inline ActionsYes and yes. vanmeeuwen: Yes and yes. | |||||
] | |||||
); | |||||
} | } | ||||
} elseif ($payment->hasRefunds()) { | } elseif ($payment->hasRefunds()) { | ||||
// The payment has been (partially) refunded. | // The payment has been (partially) refunded. | ||||
// The status of the payment is still "paid" | // The status of the payment is still "paid" | ||||
// TODO: Update balance | // TODO: Update balance | ||||
} elseif ($payment->hasChargebacks()) { | } elseif ($payment->hasChargebacks()) { | ||||
// The payment has been (partially) charged back. | // The payment has been (partially) charged back. | ||||
// The status of the payment is still "paid" | // The status of the payment is still "paid" | ||||
// TODO: Update balance | // TODO: Update balance | ||||
} | } | ||||
} | } | ||||
// 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 it's paid. | // sent us open -> paid -> open -> paid. So, we lock the payment after it's paid. | ||||
if ($db_payment->status != 'paid') { | if ($db_payment->mollie_status != 'paid') { | ||||
$db_payment->status = $payment->status; | $db_payment->mollie_status = $payment->status; | ||||
$db_payment->save(); | $db_payment->save(); | ||||
} | } | ||||
return response('Success', 200); | return response('Success', 200); | ||||
} | } | ||||
/** | /** | ||||
* Charge a wallet with a "recurring" payment. | * Charge a wallet with a "recurring" payment. | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | class PaymentsController extends Controller | ||||
* Create a payment record in DB | * Create a payment record in DB | ||||
* | * | ||||
* @param object $payment Mollie payment | * @param object $payment Mollie payment | ||||
* @param string $wallet_id Wallet ID | * @param string $wallet_id Wallet ID | ||||
* @param int $amount Amount of money in cents | * @param int $amount Amount of money in cents | ||||
*/ | */ | ||||
protected static function storePayment($payment, $wallet_id, $amount): void | protected static function storePayment($payment, $wallet_id, $amount): void | ||||
{ | { | ||||
$db_payment = new Payment(); | $db_payment = Transaction::create( | ||||
machniakUnsubmitted Not Done Inline ActionsI'm not sure about this. Looks like for finished (paid) payments we'll end up with two transactions. Do we really need two records here? I would rather have one. Then we'd maybe need some "incomplete" state column. machniak: I'm not sure about this. Looks like for finished (paid) payments we'll end up with two… | |||||
vanmeeuwenAuthorUnsubmitted Done Inline ActionsThe ->mollie_status field is what is updated, just like it was for Payment. The duplicate record is indeed the accreditation against the wallet -- but I think that is still needed in that the underlying financial transactions with references to Mollie/Stripe/Paypal customer IDs and the likes are receipt details. vanmeeuwen: The `->mollie_status` field is what is updated, just like it was for Payment.
The duplicate… | |||||
$db_payment->id = $payment->id; | [ | ||||
$db_payment->description = $payment->description; | 'wallet_id' => $wallet_id, | ||||
$db_payment->status = $payment->status; | 'amount' => $amount, | ||||
$db_payment->amount = $amount; | 'description' => $payment->description, | ||||
$db_payment->wallet_id = $wallet_id; | 'mollie_id' => $payment->id, | ||||
$db_payment->save(); | 'mollie_status' => $payment->status | ||||
] | |||||
); | |||||
} | } | ||||
} | } |
We create transaction whenever we do credit/debit a wallet. I propose to extend credit() and debit() methods with 2nd argument being a transaction description, and create transactions there.