> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lunarphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Extending Payments

Custom payment drivers can be added to support any payment provider.

## Overview

Lunar provides an easy way for you to add your own payment drivers, by default, there is a basic `OfflinePayment` driver
that ships with Lunar, additional providers should be added to your Storefront via addons.

Below is a list of available payment drivers.

## Available drivers

### First party

* [Stripe](https://github.com/lunarphp/stripe)

### Community

> Made a custom driver that should be listed here? Get in touch on the Lunar discord channel to get it added.

## Building your own

A payment driver should take into account 2 fundamentals:

* Capturing a payment (whether straight away, or at a later date)
* Refunding an existing payment

### Registering your driver

```php theme={null}
use Lunar\Facades\Payments;

Payments::extend('custom', function ($app) {
    return $app->make(CustomPayment::class);
});
```

### The payment driver class

The following is the complete class, which is then broken down below.

```php theme={null}
<?php

namespace App\PaymentTypes;

use Lunar\Base\DataTransferObjects\PaymentAuthorize;
use Lunar\Base\DataTransferObjects\PaymentCapture;
use Lunar\Base\DataTransferObjects\PaymentRefund;
use Lunar\Events\PaymentAttemptEvent;
use Lunar\Models\Transaction;
use Lunar\PaymentTypes\AbstractPayment;

class CustomPayment extends AbstractPayment
{
    /**
     * {@inheritDoc}
     */
    public function authorize(): ?PaymentAuthorize
    {
        if (!$this->order) {
            if (!$this->order = $this->cart->order) {
                $this->order = $this->cart->createOrder();
            }
        }

        // ...
        
        $response = new PaymentAuthorize(
            success: true,
            message: 'The payment was successful',
            orderId: $this->order->id,
            paymentType: 'custom-type'
        );
        
        PaymentAttemptEvent::dispatch($response);

        return $response;
    }

    /**
     * {@inheritDoc}
     */
    public function refund(Transaction $transaction, int $amount = 0, $notes = null): PaymentRefund
    {
        // ...
        return new PaymentRefund(true);
    }

    /**
     * {@inheritDoc}
     */
    public function capture(Transaction $transaction, $amount = 0): PaymentCapture
    {
        // ...
        return new PaymentCapture(true);
    }
}
```

This is the most basic implementation of a driver, extending the `AbstractPayment` class provided by Lunar. This class
contains useful helpers that can be used in custom drivers.

[See available methods](#abstract-class-methods)

#### Releasing payments

```php theme={null}
public function authorize();
```

This is where you'd check the payment details which have been passed in, create any transactions for the order and
return the response.

If payment is not being taken straight away, any transactions should be set to the type of `intent`. When the payment is
later captured, it is recommended to create another transaction that is related to the intent via
the `parent_transaction_id`.

#### Capturing payments

```php theme={null}
public function capture(Transaction $transaction, $amount = 0): PaymentCapture
```

When you have a transaction that has a type of `intent` the Staff member who is logged into the hub can then decide to
capture it so the card used gets charged the amount that has been authorised.

You can pass an optional amount, but be cautious as you generally cannot capture an amount that exceeds the original
amount on the `intent` transaction. If you capture an amount less, services like Stripe will treat that as a partial
refund and no further captures can take place on that order.

Here you should create an additional transaction against the order to show how much has been captured.

#### Refunding payments

```php theme={null}
public function refund(Transaction $transaction, int $amount = 0, $notes = null): PaymentRefund
```

When refunding a transaction, you can only do so to one that's been captured. If you need to refund an order that hasn't
been captured you should instead capture an amount less to what's been authorised.

You should only refund transactions with the type `capture`.

<a name="abstract-class-methods" />

## The AbstractPayment class

### Available methods

* [`cart`](#cart)
* [`order`](#order)
* [`withData`](#withData)
* [`setConfig`](#setconfig)

#### `cart`

```php theme={null}
public function cart(Cart $cart): self
```

Sets the `$cart` property on the payment driver. When using the `authorize` method, it is recommended to expect a `$cart`
instance and check for the existence of an order.

#### `order`

```php theme={null}
public function order(Order $order): self
```

Sets the `$order` property on the payment driver.

#### `withData`

```php theme={null}
public function withData(array $data): self
```

This method allows you to add any additional data to the payment driver, this can be anything that the payment driver
needs to function, for example.

```php theme={null}
Payments::driver('stripe')->withData([
    'payment_intent' => $paymentIntentId
])->authorize();
```

#### `setConfig`

```php theme={null}
public function setConfig(array $config): self
```

Here you can set up any additional config for this payment driver. By default, this will be called when you register
your payment driver and will take any values which are set in `config/lunar/payments.php` for that type.

## Creating transactions

Depending on how your driver works, you're likely going to need to create some transactions depending on different
scenarios.

### Database Schema

```
Lunar\Models\Transaction
```

| Field                   | Description                                     | Example                                                                                     |
| :---------------------- | :---------------------------------------------- | :------------------------------------------------------------------------------------------ |
| id                      |                                                 |                                                                                             |
| parent\_transaction\_id | The ID of the related transaction, nullable     |                                                                                             |
| order\_id               | The ID of the order this transaction relates to |                                                                                             |
| success                 | Whether or not the transaction was successful   | 1                                                                                           |
| type                    | Whether `intent`,`capture` or `refund`          | `intent`                                                                                    |
| driver                  | The driver used i.e. `stripe`                   | `stripe`                                                                                    |
| amount                  | The amount for the transaction in cents         | `10000`                                                                                     |
| reference               | The reference for the driver to use             | `STRIPE_123456`                                                                             |
| status                  | Usually populated from the payment provider     | `success`                                                                                   |
| notes                   | Any additional notes for the transaction        |                                                                                             |
| card\_type              | The card type                                   | `visa`                                                                                      |
| last\_four              | The last four digits of the card used           | `1234`                                                                                      |
| captured\_at            | The DateTime the transaction was captured       |                                                                                             |
| meta                    | Any additional meta info for the transaction    | `{"cvc_check": "pass", "address_line1_check": "pass", "address_postal_code_check": "pass"}` |
| created\_at             |                                                 |                                                                                             |
| updated\_at             |                                                 |                                                                                             |

### Best Practices

#### Releasing

When releasing a payment, if you're not charging the card straight away, you should create a transaction with
type `intent`. This tells Lunar you intend to charge the card at a later date.

```php theme={null}
Transaction::create([
    //...
    'type' => 'intent',
]);
```

If you are charging the card straight away, set the type to `capture`.

```php theme={null}
Transaction::create([
    //...
    'type' => 'capture',
]);
```

#### Capturing

<Tip>
  If the card is already being charged, this step can be skipped as payment has already been received.
</Tip>

When capturing a transaction, you should create an additional transaction with the amount that's been captured. Even if
this is the same amount as the `intent` transaction.

```php theme={null}
$intent = Transaction::whereType('intent')->first();

Transaction::create([
    //...
    'parent_transaction_id' => $intent->id,
    'type' => 'capture',
    'amount' => 2000,
]);
```

#### Refunding

```php theme={null}
$capture = Transaction::whereType('capture')->first();

Transaction::create([
    //...
    'parent_transaction_id' => $capture->id,
    'type' => 'refund',
]);
```
