I recently built a full-featured Laravel package for CREEM, the developer-first Merchant of Record platform. Here's why I built it, what it does, and how you can start accepting payments in your Laravel app in under 5 minutes.
Why CREEM?
If you're selling SaaS, digital products, or software licenses, handling global payments is painful. You need to deal with:
- VAT/GST/sales tax across 190+ countries
- Payment processor integration
- Subscription lifecycle management
- License key distribution
- Refunds and disputes
CREEM handles all of this as a Merchant of Record at 3.9% + 30 cents with zero monthly fees. They take legal responsibility for tax compliance so you can focus on building your product.
The only thing missing? A proper Laravel package.
What I Built
creem/laravel is a comprehensive Laravel package that wraps the entire CREEM API with Laravel-native patterns:
24 API Methods via Facade
use Creem\Laravel\Facades\Creem;
// Create a checkout in one line
$checkout = Creem::createCheckout('prod_abc123', [
'success_url' => route('checkout.success'),
'customer' => ['email' => $user->email],
]);
return redirect($checkout['checkout_url']);
Every CREEM API endpoint is covered: Products, Checkouts, Subscriptions, Customers, Transactions, Licenses, and Discounts.
Billable Trait (Laravel Cashier-style)
Instead of calling the Facade directly, attach the Billable trait to your User model:
use Creem\Laravel\Traits\Billable;
class User extends Authenticatable
{
use Billable;
}
Now billing methods live directly on your model:
$user->checkout('prod_abc123', ['success_url' => '/thanks']);
$user->creemSubscriptions();
$user->cancelSubscription('sub_xyz', 'scheduled');
$user->billingPortalUrl();
15 Webhook Events
The package auto-registers a webhook endpoint at POST /creem/webhook with HMAC-SHA256 signature verification. Every webhook type maps to a typed Laravel event:
// In your EventServiceProvider
CheckoutCompleted::class => [GrantAccessListener::class],
SubscriptionCanceled::class => [RevokeAccessListener::class],
I also added AccessGranted and AccessRevoked convenience events, inspired by CREEM's TypeScript SDK pattern. These fire automatically on the right webhook combinations, so you don't need to remember which events mean "give access" vs "revoke access":
Event::listen(AccessGranted::class, function ($event) {
// Fires on: checkout.completed, subscription.active, subscription.paid
$user = User::where('email', $event->payload['customer']['email'])->first();
$user->update(['has_access' => true]);
});
Auto Sandbox Detection
Use a test API key (creem_test_*) and the package automatically routes to the sandbox API. Switch to production (creem_*) and it hits the live API. Zero config changes needed.
Full Test Suite
78 tests with 148 assertions covering every API method, webhook signature verification, event dispatching, and edge cases. The CI matrix runs across PHP 8.1-8.4 and Laravel 10, 11, 12.
5-Minute Quick Start
1. Install
composer require creem/laravel
php artisan vendor:publish --tag=creem-config
php artisan vendor:publish --tag=creem-migrations
php artisan migrate
2. Configure
CREEM_API_KEY=creem_test_your_key
CREEM_WEBHOOK_SECRET=whsec_your_secret
3. Add Billable to User
use Creem\Laravel\Traits\Billable;
class User extends Authenticatable
{
use Billable;
}
4. Create a Checkout
Route::post('/buy', function (Request $request) {
$checkout = $request->user()->checkout('prod_your_product', [
'success_url' => url('/thanks'),
]);
return redirect($checkout['checkout_url']);
});
5. Handle Webhooks
// app/Listeners/GrantAccess.php
use Creem\Laravel\Events\AccessGranted;
class GrantAccess
{
public function handle(AccessGranted $event): void
{
$email = $event->payload['customer']['email'] ?? null;
User::where('email', $email)->update(['is_premium' => true]);
}
}
That's it. CREEM handles the payment page, tax calculation, and receipt. Your webhook listener grants access when payment succeeds.
Live Demo
Check it out live: https://creem.h90.space
The demo runs the included Docker app connected to the CREEM sandbox API. It features a live dashboard with webhook event tracking, interactive detail drawers, product listing pulled from the CREEM API, and a complete checkout flow — all with a premium dark glassmorphism UI.
Want to run it yourself? The repo includes everything:
cd examples/demo
cp .env.example .env
# Add your CREEM API key
docker compose up -d --build
Package Highlights
| Feature | Details |
|---|---|
| API Methods | 24 (full CREEM API coverage) |
| Webhook Events | 15 typed Laravel events |
| Tests | 78 tests, 148 assertions |
| PHP Support | 8.1, 8.2, 8.3, 8.4 |
| Laravel Support | 10, 11, 12 |
| Code Style | Laravel Pint enforced |
| CI/CD | GitHub Actions matrix |
| Artisan Commands |
creem:webhook-secret, creem:list-products
|
| Error Handling | 4 typed exception classes with trace IDs |
Links
- Live Demo: creem.h90.space
- Packagist: packagist.org/packages/creem/laravel
- GitHub: github.com/Haniamin90/creem-laravel
- CREEM: creem.io
- CREEM Docs: docs.creem.io
Built by Hani Amin (Discord: xh90) for the CREEM Scoops Laravel integration bounty.
Top comments (1)
Two things I noticed.
Support for PHP 8.1? Supporting a dead version means the package is missing features that are common nowadays. Like 8.2 readonly classes and
#[\SensitiveParameter].I looked at the API classes and most have no input checks on the array input, while the documentation has keys, types and required information. That information should be code.
This hides more of the API implementation. So when customer_id becomes CID people don't have to grep their code. It will just work.
And it doesn't give developers the option to add other keys, that could be use to hack the API in the worst case. This API is handling peoples money, so keep everything as tight as possible.
Also returning an array is just lazy.