enjin/platform-marketplace
Enjin Platform Marketplace is a Laravel package that adds NFT marketplace capabilities to Enjin Platform applications. It includes listings, auctions, offers, sales, and wallet integration, helping developers build blockchain-backed marketplaces with familiar Laravel workflows.
The marketplace package is an add-on to the Enjin Platform — it assumes you already have a working enjin/platform-core install (Laravel app, GraphQL endpoint via Lighthouse, wallet/daemon services configured). Do that first; the marketplace package will not bootstrap a platform on its own.
composer require enjin/platform-marketplace
Laravel's package auto-discovery should register Enjin\Platform\Marketplace\MarketplaceServiceProvider. Confirm it appears in php artisan about or by checking bootstrap/cache/packages.php. If you're on a platform version that disables discovery, add it manually to config/app.php.
Publish and run the scaffolding:
php artisan vendor:publish --provider="Enjin\Platform\Marketplace\MarketplaceServiceProvider"
php artisan migrate
This typically publishes:
config/marketplace.php — feature flags, listing fee/royalty defaults, escrow wallet settings, chain referencesmarketplace_listings, marketplace_sales, marketplace_bids — names vary by version; inspect the published migration folder)Where to look first: config/marketplace.php, then the package's src/Models/ directory. The models tell you what state exists and which relationships your application code will hang off. After that, hit your GraphQL endpoint with an introspection query — the marketplace mutations/queries (CreateListing, CancelListing, FillListing, GetListings etc. — names drift between versions) are the real API surface you'll integrate against.
First use case: list a collection's token for sale and query it back. Authenticate as a wallet (the platform's auth flow), call the CreateListing mutation with the token, price, and currency, then verify the listing appears in a GetListings query and that a corresponding row exists in the database. That single loop exercises escrow, signing, and the persistence layer — if it works, most other flows will.
Wrap GraphQL mutations in a service layer. Don't scatter CreateListing calls through controllers. The marketplace mutations almost always touch an on-chain transaction plus a DB row, and you'll want retries, logging, and transaction boundaries in one place.
final class ListingService
{
public function __construct(private GraphQLClient $graphql) {}
public function list(Token $token, string $price, string $currency): Listing
{
$response = $this->graphql->mutate(CreateListing::class, [
'tokenId' => $token->uuid,
'price' => $price,
'currency' => $currency,
]);
return Listing::findOrFail($response->listingId);
}
}
React to domain events. The package fires events around the listing lifecycle (created, filled, cancelled, expired). Bind listeners in a dedicated MarketplaceEventServiceProvider rather than EventServiceProvider so the wiring stays obvious:
protected $listen = [
\Enjin\Platform\Marketplace\Events\ListingWasFilled::class => [
SendSellerPayoutNotification::class,
SyncOwnershipToCrm::class,
],
];
Use jobs for anything that signs or broadcasts. On-chain operations are slow and flaky. Queue the marketplace call, put the listing ID in the job payload, and let the job retry with backoff. Give the job an idempotency_key column on your own table so a retried job doesn't double-list the same token.
Query listings by joining on your own data. The marketplace tables are lean; you'll almost always want to join them against your token/collection tables to render "my listings" pages. A read-side repository (or a dedicated MarketplaceListingResource) keeps the N+1s out of your controllers.
Listing::with(['token.collection'])
->where('seller_wallet', $wallet->address)
->where('status', 'active')
->latest()
->paginate(25);
Schedule the reconciliation command. Listings expire, on-chain state drifts from DB state, and webhooks get missed. The package usually exposes a marketplace:sync or similar artisan command — schedule it every few minutes as a safety net:
$schedule->command('marketplace:sync')->everyFiveMinutes()->withoutOverlapping();
Layer your own API on top if you're not a GraphQL shop. Many teams wrap the marketplace mutations with a small REST facade and a resource transformer — it keeps frontend contracts stable when the package's GraphQL schema shifts between minor versions.
composer.json in the marketplace package pins a range of enjin/platform-core. Mismatches produce cryptic Lighthouse schema-merge errors at boot rather than at install time. Run composer why enjin/platform-core when the schema won't compile.database/migrations/. Re-publish into a fresh directory and diff.php artisan vendor:publish --tag=marketplace-schema (or the Lighthouse schema merge directive) needs a re-run when the package version bumps. A stale schema silently hides new mutations and you'll debug your client for an hour.php artisan config:cache is safe, but if you toggled a marketplace feature flag in config/marketplace.php and forgot to clear, the flag is frozen at bootstrap. config:clear after any config edit.php artisan queue:restart in your deploy script, or you'll run yesterday's CreateListing handler against today's schema.marketplace.fake / a MarketplaceFake binding in the test suite). Bind it in tests/TestCase.php and assert on your service layer, not on the package internals — that gives you upgrade resilience.How can I help you explore Laravel packages today?