Managing Subscriptions with Laravel Cashier and Paddle

In this guide, we will explore how to correctly implement Laravel Cashier with Paddle for handling subscriptions in a Laravel 12.x application using Inertia, React, and TypeScript. This setup ensures that transactions and subscriptions are properly recorded in your database.

Overview

When integrating Paddle with Laravel Cashier, it is crucial to let Cashier manage the subscription lifecycle instead of manually creating customer records. This approach ensures that all transactions are logged correctly.

Step 1: Update Your Subscription Controller

In your SubscriptionController, you should utilize the checkout method provided by Cashier instead of manually creating a customer. Here’s how you can adjust your store method:

class SubscriptionController extends Controller
{
    public function store(Request $request)
    {
        try {
            $user = $request->user();
            $priceId = config('subscription.paddle_price_id');

            // Initiate the checkout process with Cashier
            $checkout = $user->checkout($priceId)->returnTo(route('dashboard'));

            // Return the checkout session data for the frontend
            return response()->json([
                'checkoutUrl' => $checkout->url,
            ]);

        } catch (\Exception $e) {
            Log::error('CHECKOUT - Subscription error: ' . $e->getMessage());
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }
}

Step 2: Update Your React Component

In your React component, you need to handle the checkout process by redirecting the user to the Paddle checkout URL returned from your controller:

import axios from 'axios';
import { type User } from '@/types';

export function useHandleCheckoutOpen(user: User) {
    return async () => {
        try {
            const response = await axios.post('/subscribe');

            if (response.data.error) {
                console.error('Server returned an error:', response.data.error);
                return;
            }

            const { checkoutUrl } = response.data;

            // Redirect to Paddle checkout
            window.location.href = checkoutUrl;

        } catch (error) {
            console.error('Error starting subscription:', error);
            if (error.response) {
                console.error('Server response:', error.response.data);
            }
        }
    };
}

Step 3: Ensure Your User Model is Billable

Make sure your User model includes the Billable trait to enable subscription management:

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
    // Your user model code...
}

Conclusion

By following these steps, you will ensure that your application correctly processes subscriptions and transactions using Laravel Cashier and Paddle. This setup allows for a seamless user experience and accurate transaction records in your database.