beebmx/laravel-pay

Create payments with Laravel

0.7.0 2023-11-15 22:23 UTC

This package is auto-updated.

Last update: 2024-04-15 23:23:00 UTC


README

Latest Stable Version License

Payments for Laravel is inspired by the official Laravel Cashier. The approach is slightly different to Cashier and the main behavior is the payment transactions.

Installation

You can install the package via composer:

composer require beebmx/laravel-pay

You can publish and run the migrations with:

php artisan vendor:publish --provider="Beebmx\LaravelPay\PayServiceProvider" --tag="pay-migrations"
php artisan migrate

You can publish the config file with:

php artisan vendor:publish --provider="Beebmx\LaravelPay\PayServiceProvider" --tag="pay-config"

Configuration

Before use Payments for Laravel you need to add the Payable trait to your user model, the default location of this is in App\Models\Users:

use Beebmx\LaravelPay\Payable;

class User extends Authenticatable {

    use Payable;
}

Custom models

You can use your own models to fit your needs, just set in the App\Providers\AppServiceProvider and inform Payments for Laravel in the boot method:

use App\Models\Address;
use App\Models\Pay\User;
use App\Models\Transaction;
use App\Models\TransactionItem;
use Beebmx\LaravelPay\Pay;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Pay::useCustomerModel(User::class);
    Pay::useAddressModel(Address::class);
    Pay::useTransactionModel(Transaction::class);
    Pay::useTransactionItemModel(TransactionItem::class);
}

Just remember, if you change this for your own models, make sure to extend these models with the Payments for Laravel:

use Beebmx\LaravelPay\Address as PayAddress;

class Address extends PayAddress
{
    //
}

Drivers

Sandbox

This works as a playground, with full capabilities as a testing or production driver.

Stripe

To work with the stripe driver you need to set as default driver and configurate your testing or production keys:

<?php

return [
    'default' => 'stripe',
    
    'drivers' => [
        'stripe' => [
            'driver' => Beebmx\LaravelPay\Drivers\StripeDriver::class,
            'secret' => env('STRIPE_SECRET_KEY', null),
            'public' => env('STRIPE_PUBLIC_KEY', null),
            'webhooks' => [
                'handler' => Beebmx\LaravelPay\Http\Webhooks\StripeHandler::class,
                'events' => [
                    'customer.deleted',
                    'customer.updated',
                    'invoice.payment_action_required',
                    'payment_intent.canceled',
                    'payment_intent.payment_failed',
                    'payment_intent.requires_action',
                    'payment_intent.succeeded',
                ],
            ],
        ],
    ]
];

Conekta

To work with the conekta driver you need to set as default driver and configurate your testing or production keys:

<?php

return [
    'default' => 'conekta',
    
    'drivers' => [
        'conekta' => [
            'driver' => Beebmx\LaravelPay\Drivers\ConektaDriver::class,
            'secret' => env('CONEKTA_SECRET_KEY', null),
            'public' => env('CONEKTA_PUBLIC_KEY', null),
            'webhooks' => [
                'handler' => Beebmx\LaravelPay\Http\Webhooks\ConektaHandler::class,
            ],
        ],
    ]
];

Customer

Create customer

A user can be a customer (this is not a requirement) and will create a customer in the correspond driver. To create a customer just use the method createCustomerWithDriver:

$customer = $user->createCustomerWithDriver();

Basic information

When a user becomes to customer, the columns used to create it, comes from the model, but you can customize this with:

public function driverName()
{
    return "{$this->your_own_colunm_name} {$this->your_own_colunm_lastname}";
}

public function driverEmail()
{
    return $this->your_own_colunm_email;
}

public function driverPhone()
{
    return $this->your_own_colunm_phone;
}

Creating a user can be useful for recurring payments or to avoid asking for payment methods all the time.

Payment Methods

To add a payment method, first you need to have a customer on the driver platform. After you have your customer just add the right token or payment_method:

$paymentMethod = $user->addPaymentMethod('pm_card_visa');

Shipping

If your payment have a shipping, just add the proper address before perform the payment:

$address = $user->addresses->first();
$user->shipping($address);

Remember that this package have an address model build in out of the box.

Payments

To perform a payment is as easy as:

$user->charge([
    'name' => 'Product',
    'price' => 500,
]);

If you have more than one product or service:

$user->charge([[
    'name' => 'Product 01',
    'price' => 100
], [
    'name' => 'Product 02',
    'price' => 200
]]);

If you have a discount to be applied you can set it:

$user
    ->discount(200)
    ->charge([[
        'name' => 'Product 01',
        'price' => 100,
        'quantity' => 2
    ], [
        'name' => 'Product 02',
        'price' => 200,
        'quantity' => 1,
    ]]);

Or you can set it with more information like:

$user
    ->discount([
        'amount' => 200, 
        'code' => 'coupon-code',
    ])
    ->charge([[
        'name' => 'Product 01',
        'price' => 100,
        'quantity' => 2
    ], [
        'name' => 'Product 02',
        'price' => 200,
        'quantity' => 1,
    ]]);

It's important that the user has at least one payment method.

If you have a shipping address:

$address = $user->addresses->first();

$user
    ->shipping($address)
    ->charge([
        'name' => 'Testing product',
        'price' => 500,
    ]);

If you don't need to create a payment method you can create a charge with a one time payment token:

$user
    ->token('tok_visa4242')
    ->charge([
        'name' => 'Testing product',
        'price' => 500,
    ]);

If you are using the token option, the createCustomerWithDriver is optional and not required.

If you need the payment transaction, you can retrive it with the getTransaction() method:

$payment = $user->charge([
    'name' => 'Product',
    'price' => 500,
]);

$payment->getTransaction()

Webhooks

By default Payments for Laravel provides a handy management of webhooks.

First in your App\Http\Middleware\VerifyCsrfToken file, you need to allow the path of the WebhookController:

protected $except = [
    'pay/webhooks',
];

The default path is pay/webhooks but you can change it in the config file, updating the pay.path and pay.webhooks.

You can create manually your endpoint in your driver provider site or with the command:

php artisan pay:webhook

The driver sandbox doesn't allow the creation of webhooks.

Handlers

The WebhookController always react to the events emitted from the driver provider and passes to the correct handler. By default, every driver has their own handler defined in the config/pay.php file:

'drivers' => [
    'stripe' => [
        'webhooks' => [
            'handler' => Beebmx\LaravelPay\Http\Webhooks\StripeHandler::class,
            'events' => [
                'customer.deleted',
                'customer.updated',
                'invoice.payment_action_required',
                'payment_intent.canceled',
                'payment_intent.payment_failed',
                'payment_intent.requires_action',
                'payment_intent.succeeded',
            ],
        ],
    ],

    'conekta' => [
        'webhooks' => [
            'handler' => Beebmx\LaravelPay\Http\Webhooks\ConektaHandler::class,
        ],
    ],
],

You can change the handler with your own implementation, just make your to extends from the right handler.

<?php

use Beebmx\LaravelPay\Http\Webhooks\StripeHandler as PayStripeHandler;

class StripeHandler extends PayStripeHandler
{
    // ...
}

The methods names should correspond to the convention, first all methods should be prefixed with handle and the "camel case" name of the webhook you wish to handle. For example, if you wish to handle the payment_intent.succeeded webhook, you should add a handlePaymentIntentSucceeded method to the controller:

<?php

use Beebmx\LaravelPay\Http\Webhooks\StripeHandler as PayStripeHandler;

class StripeHandler extends PayStripeHandler
{
    public function handlePaymentIntentSucceeded($payload)
    {
        // ...
    }
}

On each handled method an array with $payload correspondence is always received.

Webhook events

On each webhook call, the following events could be dispatched:

  • Beebmx\LaravelPay\Events\WebhookReceived
  • Beebmx\LaravelPay\Events\WebhookHandled

You can create your own implementation of the payload:

<?php

namespace App\Listeners;

use Beebmx\LaravelPay\Events\WebhookReceived;

class StripeEventListener
{
    public function handle(WebhookReceived $event)
    {
        // $event->payload['type']
    }
}

To listen this implementation is required to register in your App\Providers\EventServiceProvider:

<?php

namespace App\Providers;

use App\Listeners\StripeEventListener;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Beebmx\LaravelPay\Events\WebhookReceived;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        WebhookReceived::class => [
            StripeEventListener::class,
        ],
    ];
}

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Credits

License

The MIT License (MIT). Please see License File for more information.