Feat: Updates to Payments, Checkout Services, Payment Events
This commit is contained in:
@@ -2,39 +2,40 @@
|
||||
|
||||
namespace Modules\Core\Payment\Drivers;
|
||||
|
||||
use Lunar\Exceptions\FingerprintMismatchException;
|
||||
use Lunar\Exceptions\Carts\CartException;
|
||||
use Lunar\Exceptions\DisallowMultipleCartOrdersException;
|
||||
use Lunar\Models\Cart;
|
||||
use Lunar\Models\Order;
|
||||
use Lunar\Stripe\Actions\UpdateOrderFromIntent;
|
||||
use Lunar\Stripe\Facades\Stripe;
|
||||
use Lunar\Stripe\Models\StripePaymentIntent;
|
||||
use Modules\Core\Checkout\Contracts\PaymentDriver;
|
||||
use Modules\Core\Checkout\Services\CheckoutService;
|
||||
use Modules\Core\Checkout\Events\OrderPlaced;
|
||||
use Modules\Core\Checkout\Events\PaymentConfirmed;
|
||||
use Modules\Core\Payment\Contracts\PaymentDriver;
|
||||
use Modules\Core\Payment\Exceptions\PaymentNotConfirmedException;
|
||||
use Stripe\PaymentIntent;
|
||||
|
||||
/**
|
||||
* Wraps Lunar\Stripe\StripePaymentType::authorize() to satisfy
|
||||
* Modules\Core\Checkout\Contracts\PaymentDriver — calls
|
||||
* CheckoutService::placeOrder($fingerprint) at the moment Stripe confirms
|
||||
* payment, instead of the vendor's own Cart::createOrder() call.
|
||||
* Modules\Core\Payment\Contracts\PaymentDriver — dispatches
|
||||
* PaymentConfirmed at the moment Stripe confirms payment, instead of the
|
||||
* vendor's own Cart::createOrder() call.
|
||||
*
|
||||
* This is a fork, not a decoration: StripePaymentType::authorize() is
|
||||
* `final` and calls Cart::createOrder() directly with no seam to redirect
|
||||
* that one call — so this class reimplements authorize()'s logic (intent
|
||||
* retrieval, capture-on-policy, status mapping via UpdateOrderFromIntent)
|
||||
* rather than wrapping the vendor method. Kept deliberately close to the
|
||||
* original so a lunarphp/stripe upgrade is easy to diff against. See
|
||||
* docs/payments.md.
|
||||
* retrieval, capture-on-policy) rather than wrapping the vendor method.
|
||||
* Kept deliberately close to the original so a lunarphp/stripe upgrade is
|
||||
* easy to diff against. See docs/payments.md.
|
||||
*
|
||||
* The status-mapping step (UpdateOrderFromIntent) this driver used to do
|
||||
* inline right after placeOrder() returned now happens in onOrderPlaced()
|
||||
* below instead — see PaymentDriver's docblock for why a driver can no
|
||||
* longer rely on placeOrder()'s return value. Since that step needs the
|
||||
* live Stripe PaymentIntent, not just the Order, onOrderPlaced() re-fetches
|
||||
* it from Stripe via the StripePaymentIntent row this method already wrote
|
||||
* (keyed by the order's cart_id) rather than carrying the PaymentIntent
|
||||
* object across the event boundary itself.
|
||||
*/
|
||||
class StripePaymentDriver implements PaymentDriver
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CheckoutService $checkout,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Same key lunarphp/stripe's own StripeManager reads its API key from
|
||||
* (Stripe::setApiKey(config('services.stripe.key')) in
|
||||
@@ -47,13 +48,11 @@ class StripePaymentDriver implements PaymentDriver
|
||||
|
||||
/**
|
||||
* @throws PaymentNotConfirmedException if Stripe hasn't confirmed the
|
||||
* payment intent (wrong intent id, already processed, order already
|
||||
* placed, or the gateway call itself fails) — nothing here should be
|
||||
* treated as "place the order anyway."
|
||||
* @throws FingerprintMismatchException
|
||||
* @throws CartException
|
||||
* payment intent (wrong intent id, already processed, or the gateway
|
||||
* call itself fails) — nothing here should be treated as "confirm
|
||||
* anyway."
|
||||
*/
|
||||
public function confirm(Cart $cart, string $type, string $fingerprint, array $data): Order
|
||||
public function confirm(Cart $cart, string $type, string $fingerprint, array $data): void
|
||||
{
|
||||
$paymentIntentId = $data['payment_intent'];
|
||||
|
||||
@@ -93,19 +92,34 @@ class StripePaymentDriver implements PaymentDriver
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$order = $this->checkout->placeOrder($fingerprint);
|
||||
} catch (DisallowMultipleCartOrdersException|CartException $e) {
|
||||
throw new PaymentNotConfirmedException($e->getMessage(), previous: $e);
|
||||
$paymentIntentModel->status = $paymentIntent->status;
|
||||
$paymentIntentModel->save();
|
||||
|
||||
PaymentConfirmed::dispatch($cart, $type, $fingerprint, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registered in PaymentServiceProvider. Matches via the order's
|
||||
* cart_id against the StripePaymentIntent row confirm() wrote, so a
|
||||
* non-Stripe OrderPlaced (offline types fire the same event) is
|
||||
* ignored rather than acted on.
|
||||
*/
|
||||
public function onOrderPlaced(OrderPlaced $event): void
|
||||
{
|
||||
$order = $event->order;
|
||||
|
||||
$paymentIntentModel = StripePaymentIntent::where('cart_id', $order->cart_id)->first();
|
||||
|
||||
if (! $paymentIntentModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paymentIntentModel->order_id = $order->id;
|
||||
$paymentIntentModel->status = $paymentIntent->status;
|
||||
$paymentIntentModel->processed_at = now();
|
||||
$paymentIntentModel->save();
|
||||
|
||||
UpdateOrderFromIntent::execute($order, $paymentIntent);
|
||||
$paymentIntent = Stripe::getClient()->paymentIntents->retrieve($paymentIntentModel->intent_id);
|
||||
|
||||
return $order->refresh();
|
||||
UpdateOrderFromIntent::execute($order, $paymentIntent);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user