66 lines
3.1 KiB
PHP
66 lines
3.1 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace Modules\Core\Payment\Contracts;
|
||
|
|
|
||
|
|
use Lunar\Exceptions\FingerprintMismatchException;
|
||
|
|
use Lunar\Exceptions\Carts\CartException;
|
||
|
|
use Lunar\Models\Cart;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A boboko-owned payment driver — wraps a payment gateway's own confirmation
|
||
|
|
* mechanics (Stripe's synchronous authorize() call, a redirect-based
|
||
|
|
* provider's async callback/webhook, anything else) behind one uniform
|
||
|
|
* moment: "payment is confirmed."
|
||
|
|
*
|
||
|
|
* confirm() is the only thing a driver is required to do: once it has,
|
||
|
|
* by whatever mechanism is native to that gateway, independently decided
|
||
|
|
* the payment succeeded, it dispatches Modules\Core\Checkout\Events\
|
||
|
|
* PaymentConfirmed — no driver ever calls CheckoutService::placeOrder() or
|
||
|
|
* Lunar\Models\Cart::createOrder() directly. CheckoutService itself listens
|
||
|
|
* for PaymentConfirmed and places the order from there; a driver that needs
|
||
|
|
* to do something to the placed Order afterward (status mapping, syncing
|
||
|
|
* gateway state) listens for the resulting OrderPlaced itself, matching it
|
||
|
|
* via $order->meta['payment_method'] — see PaymentConfirmed's docblock for
|
||
|
|
* why. This split is what makes an async/webhook-driven gateway (payment
|
||
|
|
* confirmed in a request that has no synchronous caller waiting for an
|
||
|
|
* Order at all) and a synchronous one (Stripe) work through the exact same
|
||
|
|
* contract. See docs/checkout.md / docs/payments.md.
|
||
|
|
*/
|
||
|
|
interface PaymentDriver
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* Whether this driver can actually be used right now — e.g. Stripe
|
||
|
|
* checking its own API key is present, an offline-style driver always
|
||
|
|
* returning true since it has no external dependency. Independent of
|
||
|
|
* Modules\Core\Payment\Models\PaymentMethod::enabled (the admin
|
||
|
|
* on/off toggle) — CheckoutService::getPaymentMethods() combines both:
|
||
|
|
* a type is only offered to the storefront if it's administratively
|
||
|
|
* enabled AND its driver reports itself configured.
|
||
|
|
*/
|
||
|
|
public function isConfigured(): bool;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* $type is the payment type key being confirmed (e.g. 'cash-in-hand',
|
||
|
|
* 'cash-on-delivery', 'stripe') — passed through even though most
|
||
|
|
* drivers only ever serve one type, because a driver shared across
|
||
|
|
* several types (e.g. one "no real confirmation" offline driver behind
|
||
|
|
* both cash-in-hand and cash-on-delivery) needs it to look up that
|
||
|
|
* type's own config (e.g. its 'authorized' status) rather than another
|
||
|
|
* type's.
|
||
|
|
*
|
||
|
|
* $data carries whatever the gateway needs to confirm this specific
|
||
|
|
* payment (Stripe: ['payment_intent' => $id], a redirect-based
|
||
|
|
* provider: its callback payload) — passed explicitly by the caller
|
||
|
|
* (a controller, a webhook job) rather than a driver reaching into the
|
||
|
|
* global request(), so confirm() works the same whether it's called
|
||
|
|
* from a synchronous HTTP request or an async webhook/job with no
|
||
|
|
* active request at all.
|
||
|
|
*
|
||
|
|
* @param array<string, mixed> $data
|
||
|
|
*
|
||
|
|
* @throws FingerprintMismatchException
|
||
|
|
* @throws CartException
|
||
|
|
*/
|
||
|
|
public function confirm(Cart $cart, string $type, string $fingerprint, array $data): void;
|
||
|
|
}
|