Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

emilioschepis's avatar

Laravel Spark Paddle for Jetstream Teams

Hi all!

I've recently purchased Laravel Spark to handle subscriptions, and I installed it on my Jestream project with teams support.

According to the cookbook in the documentation I should set the paddleEmail to be the team owner's email address.

use Spark\Billable;

class Team extends JetstreamTeam
{
    use Billable;

    public function paddleEmail(): string|null
    {
        return $this->owner->email;
    }
}

This is exactly what I expect, however I also want my users to be able to own multiple teams. In this case, when the user creates a second team, they receive an error because the customer table already has a record related to the same customer paddle_id (but for another billable model).

Is there a way in Spark / Cashier to allow users to own more than one team, and potentially pay more than one subscription?

Thank you,

Emilio

0 likes
2 replies
LaryAI's avatar
Level 58

Hi Emilio,

To allow users to own multiple teams and manage multiple subscriptions in Laravel Spark with Paddle, you need to handle the paddle_id uniquely for each team. The issue arises because the paddle_id is being reused across different teams owned by the same user, which Paddle does not support.

Here's a solution to handle this scenario:

  1. Override the paddleId method in your Team model: This will ensure that each team has a unique paddle_id.

  2. Generate a unique identifier for each team: You can use a UUID or any other unique identifier for each team.

Here's how you can implement this:

  1. Update the Team model:
use Spark\Billable;
use Illuminate\Support\Str;

class Team extends JetstreamTeam
{
    use Billable;

    // Ensure each team has a unique paddle_id
    public function paddleId(): string
    {
        if (is_null($this->attributes['paddle_id'])) {
            $this->attributes['paddle_id'] = (string) Str::uuid();
            $this->save();
        }

        return $this->attributes['paddle_id'];
    }

    public function paddleEmail(): string|null
    {
        return $this->owner->email;
    }
}
  1. Migrate the teams table to include paddle_id:

If your teams table does not already have a paddle_id column, you need to add it. Create a new migration:

php artisan make:migration add_paddle_id_to_teams_table --table=teams

Then, update the migration file:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddPaddleIdToTeamsTable extends Migration
{
    public function up()
    {
        Schema::table('teams', function (Blueprint $table) {
            $table->uuid('paddle_id')->nullable()->unique();
        });
    }

    public function down()
    {
        Schema::table('teams', function (Blueprint $table) {
            $table->dropColumn('paddle_id');
        });
    }
}

Run the migration:

php artisan migrate
  1. Ensure paddle_id is set when creating a new team:

You can set the paddle_id when a new team is created. This can be done in the Team model's creating event:

protected static function booted()
{
    static::creating(function ($team) {
        if (is_null($team->paddle_id)) {
            $team->paddle_id = (string) Str::uuid();
        }
    });
}

With these changes, each team will have a unique paddle_id, allowing users to own multiple teams and manage multiple subscriptions without conflicts.

I hope this helps! Let me know if you have any further questions.

Best, LaracastsGPT

emilioschepis's avatar

The answer from Lary is pretty similar to what other AIs have suggested.

However, unless I am missing something:

  1. the ManagesCustomer trait for Cashier only has paddleEmail and paddleName, not paddleId.
  2. I should not create a Paddle id manually, since that represents the id of the customer that is created on the Paddle dashboard

Furthermore, the paddle_id column is on the Customer model, which is handled by Spark / Cashier and has a unique() constraint.

Please or to participate in this conversation.