This is the follow up post to using a renderless component for Paddle paylinks.
Sometimes you may have na application that has a few subscription options, sometimes not.
If you have a few subscription options available and you are displaying them for the user to pick one, there is a request each time to the Paddle API endpoint to generate a payment link, this in turn will reduce the response time for the subscriptions settings page, this sucks.
We will solve this delay by continuing this split of loading the payment links after the initial response.
Creating a Controller to handle the links
So to begin, you need a controller to handle the generation of the links, because this is an API controller basically, you won't need the extra methods of a normal CRUD controller, so we can go with an invokable one.
php artisan make:controller Api\\SubscriptionLinksListController --invokable
You should have a new controller under App\Http\Controllers\Api
. (Please don't feel forced to use the name I have put here, you can use whatever you feel is better.)
Getting the Paddle paylinks from Cashier
Inside our __invoke
we are going to place our main logic to check for the user and then create paddle links.
Things we want from this controller are the following:
- We need a link for each subscription type we offering to the user
- Which one the user is currently on
- The 'pretty' name that a user understands
- Possibly some features or highlights for the front-end
So with that in mind, lets get the links sorted first off, below is our skeleton of code that we will need:
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use App\Models\Plan;
...
public function __invoke(Request $request)
{
/**
* This part assumes that the current billable instance is the authorised user.
*/
$billable = $request->user();
// Our list of available plans, note the name could be anything here for the plan model.
$plans = Plan::whereIsAvailable(true)
->get(['id', 'paddle_id', 'title', 'name', 'is_available']);
$subscriptions = $plans->map(function($plan) use($billable){
if ($currentSubscription = $billable->subscribedToPlan($plan->name)) {
/** For this example you can just giving them the option to cancel the plan*/
$payLink = $billable->subscription($plan->name)->cancelUrl();
} else {
$payLink = $billable->newSubscription($plan->name, $plan->paddle_id)
->returnTo(route('billing'))
->create();
}
return [
'title' => $plan->title,
'name' => $plan->name,
'paylink' => $paylink,
'current' => $currentSubscription,
];
});
return new JsonResponse([
"data" => $subscriptions,
]);
}
Lets break down the current parts of this code.
Getting the current billable
model
The line of code that reads $billable = $request->user();
is where we get the billable model from cashier, this is the model we will use to compare if they have any active plans currently.
A point to note about this is that the Billable model could be anything, if you get that from a different source, then change that line.
For example
// if you have an Account model that is billable, then it could look like this.
$billable = Account::where('user_id', $request->user()->account_id)->firstOrFail();
Fetching the plan names for the Paddle subscriptions
You could manually hard-code the plans into place via config files, this would give you the same option of fetching them with the config file.
This would let you keep the changes in version control, but if you need to change a plan and make a custom one for a customer, you would have to go through a deploy cycle to get it to them.
In this instance, the plan details are fetched from a database table, this gives us the option to even scope the plans to certain people.
The important information to store in the plan table is the following:
- Paddle Product/Subscription ID
- The name cashier will search with
- A name to show the user in normal naming convention, not
'pro-plan-version-1-iteration-4'
:)
Creating the return data from our API
The data that is returned to the frontend code contains only the information that matters for the UI to be rendered, the Paddle Paylink, the name and if it's currently subscribed to.
This makes the data returned light so if they have network issues it isn't a large payload. Making it quicker basically.
The data is returned in the form of a JsonResponse
, this wraps the data in the appropriate headers and HTTP response codes.
You could always create a Json Resource class that has the same structure for any subscription data returned from your app, this would allow you to have a consistent pattern of data across your entire app if you are working on a large team. All each person has to do is call new SubscriptionLinkResource($data)
Getting the links from the frontend
To get the data from the front end you can use the component outlined in this post: Using a renderless component for Paddle paylinks.
Or you could create your own version of that.
Either way, you need to make sure to have an entry in your routes file.
Something like Route::get('/billing/subscriptions', SubscriptionLinksListController::class)->middleware('auth:api');
would be enough.
Alternatives
You could use a Resource class in Laravel to contain the structure for each link, then bundle it as a collection handling the fetching of each Paddle link inside the resource class, this would make for a very skinny controller, basically just a fetch of the plans from the DB or from the configuration files, then you can have something like return SubscriptionLinksCollection($data)
I will maybe make an additional version of that later on an link it here.
I hope you enjoyed this, please reach out with feedback or suggestions for improvements. You can find me on twitter @iexistin3d.
Have a great time and keep safe 🙃
Top comments (0)