Fix: Bank transfer orders should stay in awaiting payment
This commit is contained in:
@@ -9,30 +9,47 @@ use Modules\Core\Payment\Contracts\SupportsPay;
|
||||
use Modules\Core\Payment\Contracts\SupportsRefunds;
|
||||
use Modules\Core\Payment\DTOs\PaymentResult;
|
||||
use Modules\Core\Payment\Enums\PaymentResultStatus;
|
||||
use Modules\Core\Payment\Events\PaymentCaptured;
|
||||
use Modules\Core\Payment\Events\PaymentRefunded;
|
||||
|
||||
/**
|
||||
* Manual/attested, same trust model as OfflinePaymentDriver — there is no
|
||||
* bank API to call, so both pay() and refund() decide success immediately
|
||||
* on a staff member's say-so (they've already sent/received the wire
|
||||
* outside the system). Distinct from OfflinePaymentDriver in intent: this
|
||||
* exists so a payment taken through a DIFFERENT method (e.g.
|
||||
* cash-on-delivery) can still be REFUNDED via bank transfer — an admin
|
||||
* chooses this driver explicitly in the refund action, independent of
|
||||
* which driver the original payment went through (see
|
||||
* refund() is manual/attested, same trust model as OfflinePaymentDriver —
|
||||
* there is no bank API to call, so it decides success immediately on a
|
||||
* staff member's say-so (they've already sent the wire outside the
|
||||
* system). Distinct from OfflinePaymentDriver in intent: this exists so a
|
||||
* payment taken through a DIFFERENT method (e.g. cash-on-delivery) can
|
||||
* still be REFUNDED via bank transfer — an admin chooses this driver
|
||||
* explicitly in the refund action, independent of which driver the
|
||||
* original payment went through (see
|
||||
* Payment\Support\TransactionDriverAdapter::refundVia() and
|
||||
* Order\Filament\Extensions\OrderActionsExtension). pay() exists so
|
||||
* the same driver also covers receiving a payment by bank transfer, but
|
||||
* the admin UI for that (bank reference, notes, proof-of-transfer upload)
|
||||
* is deliberately not built yet — see the follow-up work tracked from this
|
||||
* session; pay() itself is complete and usable via the registry today.
|
||||
* Order\Filament\Extensions\OrderActionsExtension).
|
||||
*
|
||||
* pay() is the opposite trust direction from refund(): a bank transfer
|
||||
* payment requires the money to arrive BEFORE the order can be
|
||||
* considered paid (unlike cash-on-delivery, where payment happens on
|
||||
* delivery — see CashOnDeliveryPaymentDriver's own docblock for that
|
||||
* driver's mirror-image reasoning). So pay() returns Pending, dispatching
|
||||
* no event at all — no PaymentCaptured (nothing has been paid yet), and
|
||||
* deliberately NOT PaymentDeferred either (unlike COD, whose
|
||||
* MarkOrderPlacedOnDeferredPayment listener immediately advances the
|
||||
* order past 'awaiting_payment' since a COD order has nothing to await at
|
||||
* checkout). A bank transfer order genuinely DOES have something to
|
||||
* await: it stays at 'awaiting_payment' with Order::paid false until
|
||||
* staff confirm the wire arrived via OrderFulfillmentService::markPaid(),
|
||||
* which — unlike its COD path — also advances the order's status, since
|
||||
* nothing else ever will (see that method's own docblock).
|
||||
* CheckoutController::placeOrder() already treats a Pending result with
|
||||
* no continuation as a fully placed order (see its own docblock), so the
|
||||
* order is still created and visible to the shopper immediately; only its
|
||||
* payment/status is what's left outstanding.
|
||||
*
|
||||
* $reference is generated here for the same reason as OfflinePaymentDriver's
|
||||
* pay(): there is no gateway to hand one back. 'notes' in $context (not
|
||||
* $data — refund() has no $data parameter) is folded into
|
||||
* PaymentResult::$meta, which Order\Services\TransactionRecorder::record()
|
||||
* already writes straight into Transaction.meta with no extra plumbing.
|
||||
* pay(): there is no gateway to hand one back. refund()'s 'notes' (in
|
||||
* $context — it has no $data parameter) is folded into PaymentResult::$meta,
|
||||
* which Order\Services\TransactionRecorder::record() already writes straight
|
||||
* into Transaction.meta with no extra plumbing; pay() has no equivalent
|
||||
* write, since nothing ever records a Transaction from its own result (see
|
||||
* above) — any notes a shopper enters at checkout would need surfacing some
|
||||
* other way, e.g. when staff mark the order paid.
|
||||
*/
|
||||
class BankTransferPaymentDriver implements Configurable, SupportsPay, SupportsRefunds
|
||||
{
|
||||
@@ -46,16 +63,11 @@ class BankTransferPaymentDriver implements Configurable, SupportsPay, SupportsRe
|
||||
|
||||
public function pay(string $type, Price $amount, array $data = [], array $context = []): PaymentResult
|
||||
{
|
||||
$result = new PaymentResult(
|
||||
status: PaymentResultStatus::Succeeded,
|
||||
return new PaymentResult(
|
||||
status: PaymentResultStatus::Pending,
|
||||
reference: 'bank-transfer-'.Str::uuid(),
|
||||
amount: $amount,
|
||||
meta: array_filter(['notes' => $data['notes'] ?? null]),
|
||||
);
|
||||
|
||||
PaymentCaptured::dispatch($type, $result, $context);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function refund(string $reference, Price $amount, array $context = []): PaymentResult
|
||||
|
||||
Reference in New Issue
Block a user