Lunar\Core\Contracts\PaymentType contract and registering it with the Payments manager.
Overview
Payments in Lunar are driver-based. Each driver is responsible for authorizing a payment, capturing an intent, and issuing refunds, then recording the result as transactions against the order. Lunar ships anOfflinePayment driver for cash-in-hand and manual scenarios; anything else, including card payments, is added as a driver.
lunarphp/stripe is Lunar’s first-party reference implementation and is the best place to see a complete driver in production use. See Stripe for the add-on itself, and Payments for day-to-day configuration and usage.
The PaymentType contract
A driver implementsLunar\Core\Contracts\PaymentType:
Rather than implementing the interface directly, a driver typically extends
Lunar\Core\PaymentTypes\AbstractPayment, which implements cart(), order(), withData(), setConfig(), and allowPartialPayment(), leaving only authorize(), refund(), and capture() to be written.
AbstractPayment also defines getPaymentChecks(Transaction $transaction): Lunar\Core\DataObjects\PaymentChecks. This method is not part of the PaymentType contract itself, but every transaction resolves its driver and calls it through Transaction::paymentChecks(), so a driver that performs verification checks (AVS, postal code, CVC, and similar) should override it. The default implementation returns an empty PaymentChecks collection.
Registering a driver
A driver is registered with thePayments manager, usually from a service provider’s boot() method:
config/lunar/payments.php:
Whatever is set under a type’s
driver key is passed to setConfig() automatically when the driver is resolved, so any additional configuration a driver needs can be added alongside it.Verifying the amount and currency
Before an order is placed, a driver must confirm that the amount and currency it received from the provider match the cart or order total it is authorizing. Skipping this check allows a stale or tampered payment intent to place an order at the wrong price.authorize() should call this check as soon as the provider’s response is available, and return the failure immediately if it does not pass.
Scaling amounts
Lunar stores prices as integers scaled byCurrency::decimal_places, which a merchant can set independently of what a payment provider expects. Never assume two decimal places: convert through decimal_places explicitly when talking to a provider, and convert back the same way when reading an amount from it.
lunarphp/stripe’s Lunar\Stripe\Managers\StripeManager::toStripeAmount() and fromStripeAmount() are a complete worked example of this conversion, including handling zero-decimal and three-decimal provider currencies.
Recording transactions
Every authorization, capture, and refund a driver performs should be recorded as aLunar\Core\Models\Transaction against the order.
Authorizing
If a payment is not captured immediately, its transaction should use the typeintent. When it is later captured, create a second transaction related to the intent through parent_transaction_id:
capture instead.
Capturing
intent amount. Capturing less than the intent amount is treated by most providers as a partial refund, after which no further capture can take place against that intent.
Refunding
Only acapture transaction can be refunded. To refund a payment that has not yet been captured, capture a smaller amount instead.
Transaction exposes refund(int $amount, $notes = null) and capture(int $amount = 0) convenience methods that delegate to the transaction’s own driver, resolved through Payments::driver($transaction->driver).