PHPackages                             charlielangridge/lunar-marketplace - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. charlielangridge/lunar-marketplace

ActiveLibrary[API Development](/categories/api)

charlielangridge/lunar-marketplace
==================================

Marketplace integration scaffolding for Lunar stores.

v0.0.1(1mo ago)045MITPHPPHP ^8.4CI failing

Since Apr 13Pushed 1mo agoCompare

[ Source](https://github.com/charlielangridge/lunar-marketplace)[ Packagist](https://packagist.org/packages/charlielangridge/lunar-marketplace)[ RSS](/packages/charlielangridge-lunar-marketplace/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (8)Versions (2)Used By (0)

Lunar Marketplace
=================

[](#lunar-marketplace)

Reusable marketplace integration scaffolding for Lunar stores.

Included channels
-----------------

[](#included-channels)

- Amazon UK
- eBay UK
- Etsy

Requirements
------------

[](#requirements)

- PHP 8.4+
- Laravel 12+
- A Lunar store (migrations assume a `products` or similar ownable entity)

---

Installation
------------

[](#installation)

### 1. Require the package

[](#1-require-the-package)

```
composer require charlielangridge/lunar-marketplace
```

### 2. Publish the config

[](#2-publish-the-config)

```
php artisan vendor:publish --tag=lunar-marketplace-config
```

### 3. Run migrations

[](#3-run-migrations)

```
php artisan migrate
```

This creates the channel account, listing, sync event, and order tables for each marketplace.

---

Configuration
-------------

[](#configuration)

After publishing, edit `config/lunar-marketplace.php`. All values can be overridden via environment variables.

### `.env` variables

[](#env-variables)

```
# Amazon UK
AMAZON_UK_MARKETPLACE_ID=A1F83G8C2ARO7P
AMAZON_UK_REGION=eu-west-1
AMAZON_UK_BASE_URL=https://sellingpartnerapi-eu.amazon.com
AMAZON_UK_CURRENCY=GBP

# eBay UK
EBAY_UK_MARKETPLACE_ID=EBAY_GB
EBAY_UK_BASE_URL=https://api.ebay.com
EBAY_UK_CURRENCY=GBP

# Etsy
ETSY_BASE_URL=https://api.etsy.com
ETSY_CURRENCY=GBP

# HTTP client (optional — defaults shown)
LUNAR_MARKETPLACE_TIMEOUT=20
LUNAR_MARKETPLACE_CONNECT_TIMEOUT=10
LUNAR_MARKETPLACE_RETRY_TIMES=3
```

---

Connecting a marketplace account
--------------------------------

[](#connecting-a-marketplace-account)

Each marketplace has a `ChannelAccount` model that stores credentials. Create one record per connected account.

### Amazon

[](#amazon)

```
use Charlielangridge\LunarMarketplace\Models\AmazonChannelAccount;

AmazonChannelAccount::create([
    'name'                => 'My Amazon UK Store',
    'marketplace_code'    => 'amazon.co.uk',
    'marketplace_id'      => config('lunar-marketplace.marketplaces.amazon.co.uk.marketplace_id'),
    'region'              => config('lunar-marketplace.marketplaces.amazon.co.uk.region'),
    'base_url'            => config('lunar-marketplace.marketplaces.amazon.co.uk.base_url'),
    'seller_id'           => 'AXXXXXXXXXX',
    'refresh_token'       => 'Atzr|...',
    'lwa_client_id'       => 'amzn1.application-oa2-client...',
    'lwa_client_secret'   => '...',
    'aws_access_key_id'   => 'AKIAIOSFODNN7EXAMPLE',
    'aws_secret_access_key' => 'wJalrXUtnFEMI/...',
    'currency'            => 'GBP',
    'active'              => true,
]);
```

### eBay

[](#ebay)

```
use Charlielangridge\LunarMarketplace\Models\Ebay\EbayChannelAccount;

EbayChannelAccount::create([
    'name'                  => 'My eBay UK Store',
    'marketplace_code'      => 'ebay.co.uk',
    'marketplace_id'        => config('lunar-marketplace.marketplaces.ebay.co.uk.marketplace_id'),
    'base_url'              => config('lunar-marketplace.marketplaces.ebay.co.uk.base_url'),
    'client_id'             => '...',
    'client_secret'         => '...',
    'refresh_token'         => '...',
    'merchant_location_key' => 'default',
    'currency'              => 'GBP',
    'active'                => true,
]);
```

### Etsy

[](#etsy)

```
use Charlielangridge\LunarMarketplace\Models\Etsy\EtsyChannelAccount;

EtsyChannelAccount::create([
    'name'           => 'My Etsy Shop',
    'marketplace_code' => 'etsy.com/uk',
    'base_url'       => config('lunar-marketplace.marketplaces.etsy.com/uk.base_url'),
    'shop_id'        => '12345678',
    'client_id'      => '...',
    'client_secret'  => '...',
    'refresh_token'  => '...',
    'api_key'        => '...',
    'currency'       => 'GBP',
    'active'         => true,
]);
```

---

Implementing the contracts
--------------------------

[](#implementing-the-contracts)

The package ships with three extensibility contracts per marketplace. Your app must implement and bind these.

### Contracts

[](#contracts)

ContractPurpose`{Channel}SellableResolverInterface`Resolves a listing to sellable data (title, description, attributes, images, inventory)`{Channel}PriceCalculatorInterface`Calculates the channel price from your website price`{Channel}OrderTransformerInterface`Transforms a raw marketplace order payload into your app's order format### Example: Amazon sellable resolver

[](#example-amazon-sellable-resolver)

```
namespace App\Marketplace\Amazon;

use Charlielangridge\LunarMarketplace\Contracts\AmazonSellableResolverInterface;
use Charlielangridge\LunarMarketplace\Data\AmazonChannelConfig;
use Charlielangridge\LunarMarketplace\Data\AmazonSellableData;
use Charlielangridge\LunarMarketplace\Models\AmazonListing;

class AmazonSellableResolver implements AmazonSellableResolverInterface
{
    public function resolve(AmazonListing $listing): AmazonSellableData
    {
        $product = $listing->owner; // your Lunar product

        return new AmazonSellableData(
            sellerSku: $listing->seller_sku,
            title: $product->translateAttribute('name'),
            description: $product->translateAttribute('description'),
            productType: 'HOME_BED_AND_BATH',
            brand: $product->brand,
            imageUrls: $product->images->map->original_url->all(),
            attributes: [],
            listingAttributes: [],
            inventory: $product->stock->value,
        );
    }
}
```

### Example: Amazon price calculator

[](#example-amazon-price-calculator)

```
namespace App\Marketplace\Amazon;

use Charlielangridge\LunarMarketplace\Contracts\AmazonPriceCalculatorInterface;
use Charlielangridge\LunarMarketplace\Data\AmazonChannelConfig;
use Charlielangridge\LunarMarketplace\Data\AmazonPriceBreakdown;
use Charlielangridge\LunarMarketplace\Models\AmazonListing;

class AmazonPriceCalculator implements AmazonPriceCalculatorInterface
{
    public function calculate(AmazonListing $listing, AmazonChannelConfig $config): AmazonPriceBreakdown
    {
        $websitePrice = $listing->owner->price->value; // minor units (pence)
        $markup = (int) round($websitePrice * 0.10); // 10% markup

        return new AmazonPriceBreakdown(
            websiteMinorAmount: $websitePrice,
            amazonMinorAmount: $websitePrice + $markup,
            markupMinorAmount: $markup,
            components: [],
            currency: $config->currency,
        );
    }
}
```

### Binding interfaces

[](#binding-interfaces)

Register your implementations in a service provider:

```
use Charlielangridge\LunarMarketplace\Contracts\AmazonSellableResolverInterface;
use Charlielangridge\LunarMarketplace\Contracts\AmazonPriceCalculatorInterface;
use Charlielangridge\LunarMarketplace\Contracts\AmazonOrderTransformerInterface;
use App\Marketplace\Amazon\AmazonSellableResolver;
use App\Marketplace\Amazon\AmazonPriceCalculator;
use App\Marketplace\Amazon\AmazonOrderTransformer;

public function register(): void
{
    $this->app->bind(AmazonSellableResolverInterface::class, AmazonSellableResolver::class);
    $this->app->bind(AmazonPriceCalculatorInterface::class, AmazonPriceCalculator::class);
    $this->app->bind(AmazonOrderTransformerInterface::class, AmazonOrderTransformer::class);

    // repeat for eBay and Etsy
}
```

---

Syncing listings
----------------

[](#syncing-listings)

Dispatch queue jobs to sync a listing to a marketplace:

```
use Charlielangridge\LunarMarketplace\Jobs\SyncAmazonListingJob;
use Charlielangridge\LunarMarketplace\Jobs\SyncEbayListingJob;
use Charlielangridge\LunarMarketplace\Jobs\SyncEtsyListingJob;

SyncAmazonListingJob::dispatch($amazonListing);
SyncEbayListingJob::dispatch($ebayListing);
SyncEtsyListingJob::dispatch($etsyListing);
```

Each job resolves the sellable and price via the bound contracts, calls the appropriate API, and updates the listing's `sync_status` and `last_synced_at`.

Importing orders
----------------

[](#importing-orders)

```
use Charlielangridge\LunarMarketplace\Jobs\ImportAmazonOrdersJob;
use Charlielangridge\LunarMarketplace\Jobs\ImportEbayOrdersJob;
use Charlielangridge\LunarMarketplace\Jobs\ImportEtsyOrdersJob;

ImportAmazonOrdersJob::dispatch($amazonChannelAccount);
ImportEbayOrdersJob::dispatch($ebayChannelAccount);
ImportEtsyOrdersJob::dispatch($etsyChannelAccount);
```

Imported orders are stored in `*_external_orders` tables. Your app's order transformer is called to produce the `transformed_payload`, which you then use to create Lunar orders.

---

Lunar admin panel integration
-----------------------------

[](#lunar-admin-panel-integration)

If your store uses a Filament-based admin panel, register `LunarMarketplacePlugin` to add marketplace navigation groups. Groups are only shown when at least one active channel account exists for that marketplace — disconnected marketplaces are hidden automatically.

Install Filament if you haven't already:

```
composer require filament/filament
```

Register the plugin on your panel provider:

```
use Charlielangridge\LunarMarketplace\LunarMarketplacePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            LunarMarketplacePlugin::make(),
        ]);
}
```

In your Filament resources, set the `$navigationGroup` to match the marketplace name so they appear under the correct group:

```
// app/Filament/Resources/AmazonListingResource.php
protected static ?string $navigationGroup = 'Amazon';

// app/Filament/Resources/EbayListingResource.php
protected static ?string $navigationGroup = 'eBay';

// app/Filament/Resources/EtsyListingResource.php
protected static ?string $navigationGroup = 'Etsy';
```

When no active account exists for a marketplace, that navigation group is not registered, so any resources in that group will be hidden from the sidebar.

---

Store-side responsibilities
---------------------------

[](#store-side-responsibilities)

The consuming app is expected to provide:

- Channel-specific sellable resolvers (maps listings to marketplace product data)
- Pricing parity calculators (applies markup/fee logic)
- Order transformers (converts marketplace orders to Lunar orders)
- Admin UI and Filament resources
- Product metadata models

---

Development
-----------

[](#development)

```
composer install
composer test
composer format
```

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance89

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

57d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f4c3198a248b9580414af6bd9a09b66c8307b409de14b5bbec32140974323474?d=identicon)[charlielangridge](/maintainers/charlielangridge)

---

Top Contributors

[![charlielangridge](https://avatars.githubusercontent.com/u/8578083?v=4)](https://github.com/charlielangridge "charlielangridge (4 commits)")

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/charlielangridge-lunar-marketplace/health.svg)

```
[![Health](https://phpackages.com/badges/charlielangridge-lunar-marketplace/health.svg)](https://phpackages.com/packages/charlielangridge-lunar-marketplace)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.4k](/packages/larastan-larastan)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

815320.5k3](/packages/defstudio-telegraph)[simplestats-io/laravel-client

Analytics for Laravel. Track visitors, registrations, and payments. Discover which channels actually drive revenue, not just traffic. Server-side, GDPR compliant, ad-blocker proof.

5019.3k](/packages/simplestats-io-laravel-client)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

45344.0k1](/packages/pressbooks-pressbooks)[api-platform/laravel

API Platform support for Laravel

59156.3k10](/packages/api-platform-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
