Cashier (Paddle)

Introduzione

[!WARNING]
Questa documentazione riguarda l’integrazione di Cashier Paddle 2.x con Paddle Billing. Se stai ancora utilizzando Paddle Classic, dovresti usare Cashier Paddle 1.x.

Laravel Cashier Paddle offre un’interfaccia espressiva e fluente ai servizi di fatturazione in abbonamento di Paddle. Gestisce quasi tutto il codice boilerplate relativo alla fatturazione in abbonamento che temi. Oltre alla gestione base degli abbonamenti, Cashier può gestire: cambio di abbonamento, "quantità" di abbonamento, pausa degli abbonamenti, periodi di grazia per la cancellazione e altro ancora.

Prima di approfondire Cashier Paddle, ti consigliamo di rivedere anche le guide concettuali e la documentazione API di Paddle.

Aggiornare Cashier

Quando aggiorni a una nuova versione di Cashier, è importante che tu riveda attentamente la guida all’aggiornamento.

Installazione

Per prima cosa, installa il pacchetto Cashier per Paddle usando il gestore di pacchetti Composer:

composer require laravel/cashier-paddle

Successivamente, pubblica i file di migrazione di Cashier utilizzando il comando Artisan vendor:publish:

php artisan vendor:publish --tag="cashier-migrations"

Poi, esegui le migrazioni del database della tua applicazione. Le migrazioni di Cashier creeranno una nuova tabella customers. Inoltre, verranno create le tabelle subscriptions e subscription_items per memorizzare tutte le sottoscrizioni dei tuoi clienti. Infine, verrà creata una nuova tabella transactions per memorizzare tutte le transazioni Paddle associate ai tuoi clienti:

php artisan migrate

Per garantire che Cashier gestisca correttamente tutti gli eventi di Paddle, ricorda di configurare la gestione dei webhook di Cashier.

Paddle Sandbox

Durante lo sviluppo locale e di staging, dovresti registrare un account Paddle Sandbox. Questo account ti offre un ambiente sandbox per testare e sviluppare le tue applicazioni senza effettuare pagamenti reali. Puoi usare i numeri di carte di test di Paddle per simulare diverse situazioni di pagamento.

Quando usi l’ambiente Paddle Sandbox, devi impostare la variabile d’ambiente PADDLE_SANDBOX su true nel file .env della tua applicazione:

PADDLE_SANDBOX=true

Dopo aver finito di sviluppare la tua applicazione, puoi richiedere un account fornitore Paddle. Prima che la tua applicazione sia messa in produzione, Paddle dovrà approvare il dominio della tua applicazione.

Configurazione

Modello Billable

Prima di usare Cashier, devi aggiungere il trait Billable alla definizione del tuo modello utente. Questo trait fornisce vari metodi per eseguire operazioni di fatturazione comuni, come creare abbonamenti e aggiornare le informazioni del metodo di pagamento:

use Laravel\Paddle\Billable;

class User extends Authenticatable
{
    use Billable;
}

Se hai entità fatturabili che non sono utenti, puoi anche aggiungere il trait a quelle classi:

use Illuminate\Database\Eloquent\Model;
use Laravel\Paddle\Billable;

class Team extends Model
{
    use Billable;
}

Chiavi API

Successivamente, devi configurare le tue chiavi Paddle nel file .env della tua applicazione. Puoi recuperare le chiavi API Paddle dal pannello di controllo di Paddle:

PADDLE_CLIENT_SIDE_TOKEN=your-paddle-client-side-token
PADDLE_API_KEY=your-paddle-api-key
PADDLE_RETAIN_KEY=your-paddle-retain-key
PADDLE_WEBHOOK_SECRET="your-paddle-webhook-secret"
PADDLE_SANDBOX=true

La variabile d’ambiente PADDLE_SANDBOX deve essere impostata su true quando usi l’ambiente Sandbox di Paddle. La variabile PADDLE_SANDBOX deve essere impostata su false se stai distribuendo la tua applicazione in produzione e stai usando l’ambiente live vendor di Paddle.

La PADDLE_RETAIN_KEY è opzionale e deve essere impostata solo se usi Paddle con Retain.

Paddle JS

Paddle si basa sulla propria libreria JavaScript per avviare il widget di checkout di Paddle. Puoi caricare la libreria JavaScript inserendo la direttiva Blade @paddleJS subito prima del tag di chiusura </head> del layout della tua applicazione:

<head>
    ...

    @paddleJS
</head>

Configurazione della Valuta

Puoi specificare una locale da utilizzare quando formatti i valori monetari per la visualizzazione sulle fatture. Internamente, Cashier utilizza la classe NumberFormatter di PHP per impostare la locale della valuta:

CASHIER_CURRENCY_LOCALE=nl_BE

Per utilizzare locali diversi da en, assicurati che l’estensione PHP ext-intl sia installata e configurata sul tuo server.

Sovrascrivere i Modelli Predefiniti

Puoi estendere i modelli utilizzati internamente da Cashier definendo il tuo modello e estendendo il modello corrispondente di Cashier:

use Laravel\Paddle\Subscription as CashierSubscription;

class Subscription extends CashierSubscription
{
    // ...
}

Dopo aver definito il tuo modello, puoi indicare a Cashier di usare il tuo modello personalizzato tramite la classe Laravel\Paddle\Cashier. Di solito, dovresti informare Cashier dei tuoi modelli personalizzati nel metodo boot della classe App\Providers\AppServiceProvider della tua applicazione:

use App\Models\Cashier\Subscription;
use App\Models\Cashier\Transaction;

/**
 * Inizializza qualsiasi servizio dell'applicazione.
 */
public function boot(): void
{
    Cashier::useSubscriptionModel(Subscription::class);
    Cashier::useTransactionModel(Transaction::class);
}

Avvio rapido

Vendita di Prodotti

Prima di utilizzare Paddle Checkout, dovresti definire i Prodotti con prezzi fissi nel tuo dashboard di Paddle. Inoltre, dovresti configurare la gestione dei webhook di Paddle.

Offrire fatturazione per prodotti e abbonamenti attraverso la tua applicazione può essere intimidatorio. Tuttavia, grazie a Cashier e a Checkout Overlay di Paddle, puoi facilmente costruire integrazioni di pagamento moderne e robuste.

Per addebitare i clienti per prodotti a pagamento una tantum, utilizzeremo Cashier per addebitare i clienti con il Checkout Overlay di Paddle, dove forniranno i loro dati di pagamento e confermeranno l’acquisto. Una volta effettuato il pagamento tramite il Checkout Overlay, il cliente verrà reindirizzato a un URL di successo a tua scelta all’interno della tua applicazione:

use Illuminate\Http\Request;

Route::get('/buy', function (Request $request) {
    $checkout = $request->user()->checkout('pri_deluxe_album')
        ->returnTo(route('dashboard'));

    return view('buy', ['checkout' => $checkout]);
})->name('checkout');

Come puoi vedere nell’esempio sopra, utilizzeremo il metodo checkout fornito da Cashier per creare un oggetto checkout e presentare al cliente il Paddle Checkout Overlay per un determinato "identificatore di prezzo". Quando usi Paddle, i "prezzi" si riferiscono a prezzi definiti per prodotti specifici.

Se necessario, il metodo checkout creerà automaticamente un cliente in Paddle e collegherà quel record cliente di Paddle all’utente corrispondente nel database della tua applicazione. Dopo aver completato la sessione di checkout, il cliente verrà reindirizzato a una pagina di successo dedicata dove potrai mostrare un messaggio informativo al cliente.

Nella vista buy, includeremo un pulsante per mostrare il Checkout Overlay. Il componente Blade paddle-button è incluso con Cashier Paddle; tuttavia, puoi anche renderizzare manualmente un checkout overlay:

<x-paddle-button :checkout="$checkout" class="px-8 py-4">
    Buy Product
</x-paddle-button>

Fornire Metadati al Checkout di Paddle

Quando si vendono prodotti, è comune tenere traccia degli ordini completati e dei prodotti acquistati tramite i modelli Cart e Order definiti dalla propria applicazione. Quando si reindirizza i clienti al Checkout Overlay di Paddle per completare un acquisto, potrebbe essere necessario fornire un identificatore d’ordine esistente in modo da poter associare l’acquisto completato all’ordine corrispondente quando il cliente viene reindirizzato nuovamente alla tua applicazione.

Per fare ciò, puoi fornire un array di dati personalizzati al metodo checkout. Immaginiamo che un Order in sospeso venga creato nella nostra applicazione quando un utente inizia il processo di checkout. Ricorda, i modelli Cart e Order in questo esempio sono illustrativi e non forniti da Cashier. Sei libero di implementare questi concetti in base alle esigenze della tua applicazione:

use App\Models\Cart;
use App\Models\Order;
use Illuminate\Http\Request;

Route::get('/cart/{cart}/checkout', function (Request $request, Cart $cart) {
    $order = Order::create([
        'cart_id' => $cart->id,
        'price_ids' => $cart->price_ids,
        'status' => 'incomplete',
    ]);

    $checkout = $request->user()->checkout($order->price_ids)
        ->customData(['order_id' => $order->id]);

    return view('billing', ['checkout' => $checkout]);
})->name('checkout');

Come puoi vedere nell’esempio sopra, quando un utente inizia il processo di checkout, forniamo tutti gli identificatori di prezzo di Paddle associati al carrello/ordine al metodo checkout. Naturalmente, la tua applicazione è responsabile di associare questi elementi al "carrello della spesa" o all’ordine man mano che il cliente li aggiunge. Forniamo anche l’ID dell’ordine al Checkout Overlay di Paddle tramite il metodo customData.

Naturalmente, probabilmente vorrai contrassegnare l’ordine come "completato" una volta che il cliente ha terminato il processo di checkout. Per fare ciò, puoi ascoltare i webhook inviati da Paddle e sollevati tramite eventi da Cashier per memorizzare le informazioni dell’ordine nel tuo database.

Per iniziare, ascolta l’evento TransactionCompleted inviato da Cashier. Tipicamente, dovresti registrare l’ascoltatore di eventi nel metodo boot dell’AppServiceProvider della tua applicazione:

use App\Listeners\CompleteOrder;
use Illuminate\Support\Facades\Event;
use Laravel\Paddle\Events\TransactionCompleted;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Event::listen(TransactionCompleted::class, CompleteOrder::class);
}

In questo esempio, l’ascoltatore CompleteOrder potrebbe essere simile al seguente:

namespace App\Listeners;

use App\Models\Order;
use Laravel\Paddle\Cashier;
use Laravel\Paddle\Events\TransactionCompleted;

class CompleteOrder
{
    /**
     * Gestisci l'evento webhook di Cashier in arrivo.
     */
    public function handle(TransactionCompleted $event): void
    {
        $orderId = $event->payload['data']['custom_data']['order_id'] ?? null;

        $order = Order::findOrFail($orderId);

        $order->update(['status' => 'completed']);
    }
}

Per ulteriori informazioni, consulta la documentazione di Paddle sui dati contenuti nell’evento transaction.completed.

Vendita di Abbonamenti

Prima di utilizzare Paddle Checkout, dovresti definire i Prodotti con prezzi fissi nel tuo dashboard di Paddle. Inoltre, dovresti configurare la gestione dei webhook di Paddle.

Offrire prodotti e fatturazione in abbonamento tramite la tua applicazione può sembrare complicato. Tuttavia, grazie a Cashier e a Paddle’s Checkout Overlay, puoi costruire facilmente integrazioni di pagamento moderne e robuste.

Per imparare come vendere abbonamenti utilizzando Cashier e Paddle’s Checkout Overlay, consideriamo lo scenario semplice di un servizio di abbonamento con un piano mensile di base (price_basic_monthly) e un piano annuale (price_basic_yearly). Questi due prezzi potrebbero essere raggruppati sotto un prodotto "Basic" (pro_basic) nel nostro dashboard di Paddle. Inoltre, il nostro servizio di abbonamento potrebbe offrire un piano Expert come pro_expert.

Innanzitutto, scopriamo come un cliente può abbonarsi ai nostri servizi. Naturalmente, puoi immaginare che il cliente possa cliccare un pulsante "subscribe" per il piano Basic nella pagina dei prezzi della nostra applicazione. Questo pulsante attiverà un Paddle Checkout Overlay per il piano scelto. Per iniziare, avviamo una sessione di checkout tramite il metodo checkout:

use Illuminate\Http\Request;

Route::get('/subscribe', function (Request $request) {
    $checkout = $request->user()->checkout('price_basic_monthly')
        ->returnTo(route('dashboard'));

    return view('subscribe', ['checkout' => $checkout]);
})->name('subscribe');

Nella vista subscribe, includeremo un pulsante per mostrare il Checkout Overlay. Il componente Blade paddle-button è incluso con Cashier Paddle; tuttavia, puoi anche renderizzare manualmente un overlay checkout:

<x-paddle-button :checkout="$checkout" class="px-8 py-4">
    Subscribe
</x-paddle-button>

Ora, quando il pulsante Subscribe viene cliccato, il cliente potrà inserire i propri dati di pagamento e avviare l’abbonamento. Per sapere quando l’abbonamento è effettivamente iniziato (dato che alcuni metodi di pagamento richiedono alcuni secondi per essere elaborati), dovresti anche configurare la gestione dei webhook di Cashier.

Ora che i clienti possono avviare abbonamenti, dobbiamo limitare alcune parti della nostra applicazione affinché solo gli utenti abbonati possano accedervi. Naturalmente, possiamo sempre determinare lo stato attuale dell’abbonamento di un utente tramite il metodo subscribed fornito dal trait Billable di Cashier:

@if ($user->subscribed())
    <p>Sei abbonato.</p>
@endif

Possiamo anche determinare facilmente se un utente è abbonato a un prodotto o prezzo specifico:

@if ($user->subscribedToProduct('pro_basic'))
    <p>Sei abbonato al nostro prodotto Basic.</p>
@endif

@if ($user->subscribedToPrice('price_basic_monthly'))
    <p>Sei abbonato al nostro piano Basic mensile.</p>
@endif

Creare un Middleware per Utenti Abbonati

Per comodità, potresti creare un middleware che determina se la richiesta in arrivo proviene da un utente abbonato. Una volta definito questo middleware, puoi facilmente assegnarlo a una rotta per impedire agli utenti non abbonati di accedere alla rotta:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class Subscribed
{
    /**
     * Gestisce una richiesta in arrivo.
     */
    public function handle(Request $request, Closure $next): Response
    {
        if (! $request->user()?->subscribed()) {
            // Reindirizza l'utente alla pagina di abbonamento e chiedi di abbonarsi...
            return redirect('/subscribe');
        }

        return $next($request);
    }
}

Una volta definito il middleware, puoi assegnarlo a una rotta:

use App\Http\Middleware\Subscribed;

Route::get('/dashboard', function () {
    // ...
})->middleware([Subscribed::class]);

Consenti ai clienti di gestire il loro piano di fatturazione

Naturalmente, i clienti potrebbero voler cambiare il loro piano di abbonamento a un altro prodotto o "livello". Nell’esempio sopra, vorremmo permettere al cliente di passare da un abbonamento mensile a uno annuale. Per fare ciò, dovrai implementare un pulsante che indirizza alla rotta seguente:

use Illuminate\Http\Request;

Route::put('/subscription/{price}/swap', function (Request $request, $price) {
    $user->subscription()->swap($price); // Con "$price" che in questo esempio è "price_basic_yearly".

    return redirect()->route('dashboard');
})->name('subscription.swap');

Oltre a cambiare i piani, dovrai anche permettere ai clienti di cancellare il loro abbonamento. Come per il cambio dei piani, fornisci un pulsante che indirizza alla seguente rotta:

use Illuminate\Http\Request;

Route::put('/subscription/cancel', function (Request $request, $price) {
    $user->subscription()->cancel();

    return redirect()->route('dashboard');
})->name('subscription.cancel');

Ora il tuo abbonamento sarà annullato alla fine del periodo di fatturazione.

Finché hai configurato la gestione dei webhook di Cashier, Cashier manterrà automaticamente sincronizzate le tabelle del database relative a Cashier della tua applicazione ispezionando i webhook in arrivo da Paddle. Quindi, ad esempio, quando cancelli un abbonamento di un cliente tramite il dashboard di Paddle, Cashier riceverà il webhook corrispondente e segnerà l’abbonamento come "cancellato" nel database della tua applicazione.

Checkout Sessions

La maggior parte delle operazioni per fatturare i clienti vengono eseguite utilizzando i "checkout" tramite il Checkout Overlay widget di Paddle o utilizzando il checkout inline.

Prima di elaborare i pagamenti tramite checkout con Paddle, dovresti definire il link di pagamento predefinito della tua applicazione nel dashboard delle impostazioni di checkout di Paddle.

Overlay Checkout

Prima di mostrare il widget Overlay Checkout, devi generare una sessione di checkout usando Cashier. Una sessione di checkout informerà il widget di checkout sull’operazione di fatturazione che deve essere eseguita:

    use Illuminate\Http\Request;

    Route::get('/buy', function (Request $request) {
        $checkout = $user->checkout('pri_34567')
            ->returnTo(route('dashboard'));

        return view('billing', ['checkout' => $checkout]);
    });

Cashier include un componente paddle-button Blade component. Puoi passare la sessione di checkout a questo componente come "prop". Poi, quando questo pulsante viene cliccato, verrà mostrato il widget di checkout di Paddle:

<x-paddle-button :checkout="$checkout" class="px-8 py-4">
    Abbonati
</x-paddle-button>

Per impostazione predefinita, questo mostrerà il widget utilizzando lo stile predefinito di Paddle. Puoi personalizzare il widget aggiungendo attributi supportati da Paddle come l’attributo data-theme="light" al componente:

<x-paddle-button :checkout="$checkout" class="px-8 py-4" data-theme="light">
    Abbonati
</x-paddle-button>

Il widget di checkout di Paddle è asincrono. Una volta che l’utente crea un abbonamento all’interno del widget, Paddle invierà alla tua applicazione un webhook in modo da poter aggiornare correttamente lo stato dell’abbonamento nel database della tua applicazione. Pertanto, è importante che tu configuri correttamente i webhook per gestire i cambiamenti di stato da Paddle.

Dopo un cambiamento nello stato dell’abbonamento, il ritardo nel ricevere il webhook corrispondente è solitamente minimo, ma dovresti considerarlo nella tua applicazione tenendo conto che l’abbonamento del tuo utente potrebbe non essere immediatamente disponibile dopo aver completato il checkout.

Visualizzare manualmente un checkout in overlay

Puoi anche visualizzare manualmente un checkout in overlay senza usare i componenti Blade integrati di Laravel. Per iniziare, genera la sessione di checkout come mostrato negli esempi precedenti:

    use Illuminate\Http\Request;

    Route::get('/buy', function (Request $request) {
        $checkout = $user->checkout('pri_34567')
            ->returnTo(route('dashboard'));

        return view('billing', ['checkout' => $checkout]);
    });

Successivamente, puoi usare Paddle.js per inizializzare il checkout. In questo esempio, creeremo un link con la classe paddle_button. Paddle.js rileverà questa classe e mostrerà il checkout in overlay quando il link viene cliccato:

<?php
$items = $checkout->getItems();
$customer = $checkout->getCustomer();
$custom = $checkout->getCustomData();
?>

<a
    href='#!'
    class='paddle_button'
    data-items='{!! json_encode($items) !!}'
    @if ($customer) data-customer-id='{{ $customer->paddle_id }}' @endif
    @if ($custom) data-custom-data='{{ json_encode($custom) }}' @endif
    @if ($returnUrl = $checkout->getReturnUrl()) data-success-url='{{ $returnUrl }}' @endif
>
    Buy Product
</a>

Inline Checkout

Se non desideri utilizzare il widget di checkout in stile "overlay" di Paddle, Paddle offre anche l’opzione di visualizzare il widget inline. Sebbene questo approccio non consenta di modificare i campi HTML del checkout, permette di incorporare il widget all’interno della tua applicazione.

Per facilitare l’inizio con il checkout inline, Cashier include un componente Blade paddle-checkout. Per iniziare, dovresti generare una sessione di checkout:

use Illuminate\Http\Request;

Route::get('/buy', function (Request $request) {
    $checkout = $user->checkout('pri_34567')
        ->returnTo(route('dashboard'));

    return view('billing', ['checkout' => $checkout]);
});

Successivamente, puoi passare la sessione di checkout all’attributo checkout del componente:

<x-paddle-checkout :checkout="$checkout" class="w-full" />

Per regolare l’altezza del componente di checkout inline, puoi passare l’attributo height al componente Blade:

<x-paddle-checkout :checkout="$checkout" class="w-full" height="500" />

Consulta la guida di Paddle sul Checkout Inline e le impostazioni di checkout disponibili per ulteriori dettagli sulle opzioni di personalizzazione del checkout inline.

Renderizzazione Manuale di un Checkout Inline

Puoi anche renderizzare manualmente un checkout inline senza utilizzare i componenti Blade integrati di Laravel. Per iniziare, genera la sessione di checkout come mostrato negli esempi precedenti:

use Illuminate\Http\Request;

Route::get('/buy', function (Request $request) {
    $checkout = $user->checkout('pri_34567')
        ->returnTo(route('dashboard'));

    return view('billing', ['checkout' => $checkout]);
});

Successivamente, puoi usare Paddle.js per inizializzare il checkout. In questo esempio, mostriamo come fare utilizzando Alpine.js; tuttavia, puoi modificare questo esempio per il tuo stack frontend:

<?php
$options = $checkout->options();

$options['settings']['frameTarget'] = 'paddle-checkout';
$options['settings']['frameInitialHeight'] = 366;
?>

<div class="paddle-checkout" x-data="{}" x-init="
    Paddle.Checkout.open(@json($options));
">
</div>

Guest Checkouts

A volte, potresti dover creare una sessione di checkout per utenti che non necessitano di un account con la tua applicazione. Per farlo, puoi usare il metodo guest:

use Illuminate\Http\Request;
use Laravel\Paddle\Checkout;

Route::get('/buy', function (Request $request) {
    $checkout = Checkout::guest('pri_34567')
        ->returnTo(route('home'));

    return view('billing', ['checkout' => $checkout]);
});

Successivamente, puoi fornire la sessione di checkout ai componenti Blade Paddle button o inline checkout.

Anteprime dei Prezzi

Paddle permette di personalizzare i prezzi per valuta, permettendoti sostanzialmente di configurare prezzi diversi per paesi differenti. Cashier Paddle ti consente di recuperare tutti questi prezzi usando il metodo previewPrices. Questo metodo accetta gli ID dei prezzi per cui desideri recuperare i prezzi:

use Laravel\Paddle\Cashier;

$prices = Cashier::previewPrices(['pri_123', 'pri_456']);

La valuta sarà determinata basandosi sull’indirizzo IP della richiesta; tuttavia, puoi opzionalmente fornire un paese specifico per recuperare i prezzi per esso:

use Laravel\Paddle\Cashier;

$prices = Cashier::previewPrices(['pri_123', 'pri_456'], ['address' => [
    'country_code' => 'BE',
    'postal_code' => '1234',
]]);

Una volta recuperati i prezzi, puoi mostrarli come preferisci:

<ul>
    @foreach ($prices as $price)
        <li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
    @endforeach
</ul>

Puoi anche mostrare separatamente il prezzo netto e l’importo dell’imposta:

<ul>
    @foreach ($prices as $price)
        <li>{{ $price->product['name'] }} - {{ $price->subtotal() }} (+ {{ $price->tax() }} tax)</li>
    @endforeach
</ul>

Per ulteriori informazioni, consulta la documentazione API di Paddle riguardo le anteprime dei prezzi.

Anteprime dei Prezzi per il Cliente

Se un utente è già un cliente e desideri mostrare i prezzi applicabili a quel cliente, puoi farlo recuperando i prezzi direttamente dall’istanza del cliente:

use App\Models\User;

$prices = User::find(1)->previewPrices(['pri_123', 'pri_456']);

Internamente, Cashier utilizzerà l’ID cliente dell’utente per ottenere i prezzi nella loro valuta. Ad esempio, un utente che vive negli Stati Uniti vedrà i prezzi in dollari USA, mentre un utente in Belgio vedrà i prezzi in Euro. Se non viene trovata una valuta corrispondente, verrà utilizzata la valuta predefinita del prodotto. Puoi personalizzare tutti i prezzi di un prodotto o di un piano di abbonamento nel pannello di controllo di Paddle.

Sconti

Puoi anche scegliere di mostrare i prezzi dopo uno sconto. Quando chiami il metodo previewPrices, fornisci l’ID dello sconto tramite l’opzione discount_id:

    use Laravel\Paddle\Cashier;

    $prices = Cashier::previewPrices(['pri_123', 'pri_456'], [
        'discount_id' => 'dsc_123'
    ]);

Successivamente, mostra i prezzi calcolati:

<ul>
    @foreach ($prices as $price)
        <li>{{ $price->product['name'] }} - {{ $price->total() }}</li>
    @endforeach
</ul>

Clienti

Impostazioni Predefinite del Cliente

Cashier ti permette di definire alcune impostazioni predefinite utili per i tuoi clienti quando crei le sessioni di checkout. Impostare questi valori predefiniti consente di precompilare l’indirizzo email e il nome del cliente, in modo che possano passare immediatamente alla parte di pagamento del widget di checkout. Puoi impostare questi valori predefiniti sovrascrivendo i seguenti metodi nel tuo modello billable:

/**
 * Ottieni il nome del cliente da associare a Paddle.
 */
public function paddleName(): string|null
{
    return $this->name;
}

/**
 * Ottieni l'indirizzo email del cliente da associare a Paddle.
 */
public function paddleEmail(): string|null
{
    return $this->email;
}

Questi valori predefiniti saranno utilizzati per ogni azione in Cashier che genera una sessione di checkout.

Recupero dei Clienti

Puoi recuperare un cliente utilizzando il suo Paddle Customer ID con il metodo Cashier::findBillable. Questo metodo restituirà un’istanza del modello billable:

use Laravel\Paddle\Cashier;

$user = Cashier::findBillable($customerId);

Creazione dei Clienti

Occasionalmente, potresti voler creare un cliente Paddle senza iniziare un abbonamento. Puoi fare ciò utilizzando il metodo createAsCustomer:

    $customer = $user->createAsCustomer();

Viene restituita un’istanza di Laravel\Paddle\Customer. Una volta che il cliente è stato creato in Paddle, potrai iniziare un abbonamento in un secondo momento. Puoi fornire un array opzionale $options per passare eventuali parametri aggiuntivi di creazione del cliente supportati dall’API di Paddle:

    $customer = $user->createAsCustomer($options);

Sottoscrizioni

Creazione delle Sottoscrizioni

Per creare una sottoscrizione, prima recupera un’istanza del tuo modello billable dal database, che tipicamente sarà un’istanza di App\Models\User. Una volta ottenuta l’istanza del modello, puoi utilizzare il metodo subscribe per creare la sessione di checkout del modello:

use Illuminate\Http\Request;

Route::get('/user/subscribe', function (Request $request) {
    $checkout = $request->user()->subscribe($premium = 12345, 'default')
        ->returnTo(route('home'));

    return view('billing', ['checkout' => $checkout]);
});

Il primo argomento passato al metodo subscribe è il prezzo specifico a cui l’utente si sta iscrivendo. Questo valore deve corrispondere all’identificatore del prezzo in Paddle. Il metodo returnTo accetta un URL a cui l’utente verrà reindirizzato dopo aver completato correttamente il checkout. Il secondo argomento passato al metodo subscribe dovrebbe essere il "tipo" interno della sottoscrizione. Se la tua applicazione offre solo una singola sottoscrizione, potresti chiamarla default o primary. Questo tipo di sottoscrizione è solo per uso interno dell’applicazione e non è destinato a essere mostrato agli utenti. Inoltre, non dovrebbe contenere spazi e non dovrebbe mai essere modificato dopo aver creato la sottoscrizione.

Puoi anche fornire un array di metadati personalizzati riguardanti la sottoscrizione usando il metodo customData:

$checkout = $request->user()->subscribe($premium = 12345, 'default')
    ->customData(['key' => 'value'])
    ->returnTo(route('home'));

Una volta creata una sessione di checkout per la sottoscrizione, la sessione di checkout può essere fornita al componente paddle-button Blade incluso con Cashier Paddle:

<x-paddle-button :checkout="$checkout" class="px-8 py-4">
    Subscribe
</x-paddle-button>

Dopo che l’utente ha completato il checkout, Paddle invierà un webhook subscription_created. Cashier riceverà questo webhook e configurerà la sottoscrizione per il tuo cliente. Per assicurarti che tutti i webhook siano correttamente ricevuti e gestiti dalla tua applicazione, assicurati di aver configurato correttamente la gestione dei webhook.

Verifica dello Stato Abbonamento

Una volta che un utente è abbonato alla tua applicazione, puoi verificare lo stato del suo abbonamento utilizzando diversi metodi comodi. Innanzitutto, il metodo subscribed restituisce true se l’utente ha un abbonamento valido, anche se l’abbonamento è attualmente in periodo di prova:

    if ($user->subscribed()) {
        // ...
    }

Se la tua applicazione offre più abbonamenti, puoi specificare l’abbonamento quando invochi il metodo subscribed:

    if ($user->subscribed('default')) {
        // ...
    }

Il metodo subscribed è anche un’ottima opzione per un middleware di route, permettendoti di filtrare l’accesso alle route e ai controller in base allo stato dell’abbonamento dell’utente:

    <?php

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Http\Request;
    use Symfony\Component\HttpFoundation\Response;

    class EnsureUserIsSubscribed
    {
        /**
         * Gestisce una richiesta in arrivo.
         *
         * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
         */
        public function handle(Request $request, Closure $next): Response
        {
            if ($request->user() && ! $request->user()->subscribed()) {
                // Questo utente non è un cliente pagante...
                return redirect('/billing');
            }

            return $next($request);
        }
    }

Se desideri determinare se un utente è ancora nel periodo di prova, puoi utilizzare il metodo onTrial. Questo metodo può essere utile per decidere se mostrare un avviso all’utente che è ancora nel periodo di prova:

    if ($user->subscription()->onTrial()) {
        // ...
    }

Il metodo subscribedToPrice può essere usato per determinare se l’utente è abbonato a un determinato piano basato su un dato ID prezzo di Paddle. In questo esempio, verificheremo se l’abbonamento default dell’utente è attivamente abbonato al prezzo mensile:

    if ($user->subscribedToPrice($monthly = 'pri_123', 'default')) {
        // ...
    }

Il metodo recurring può essere usato per determinare se l’utente ha attualmente un abbonamento attivo e non è più nel periodo di prova o nel periodo di grazia:

    if ($user->subscription()->recurring()) {
        // ...
    }

Stato Abbonamento Cancellato

Per verificare se un utente era abbonato attivamente ma ha poi cancellato l’abbonamento, puoi utilizzare il metodo canceled:

if ($user->subscription()->canceled()) {
    // ...
}

Puoi anche verificare se un utente ha cancellato il suo abbonamento ma si trova ancora nel "periodo di grazia" fino a quando l’abbonamento non scade completamente. Ad esempio, se un utente cancella un abbonamento il 5 marzo che era originariamente previsto per scadere il 10 marzo, l’utente si trova nel "periodo di grazia" fino al 10 marzo. Inoltre, il metodo subscribed restituirà comunque true durante questo periodo:

if ($user->subscription()->onGracePeriod()) {
    // ...
}

Stato In Ritardo

Se un pagamento fallisce per un abbonamento, verrà contrassegnato come past_due. Quando il tuo abbonamento è in questo stato, non sarà attivo fino a quando il cliente non aggiornerà le proprie informazioni di pagamento. Puoi determinare se un abbonamento è in ritardo utilizzando il metodo pastDue sull’istanza dell’abbonamento:

if ($user->subscription()->pastDue()) {
    // ...
}

Quando un abbonamento è in ritardo, dovresti istruire l’utente a aggiornare le informazioni di pagamento.

Se desideri che gli abbonamenti siano ancora considerati validi quando sono past_due, puoi utilizzare il metodo keepPastDueSubscriptionsActive fornito da Cashier. Tipicamente, questo metodo dovrebbe essere chiamato nel metodo register del tuo AppServiceProvider:

use Laravel\Paddle\Cashier;

/**
 * Registra i servizi dell'applicazione.
 */
public function register(): void
{
    Cashier::keepPastDueSubscriptionsActive();
}

Quando un abbonamento è in stato past_due non può essere modificato finché le informazioni di pagamento non sono state aggiornate. Pertanto, i metodi swap e updateQuantity genereranno un’eccezione quando l’abbonamento è in stato past_due.

Scope delle Sottoscrizioni

La maggior parte degli stati delle sottoscrizioni sono disponibili come scope di query, permettendoti di interrogare facilmente il tuo database per le sottoscrizioni in uno stato specifico:

// Ottieni tutte le sottoscrizioni valide...
$subscriptions = Subscription::query()->valid()->get();

// Ottieni tutte le sottoscrizioni cancellate di un utente...
$subscriptions = $user->subscriptions()->canceled()->get();

Di seguito è riportata una lista completa degli scope disponibili:

Subscription::query()->valid();
Subscription::query()->onTrial();
Subscription::query()->expiredTrial();
Subscription::query()->notOnTrial();
Subscription::query()->active();
Subscription::query()->recurring();
Subscription::query()->pastDue();
Subscription::query()->paused();
Subscription::query()->notPaused();
Subscription::query()->onPausedGracePeriod();
Subscription::query()->notOnPausedGracePeriod();
Subscription::query()->canceled();
Subscription::query()->notCanceled();
Subscription::query()->onGracePeriod();
Subscription::query()->notOnGracePeriod();

Addebiti Singoli per Abbonamenti

Gli addebiti singoli per abbonamenti ti permettono di addebitare agli abbonati un costo una tantum oltre al loro abbonamento. Devi fornire uno o più ID prezzo quando invochi il metodo charge:

// Addebita un singolo prezzo...
$response = $user->subscription()->charge('pri_123');

// Addebita più prezzi contemporaneamente...
$response = $user->subscription()->charge(['pri_123', 'pri_456']);

Il metodo charge non addebiterà effettivamente il cliente fino al prossimo intervallo di fatturazione del loro abbonamento. Se vuoi fatturare il cliente immediatamente, puoi usare invece il metodo chargeAndInvoice:

$response = $user->subscription()->chargeAndInvoice('pri_123');

Aggiornamento delle Informazioni di Pagamento

Paddle salva sempre un metodo di pagamento per ogni abbonamento. Se vuoi aggiornare il metodo di pagamento predefinito per un abbonamento, devi reindirizzare il tuo cliente alla pagina di aggiornamento del metodo di pagamento ospitata da Paddle utilizzando il metodo redirectToUpdatePaymentMethod sul modello di abbonamento:

    use Illuminate\Http\Request;

    Route::get('/update-payment-method', function (Request $request) {
        $user = $request->user();

        return $user->subscription()->redirectToUpdatePaymentMethod();
    });

Quando un utente ha finito di aggiornare le proprie informazioni, Paddle invierà un webhook subscription_updated e i dettagli dell’abbonamento saranno aggiornati nel database della tua applicazione.

Cambiare Piano

Dopo che un utente si è iscritto alla tua applicazione, potrebbe voler cambiare il suo piano di abbonamento. Per aggiornare il piano di abbonamento di un utente, devi passare l’identificatore del prezzo di Paddle al metodo swap dell’abbonamento:

use App\Models\User;

$user = User::find(1);

$user->subscription()->swap($premium = 'pri_456');

Se desideri cambiare piano e fatturare immediatamente l’utente invece di aspettare il prossimo ciclo di fatturazione, puoi usare il metodo swapAndInvoice:

$user = User::find(1);

$user->subscription()->swapAndInvoice($premium = 'pri_456');

Prorata

Per impostazione predefinita, Paddle applica la prorata alle spese quando si cambia piano. Il metodo noProrate può essere utilizzato per aggiornare gli abbonamenti senza effettuare la prorata delle spese:

$user->subscription('default')->noProrate()->swap($premium = 'pri_456');

Se desideri disabilitare la prorata e fatturare i clienti immediatamente, puoi utilizzare il metodo swapAndInvoice insieme a noProrate:

$user->subscription('default')->noProrate()->swapAndInvoice($premium = 'pri_456');

Oppure, per non addebitare il cliente per un cambio di abbonamento, puoi utilizzare il metodo doNotBill:

$user->subscription('default')->doNotBill()->swap($premium = 'pri_456');

Per maggiori informazioni sulle politiche di proration di Paddle, consulta la documentazione sulla proration di Paddle.

Quantità dell’Abbonamento

A volte gli abbonamenti sono influenzati dalla "quantità". Ad esempio, un’applicazione di gestione progetti potrebbe addebitare $10 al mese per progetto. Per aumentare o diminuire facilmente la quantità del tuo abbonamento, usa i metodi incrementQuantity e decrementQuantity:

    $user = User::find(1);

    $user->subscription()->incrementQuantity();

    // Aggiungi cinque alla quantità attuale dell'abbonamento...
    $user->subscription()->incrementQuantity(5);

    $user->subscription()->decrementQuantity();

    // Sottrai cinque dalla quantità attuale dell'abbonamento...
    $user->subscription()->decrementQuantity(5);

In alternativa, puoi impostare una quantità specifica usando il metodo updateQuantity:

    $user->subscription()->updateQuantity(10);

Il metodo noProrate può essere usato per aggiornare la quantità dell’abbonamento senza prorrateare gli addebiti:

    $user->subscription()->noProrate()->updateQuantity(10);

Quantità per Sottoscrizioni con Molti Prodotti

Se la tua sottoscrizione è una sottoscrizione con molti prodotti, dovresti passare l’ID del prezzo di cui vuoi incrementare o decrementare la quantità come secondo argomento ai metodi increment / decrement:

$user->subscription()->incrementQuantity(1, 'price_chat');

Abbonamenti con Prodotti Multipli

Abbonamenti con prodotti multipli ti permettono di assegnare più prodotti di fatturazione a un unico abbonamento. Ad esempio, immagina di creare un’applicazione di assistenza clienti "helpdesk" con un costo base di abbonamento di $10 al mese, ma che offre un prodotto aggiuntivo live chat per $15 aggiuntivi al mese.

Quando crei le sessioni di checkout per l’abbonamento, puoi specificare più prodotti per un dato abbonamento passando un array di prezzi come primo argomento del metodo subscribe:

use Illuminate\Http\Request;

Route::post('/user/subscribe', function (Request $request) {
    $checkout = $request->user()->subscribe([
        'price_monthly',
        'price_chat',
    ]);

    return view('billing', ['checkout' => $checkout]);
});

Nell’esempio sopra, il cliente avrà due prezzi associati al suo abbonamento default. Entrambi i prezzi verranno addebitati nei rispettivi intervalli di fatturazione. Se necessario, puoi passare un array associativo di coppie chiave/valore per indicare una quantità specifica per ogni prezzo:

$user = User::find(1);

$checkout = $user->subscribe('default', ['price_monthly', 'price_chat' => 5]);

Se desideri aggiungere un altro prezzo a un abbonamento esistente, devi usare il metodo swap dell’abbonamento. Quando utilizzi il metodo swap, devi anche includere i prezzi e le quantità attuali dell’abbonamento:

$user = User::find(1);

$user->subscription()->swap(['price_chat', 'price_original' => 2]);

L’esempio sopra aggiungerà il nuovo prezzo, ma il cliente non verrà addebitato finché non inizierà il prossimo ciclo di fatturazione. Se desideri addebitare immediatamente il cliente, puoi usare il metodo swapAndInvoice:

$user->subscription()->swapAndInvoice(['price_chat', 'price_original' => 2]);

Puoi rimuovere prezzi dagli abbonamenti utilizzando il metodo swap e omettendo il prezzo che vuoi rimuovere:

$user->subscription()->swap(['price_original' => 2]);

Non puoi rimuovere l’ultimo prezzo di un abbonamento. Invece, devi semplicemente cancellare l’abbonamento.

Abbonamenti Multipli

Paddle permette ai tuoi clienti di avere più abbonamenti contemporaneamente. Ad esempio, potresti gestire una palestra che offre un abbonamento per il nuoto e uno per il sollevamento pesi, ognuno con prezzi differenti. Naturalmente, i clienti dovrebbero poter sottoscrivere uno o entrambi i piani.

Quando la tua applicazione crea abbonamenti, puoi fornire il tipo di abbonamento al metodo subscribe come secondo argomento. Il tipo può essere una qualsiasi stringa che rappresenta il tipo di abbonamento che l’utente sta iniziando:

    use Illuminate\Http\Request;
    
    Route::post('/swimming/subscribe', function (Request $request) {
        $checkout = $request->user()->subscribe($swimmingMonthly = 'pri_123', 'swimming');
    
        return view('billing', ['checkout' => $checkout]);
    });

In questo esempio, abbiamo iniziato un abbonamento mensile per il nuoto per il cliente. Tuttavia, potrebbe voler passare a un abbonamento annuale in seguito. Quando si modifica l’abbonamento del cliente, possiamo semplicemente cambiare il prezzo sull’abbonamento swimming:

    $user->subscription('swimming')->swap($swimmingYearly = 'pri_456');

Naturalmente, puoi anche cancellare completamente l’abbonamento:

    $user->subscription('swimming')->cancel();

Sospendere gli Abbonamenti

Per sospendere un abbonamento, chiama il metodo pause sull’abbonamento dell’utente:

$user->subscription()->pause();

Quando un abbonamento è sospeso, Cashier imposterà automaticamente la colonna paused_at nel tuo database. Questa colonna serve a determinare quando il metodo paused dovrebbe iniziare a restituire true. Ad esempio, se un cliente sospende un abbonamento il 1° marzo, ma l’abbonamento non doveva rinnovarsi fino al 5 marzo, il metodo paused continuerà a restituire false fino al 5 marzo. Questo perché solitamente un utente può continuare a usare un’applicazione fino alla fine del suo ciclo di fatturazione.

Per impostazione predefinita, la sospensione avviene al prossimo intervallo di fatturazione in modo che il cliente possa utilizzare il resto del periodo per cui ha pagato. Se vuoi sospendere immediatamente un abbonamento, puoi usare il metodo pauseNow:

$user->subscription()->pauseNow();

Usando il metodo pauseUntil, puoi sospendere l’abbonamento fino a un momento specifico:

$user->subscription()->pauseUntil(now()->addMonth());

Oppure, puoi usare il metodo pauseNowUntil per sospendere immediatamente l’abbonamento fino a un determinato punto nel tempo:

$user->subscription()->pauseNowUntil(now()->addMonth());

Puoi verificare se un utente ha sospeso il suo abbonamento ma è ancora nel suo "periodo di grazia" usando il metodo onPausedGracePeriod:

if ($user->subscription()->onPausedGracePeriod()) {
    // ...
}

Per riprendere un abbonamento sospeso, puoi invocare il metodo resume sull’abbonamento:

$user->subscription()->resume();

Un abbonamento non può essere modificato mentre è sospeso. Se vuoi passare a un piano diverso o aggiornare le quantità, devi prima riprendere l’abbonamento.

Annullamento Abbonamenti

Per annullare un abbonamento, chiama il metodo cancel sull’abbonamento dell’utente:

$user->subscription()->cancel();

Quando un abbonamento viene annullato, Cashier imposterà automaticamente la colonna ends_at nel tuo database. Questa colonna serve a determinare quando il metodo subscribed deve iniziare a restituire false. Ad esempio, se un cliente annulla un abbonamento il 1° marzo, ma l’abbonamento non doveva terminare fino al 5 marzo, il metodo subscribed continuerà a restituire true fino al 5 marzo. Questo accade perché generalmente all’utente è permesso continuare a utilizzare l’applicazione fino alla fine del ciclo di fatturazione.

Puoi determinare se un utente ha annullato il proprio abbonamento ma è ancora nel "periodo di grazia" usando il metodo onGracePeriod:

if ($user->subscription()->onGracePeriod()) {
    // ...
}

Se desideri annullare un abbonamento immediatamente, puoi chiamare il metodo cancelNow sull’abbonamento:

$user->subscription()->cancelNow();

Per impedire che un abbonamento in periodo di grazia venga annullato, puoi invocare il metodo stopCancelation:

$user->subscription()->stopCancelation();

Gli abbonamenti di Paddle non possono essere ripresi dopo l’annullamento. Se il tuo cliente desidera riprendere il proprio abbonamento, dovrà crearne uno nuovo.

Prove di Abbonamento

Con Metodo di Pagamento in Anticipo

Se desideri offrire periodi di prova ai tuoi clienti raccogliendo comunque le informazioni del metodo di pagamento in anticipo, dovresti impostare un periodo di prova nella dashboard di Paddle sul prezzo a cui il cliente si sta iscrivendo. Successivamente, avvia la sessione di checkout normalmente:

use Illuminate\Http\Request;

Route::get('/user/subscribe', function (Request $request) {
    $checkout = $request->user()->subscribe('pri_monthly')
                ->returnTo(route('home'));

    return view('billing', ['checkout' => $checkout]);
});

Quando la tua applicazione riceve l’evento subscription_created, Cashier imposterà la data di fine del periodo di prova nel record della sottoscrizione nel database della tua applicazione e istruirà Paddle a non iniziare a fatturare il cliente fino a dopo questa data.

Se l’abbonamento del cliente non viene annullato prima della data di fine del periodo di prova, verrà addebitato non appena la prova scade. Pertanto, dovresti assicurarti di notificare i tuoi utenti della data di fine della prova.

Puoi determinare se l’utente è nel periodo di prova utilizzando il metodo onTrial dell’istanza utente o il metodo onTrial dell’istanza di sottoscrizione. I due esempi seguenti sono equivalenti:

if ($user->onTrial()) {
    // ...
}

if ($user->subscription()->onTrial()) {
    // ...
}

Per determinare se un periodo di prova esistente è scaduto, puoi utilizzare i metodi hasExpiredTrial:

if ($user->hasExpiredTrial()) {
    // ...
}

if ($user->subscription()->hasExpiredTrial()) {
    // ...
}

Per determinare se un utente è in prova per un tipo specifico di sottoscrizione, puoi fornire il tipo ai metodi onTrial o hasExpiredTrial:

if ($user->onTrial('default')) {
    // ...
}

if ($user->hasExpiredTrial('default')) {
    // ...
}

Senza Metodo di Pagamento Iniziale

Se desideri offrire periodi di prova senza raccogliere in anticipo le informazioni sul metodo di pagamento dell’utente, puoi impostare la colonna trial_ends_at nel record del cliente associato al tuo utente alla data di fine prova desiderata. Questo viene solitamente fatto durante la registrazione dell’utente:

use App\Models\User;

$user = User::create([
    // ...
]);

$user->createAsCustomer([
    'trial_ends_at' => now()->addDays(10)
]);

Cashier si riferisce a questo tipo di prova come "trial generico", poiché non è collegato a nessuna sottoscrizione esistente. Il metodo onTrial sull’istanza User restituirà true se la data corrente non supera il valore di trial_ends_at:

if ($user->onTrial()) {
    // L'utente è nel periodo di prova...
}

Quando sei pronto per creare una vera sottoscrizione per l’utente, puoi utilizzare il metodo subscribe come di consueto:

use Illuminate\Http\Request;

Route::get('/user/subscribe', function (Request $request) {
    $checkout = $user->subscribe('pri_monthly')
        ->returnTo(route('home'));

    return view('billing', ['checkout' => $checkout]);
});

Per recuperare la data di fine prova dell’utente, puoi usare il metodo trialEndsAt. Questo metodo restituirà un’istanza di data Carbon se l’utente è in prova oppure null se non lo è. Puoi anche passare un parametro opzionale di tipo sottoscrizione se desideri ottenere la data di fine prova per una specifica sottoscrizione diversa da quella predefinita:

if ($user->onTrial('default')) {
    $trialEndsAt = $user->trialEndsAt();
}

Puoi usare il metodo onGenericTrial se desideri sapere specificamente che l’utente è all’interno del suo periodo di prova "generico" e non ha ancora creato una sottoscrizione effettiva:

if ($user->onGenericTrial()) {
    // L'utente è nel suo periodo di prova "generico"...
}

Estendere o Attivare una Prova

Puoi estendere un periodo di prova esistente su un abbonamento utilizzando il metodo extendTrial e specificando il momento in cui la prova dovrebbe terminare:

    $user->subscription()->extendTrial(now()->addDays(5));

Oppure, puoi attivare immediatamente un abbonamento terminando la prova chiamando il metodo activate sull’abbonamento:

    $user->subscription()->activate();

Gestione dei Webhook di Paddle

Paddle può notificare la tua applicazione di una varietà di eventi tramite webhook. Per impostazione predefinita, una rotta che punta al controller webhook di Cashier è registrata dal service provider di Cashier. Questo controller gestirà tutte le richieste webhook in arrivo.

Per impostazione predefinita, questo controller gestirà automaticamente l’annullamento degli abbonamenti che hanno troppe charge fallite, gli aggiornamenti degli abbonamenti e i cambiamenti dei metodi di pagamento; tuttavia, come presto scopriremo, puoi estendere questo controller per gestire qualsiasi evento webhook di Paddle tu desideri.

Per assicurarti che la tua applicazione possa gestire i webhook di Paddle, assicurati di configurare l’URL del webhook nel pannello di controllo di Paddle. Per impostazione predefinita, il controller webhook di Cashier risponde al percorso URL /paddle/webhook. L’elenco completo di tutti i webhook che dovresti abilitare nel pannello di controllo di Paddle è:

  • Customer Updated
  • Transaction Completed
  • Transaction Updated
  • Subscription Created
  • Subscription Updated
  • Subscription Paused
  • Subscription Canceled

Assicurati di proteggere le richieste in arrivo con il middleware verifica firma webhook incluso in Cashier.

Webhooks e Protezione CSRF

Poiché i webhook di Paddle devono bypassare la protezione CSRF di Laravel, devi assicurarti che Laravel non tenti di verificare il token CSRF per i webhook in arrivo di Paddle. Per fare ciò, dovresti escludere paddle/* dalla protezione CSRF nel file bootstrap/app.php della tua applicazione:

    ->withMiddleware(function (Middleware $middleware) {
        $middleware->validateCsrfTokens(except: [
            'paddle/*',
        ]);
    })

Webhook e Sviluppo Locale

Per permettere a Paddle di inviare webhook alla tua applicazione durante lo sviluppo locale, dovrai rendere la tua applicazione accessibile tramite un servizio di condivisione siti come Ngrok o Expose. Se stai sviluppando la tua applicazione localmente usando Laravel Sail, puoi utilizzare il comando di condivisione sito di Sail.

Definizione dei Gestori di Eventi Webhook

Cashier gestisce automaticamente la cancellazione degli abbonamenti in caso di addebiti non riusciti e altri webhook comuni di Paddle. Tuttavia, se hai altri eventi webhook che desideri gestire, puoi farlo ascoltando i seguenti eventi che vengono emessi da Cashier:

  • Laravel\Paddle\Events\WebhookReceived
  • Laravel\Paddle\Events\WebhookHandled

Entrambi gli eventi contengono il payload completo del webhook di Paddle. Ad esempio, se desideri gestire il webhook transaction.billed, puoi registrare un listener che gestirà l’evento:

<?php

namespace App\Listeners;

use Laravel\Paddle\Events\WebhookReceived;

class PaddleEventListener
{
    /**
     * Gestisci i webhook ricevuti da Paddle.
     */
    public function handle(WebhookReceived $event): void
    {
        if ($event->payload['event_type'] === 'transaction.billed') {
            // Gestisci l'evento in arrivo...
        }
    }
}

Cashier emette anche eventi specifici per il tipo di webhook ricevuto. Oltre al payload completo da Paddle, contengono anche i modelli rilevanti utilizzati per elaborare il webhook, come il modello billable, l’abbonamento o la ricevuta:

  • Laravel\Paddle\Events\CustomerUpdated
  • Laravel\Paddle\Events\TransactionCompleted
  • Laravel\Paddle\Events\TransactionUpdated
  • Laravel\Paddle\Events\SubscriptionCreated
  • Laravel\Paddle\Events\SubscriptionUpdated
  • Laravel\Paddle\Events\SubscriptionPaused
  • Laravel\Paddle\Events\SubscriptionCanceled

Puoi anche sovrascrivere la route webhook predefinita definendo la variabile d’ambiente CASHIER_WEBHOOK nel file .env della tua applicazione. Questo valore deve essere l’URL completo della tua route webhook e deve corrispondere all’URL impostato nel pannello di controllo di Paddle:

CASHIER_WEBHOOK=https://example.com/my-paddle-webhook-url

Verifica delle firme dei Webhook

Per proteggere i tuoi webhook, puoi utilizzare le firme dei webhook di Paddle. Per comodità, Cashier include automaticamente un middleware che valida che la richiesta webhook in arrivo da Paddle sia valida.

Per abilitare la verifica dei webhook, assicurati che la variabile d’ambiente PADDLE_WEBHOOK_SECRET sia definita nel file .env della tua applicazione. Il segreto del webhook può essere ottenuto dal pannello di controllo del tuo account Paddle.

Addebiti Singoli

Pagamento per Prodotti

Se desideri avviare l’acquisto di un prodotto per un cliente, puoi usare il metodo checkout su un’istanza di modello billable per generare una sessione di checkout per l’acquisto. Il metodo checkout accetta uno o più ID di prezzo. Se necessario, puoi usare un array associativo per specificare la quantità del prodotto acquistato:

    use Illuminate\Http\Request;

    Route::get('/buy', function (Request $request) {
        $checkout = $request->user()->checkout(['pri_tshirt', 'pri_socks' => 5]);

        return view('buy', ['checkout' => $checkout]);
    });

Dopo aver generato la sessione di checkout, puoi usare il componente paddle-button fornito da Cashier componente Blade per permettere all’utente di visualizzare il widget di checkout di Paddle e completare l’acquisto:

<x-paddle-button :checkout="$checkout" class="px-8 py-4">
    Buy
</x-paddle-button>

Una sessione di checkout include un metodo customData, che ti permette di passare dati personalizzati alla creazione della transazione sottostante. Consulta la documentazione di Paddle per saperne di più sulle opzioni disponibili per passare dati personalizzati:

    $checkout = $user->checkout('pri_tshirt')
        ->customData([
            'custom_option' => $value,
        ]);

Rimborsare le Transazioni

Rimborsare le transazioni restituirà l’importo rimborsato al metodo di pagamento del cliente utilizzato al momento dell’acquisto. Se hai bisogno di rimborsare un acquisto tramite Paddle, puoi usare il metodo refund su un modello Cashier\Paddle\Transaction. Questo metodo accetta una motivazione come primo argomento, uno o più ID prezzo da rimborsare con importi opzionali come array associativo. Puoi recuperare le transazioni per un determinato modello billable usando il metodo transactions.

Ad esempio, immagina di voler rimborsare una transazione specifica per i prezzi pri_123 e pri_456. Vogliamo rimborsare completamente pri_123, ma solo due dollari per pri_456:

    use App\Models\User;

    $user = User::find(1);

    $transaction = $user->transactions()->first();

    $response = $transaction->refund('Accidental charge', [
        'pri_123', // Rimborsa completamente questo prezzo...
        'pri_456' => 200, // Rimborsa parzialmente questo prezzo...
    ]);

L’esempio sopra rimborsa specifici articoli in una transazione. Se vuoi rimborsare l’intera transazione, basta fornire una motivazione:

    $response = $transaction->refund('Accidental charge');

Per maggiori informazioni sui rimborsi, consulta la documentazione sui rimborsi di Paddle.

I rimborsi devono sempre essere approvati da Paddle prima di essere completamente elaborati.

Transazioni di Accredito

Proprio come per i rimborsi, puoi anche accreditare le transazioni. L’accredito delle transazioni aggiungerà i fondi al saldo del cliente, che potranno essere utilizzati per acquisti futuri. Le transazioni possono essere accreditate solo se sono raccolte manualmente e non per le transazioni raccolte automaticamente (come gli abbonamenti), poiché Paddle gestisce automaticamente gli accrediti degli abbonamenti:

$transaction = $user->transactions()->first();

// Credit a specific line item fully...
$response = $transaction->credit('Compensation', 'pri_123');

Per maggiori informazioni, consulta la documentazione di Paddle sugli accrediti.

Gli accrediti possono essere applicati solo per le transazioni raccolte manualmente. Le transazioni raccolte automaticamente vengono accreditate direttamente da Paddle.

Transazioni

Puoi facilmente recuperare un array delle transazioni di un modello billable tramite la proprietà transactions:

use App\Models\User;

$user = User::find(1);

$transactions = $user->transactions;

Le transazioni rappresentano i pagamenti per i tuoi prodotti e acquisti e sono accompagnate da fatture. Solo le transazioni completate vengono memorizzate nel database della tua applicazione.

Quando elenchi le transazioni per un cliente, puoi utilizzare i metodi dell’istanza della transazione per mostrare le informazioni di pagamento rilevanti. Ad esempio, potresti desiderare di elencare ogni transazione in una tabella, permettendo all’utente di scaricare facilmente una delle fatture:

<table>
    @foreach ($transactions as $transaction)
        <tr>
            <td>{{ $transaction->billed_at->toFormattedDateString() }}</td>
            <td>{{ $transaction->total() }}</td>
            <td>{{ $transaction->tax() }}</td>
            <td><a href="{{ route('download-invoice', $transaction->id) }}" target="_blank">Download</a></td>
        </tr>
    @endforeach
</table>

La route download-invoice potrebbe essere la seguente:

use Illuminate\Http\Request;
use Laravel\Paddle\Transaction;

Route::get('/download-invoice/{transaction}', function (Request $request, Transaction $transaction) {
    return $transaction->redirectToInvoicePdf();
})->name('download-invoice');

Pagamenti Passati e Futuri

Puoi usare i metodi lastPayment e nextPayment per recuperare e mostrare i pagamenti passati o futuri di un cliente per abbonamenti ricorrenti:

    use App\Models\User;

    $user = User::find(1);

    $subscription = $user->subscription();

    $lastPayment = $subscription->lastPayment();
    $nextPayment = $subscription->nextPayment();

Entrambi i metodi restituiscono un’istanza di Laravel\Paddle\Payment; tuttavia, lastPayment restituirà null quando le transazioni non sono ancora sincronizzate tramite webhook, mentre nextPayment restituirà null quando il ciclo di fatturazione è terminato (ad esempio, quando un abbonamento è stato annullato):

Next payment: {{ $nextPayment->amount() }} due on {{ $nextPayment->date()->format('d/m/Y') }}

Test

Durante i test, dovresti verificare manualmente il flusso di fatturazione per assicurarti che l’integrazione funzioni come previsto.

Per i test automatizzati, inclusi quelli eseguiti in un ambiente CI, puoi usare Laravel’s HTTP Client per simulare le chiamate HTTP a Paddle. Anche se questo non testa le risposte effettive di Paddle, fornisce un modo per testare la tua applicazione senza chiamare effettivamente l’API di Paddle.

Lascia un commento

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *