Feat: Updates to Payments, Checkout Services, Payment Events
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Core\Checkout\Events;
|
||||
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Lunar\Models\Cart;
|
||||
|
||||
/**
|
||||
* Dispatched by a PaymentDriver once it has independently decided (by
|
||||
* whatever mechanism is native to its gateway) that payment succeeded —
|
||||
* the event-driven counterpart to what used to be a direct
|
||||
* CheckoutService::placeOrder() call from inside confirm(). Listened to by
|
||||
* CheckoutService itself, which places the order and dispatches
|
||||
* OrderPlaced.
|
||||
*
|
||||
* $type/$data are carried through for the same reason PaymentDriver::
|
||||
* confirm() takes them — a driver-specific post-placement step (e.g.
|
||||
* OfflinePaymentDriver's status mapping, StripePaymentDriver's
|
||||
* UpdateOrderFromIntent) still needs them, but can no longer receive the
|
||||
* placed Order as a return value. Each driver instead listens for
|
||||
* OrderPlaced and checks $order->meta['payment_method'] against its own
|
||||
* type(s) to recognize which OrderPlaced is its own — carrying $fingerprint
|
||||
* here too lets a driver correlate its own OrderPlaced listener call back
|
||||
* to the specific confirmation that triggered it, if it needs to.
|
||||
*/
|
||||
class PaymentConfirmed
|
||||
{
|
||||
use Dispatchable;
|
||||
|
||||
public function __construct(
|
||||
public readonly Cart $cart,
|
||||
public readonly string $type,
|
||||
public readonly string $fingerprint,
|
||||
public readonly array $data = [],
|
||||
) {}
|
||||
}
|
||||
@@ -12,15 +12,16 @@ use Lunar\Facades\ShippingManifest;
|
||||
use Lunar\Models\Cart;
|
||||
use Lunar\Models\Order;
|
||||
use Modules\Core\Cart\Services\CartService;
|
||||
use Modules\Core\Checkout\Contracts\PaymentDriver;
|
||||
use Modules\Core\Checkout\Events\BillingAddressSet;
|
||||
use Modules\Core\Checkout\Events\OrderPlaced;
|
||||
use Modules\Core\Checkout\Events\PaymentConfirmed;
|
||||
use Modules\Core\Checkout\Events\PaymentMethodSelected;
|
||||
use Modules\Core\Checkout\Events\ShippingAddressSet;
|
||||
use Modules\Core\Checkout\Events\ShippingOptionSelected;
|
||||
use Modules\Core\Checkout\Exceptions\InvalidShippingOptionException;
|
||||
use Modules\Core\Checkout\Exceptions\UnknownPaymentTypeException;
|
||||
use Modules\Core\Payment\Models\PaymentMethod;
|
||||
use Modules\Core\Payment\Services\PaymentDriverResolver;
|
||||
|
||||
/**
|
||||
* Storefront-facing checkout operations, mirroring
|
||||
@@ -41,6 +42,7 @@ class CheckoutService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CartService $cart,
|
||||
private readonly PaymentDriverResolver $paymentDrivers,
|
||||
) {}
|
||||
|
||||
public function setShippingAddress(array|Addressable $address): Cart
|
||||
@@ -149,7 +151,7 @@ class CheckoutService
|
||||
{
|
||||
return PaymentMethod::where('enabled', true)
|
||||
->pluck('type')
|
||||
->filter(fn (string $type) => $this->resolvePaymentDriver($type)?->isConfigured() ?? false)
|
||||
->filter(fn (string $type) => $this->paymentDrivers->resolve($type)?->isConfigured() ?? false)
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
@@ -198,11 +200,18 @@ class CheckoutService
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves $type's registered PaymentDriver and calls confirm() —
|
||||
* the driver decides whether/when the order actually gets placed (see
|
||||
* Modules\Core\Checkout\Contracts\PaymentDriver's docblock). $data
|
||||
* carries whatever that driver needs (Stripe's payment_intent id, a
|
||||
* future redirect-based provider's callback payload).
|
||||
* Resolves $type's registered PaymentDriver and calls confirm() — the
|
||||
* driver independently decides whether payment succeeded and, if so,
|
||||
* dispatches PaymentConfirmed (see PaymentDriver's docblock) rather
|
||||
* than placing the order itself or returning it here. This method is
|
||||
* fire-and-forget as far as the Order is concerned: a caller that
|
||||
* needs it back listens for OrderPlaced, the same way a driver's own
|
||||
* post-placement step does — see PaymentConfirmed's docblock for why a
|
||||
* direct return value doesn't fit every gateway (async/webhook-driven
|
||||
* confirmations have no synchronous caller waiting for one at all).
|
||||
*
|
||||
* $data carries whatever that driver needs (Stripe's payment_intent
|
||||
* id, a future redirect-based provider's callback payload).
|
||||
*
|
||||
* The fingerprint passed to the driver is the one captured by
|
||||
* selectPaymentMethod(), not supplied by the caller — see that
|
||||
@@ -220,7 +229,7 @@ class CheckoutService
|
||||
* @throws \Lunar\Exceptions\FingerprintMismatchException
|
||||
* @throws \Lunar\Exceptions\Carts\CartException
|
||||
*/
|
||||
public function confirmPayment(string $type, array $data = []): Order
|
||||
public function confirmPayment(string $type, array $data = []): void
|
||||
{
|
||||
if (! in_array($type, $this->getPaymentMethods(), true)) {
|
||||
throw new UnknownPaymentTypeException($type);
|
||||
@@ -229,20 +238,6 @@ class CheckoutService
|
||||
$cart = $this->cart->currentOrCreate();
|
||||
$fingerprint = $cart->meta['checkout_fingerprint'] ?? '';
|
||||
|
||||
return $this->resolvePaymentDriver($type)->confirm($cart, $type, $fingerprint, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves $type's registered PaymentDriver, or null if $type has no
|
||||
* 'payment_driver' registered in config('lunar.payments.types.<type>')
|
||||
* at all — deliberately non-throwing so getPaymentMethods() can filter
|
||||
* unresolvable types silently rather than treating "not registered"
|
||||
* as an error condition when just checking availability.
|
||||
*/
|
||||
private function resolvePaymentDriver(string $type): ?PaymentDriver
|
||||
{
|
||||
$driverClass = config("lunar.payments.types.{$type}.payment_driver");
|
||||
|
||||
return $driverClass ? app($driverClass) : null;
|
||||
$this->paymentDrivers->resolve($type)->confirm($cart, $type, $fingerprint, $data);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user