PHPackages                             hejunjie/trade-splitter - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. hejunjie/trade-splitter

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

hejunjie/trade-splitter
=======================

一个灵活、可扩展的交易/利润分账组件，提供百分比、固定金额、阶梯与递归分账等内置策略，并支持注册自定义策略 | A flexible and scalable transaction/profit sharing component, offering built-in strategies such as percentage, fixed amount, tiered, and recursive sharing, and supporting the registration of custom strategies

v1.1.0(1mo ago)1013MITPHPPHP ^8.1

Since Oct 13Pushed 1mo agoCompare

[ Source](https://github.com/zxc7563598/php-trade-splitter)[ Packagist](https://packagist.org/packages/hejunjie/trade-splitter)[ RSS](/packages/hejunjie-trade-splitter/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (2)Dependencies (2)Versions (3)Used By (0)

PHP Trade Splitter
==================

[](#php-trade-splitter)

English ｜ [简体中文](./README.zh-CN.md)

A flexible, extensible trade/profit distribution component. Features **8 built-in split strategies** covering percentage, fixed amount, weighted, equal, ladder, recursive, ceiling, and priority scenarios — with support for custom strategies.

Perfect for multi-level commissions, e-commerce settlements, platform fees, and agent profit sharing.

> This project has been parsed by [Zread](https://zread.ai/zxc7563598/php-trade-splitter) — click to view an AI-generated summary of the code structure and logic.

Features
--------

[](#features)

- 🧮 **8 Built-in Strategies**: percentage, fixed, weighted, equal, ladder, recursive, ceiling, and priority — covering virtually all split scenarios
- 🏗️ **Typed Participants**: each strategy provides a corresponding Participant class with constructor-time validation (fail fast) and IDE autocomplete support
- 🔌 **Extensible**: implement the `StrategyInterface` to register custom strategies with the same first-class experience as built-in ones
- 📦 **Backward Compatible**: works with plain arrays for quick calls or typed participant objects — both styles can be mixed
- 🛡️ **Strict Validation**: overflow rates, excessive amounts, conflicting ladder rules — errors are thrown explicitly rather than silently computing incorrect results
- ⚡ **Zero Dependencies**: requires only PHP ^8.1; a single `composer require` is all it takes

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

[](#installation)

```
composer require hejunjie/trade-splitter
```

Quick Start
-----------

[](#quick-start)

### Option 1: Plain Arrays (Quick)

[](#option-1-plain-arrays-quick)

```
use Hejunjie\TradeSplitter\Splitter;

// Percentage split: A gets 60%, B gets 40%
$result = Splitter::split(1000, [
    ['name' => 'A', 'rate' => 0.6],
    ['name' => 'B', 'rate' => 0.4],
], 'percentage');

foreach ($result as $allocation) {
    printf("%s: %.2f (%.2f%%)\n", $allocation->name, $allocation->amount, $allocation->ratio * 100);
}
// Output:
// A: 600.00 (60.00%)
// B: 400.00 (40.00%)
```

### Option 2: Typed Participants (Recommended)

[](#option-2-typed-participants-recommended)

```
use Hejunjie\TradeSplitter\Splitter;
use Hejunjie\TradeSplitter\Participants\PercentageParticipant;
use Hejunjie\TradeSplitter\Participants\PriorityParticipant;

// Percentage split
$result = Splitter::split(1000, [
    new PercentageParticipant('A', 0.6),
    new PercentageParticipant('B', 0.4),
], 'percentage');

// Priority split: platform fee → commission → merchant revenue
$result = Splitter::split(1000, [
    PriorityParticipant::fixed('Platform Fee', 100),
    PriorityParticipant::rate('Commission', 0.1),
    PriorityParticipant::residual('Merchant'),
], 'priority');
```

Typed participants validate parameters at construction time. Combined with IDE autocomplete, they significantly reduce typos and invalid data entering the calculation logic.

Built-in Strategies
-------------------

[](#built-in-strategies)

StrategyClassDescription`percentage`PercentageStrategyProportional split; all rates must sum to 1.0`fixed`FixedStrategyFixed-amount split; a participant named "platform" automatically receives the remainder`weighted`WeightedStrategyWeight-based split with automatic normalization — no need to pre-calculate percentages`equal`EqualStrategyEqual split among N parties`ladder`LadderStrategyTiered rates based on amount thresholds; ideal for sales commission tiers`recursive`RecursiveStrategyMulti-level commission chain; each level takes a cut from the previous level's earnings`ceiling`CeilingStrategyProportional split with per-participant caps; overflow is redistributed to uncapped participants`priority`PriorityStrategySequential deduction in array order; supports fixed, rate, and residual modes### Percentage Split `percentage`

[](#percentage-split-percentage)

All `rate` values must sum to exactly 1.0.

```
$result = Splitter::split(1000, [
    ['name' => 'Platform', 'rate' => 0.1],
    ['name' => 'Author',   'rate' => 0.9],
], 'percentage');
// Platform: 100.00, Author: 900.00
```

### Fixed Amount Split `fixed`

[](#fixed-amount-split-fixed)

Each participant receives a predefined fixed amount. A participant named `platform` is handled specially: it does not participate in the fixed allocation and instead receives whatever remains.

```
$result = Splitter::split(3000, [
    ['name' => 'Agent A', 'amount' => 200],
    ['name' => 'Agent B', 'amount' => 300],
], 'fixed');
// Agent A: 200, Agent B: 300 (remaining 2500 unallocated)
```

### Weighted Split `weighted`

[](#weighted-split-weighted)

Amounts are distributed proportionally by weight — normalization is automatic.

```
$result = Splitter::split(1000, [
    ['name' => 'A', 'weight' => 3],
    ['name' => 'B', 'weight' => 2],
    ['name' => 'C', 'weight' => 1],
], 'weighted');
// A: 500 (3/6), B: 333.33 (2/6), C: 166.67 (1/6)
```

### Equal Split `equal`

[](#equal-split-equal)

The total amount is split equally among all participants.

```
$result = Splitter::split(1000, [
    ['name' => 'A'],
    ['name' => 'B'],
    ['name' => 'C'],
    ['name' => 'D'],
], 'equal');
// 250 each
```

### Ladder Split `ladder`

[](#ladder-split-ladder)

Matches the total amount against tiered thresholds. Use `null` for an unbounded top tier (only one allowed).

```
$result = Splitter::split(5000, [
    [
        'name' => 'Agent A',
        'ladders' => [
            ['max' => 1000, 'rate' => 0.05],
            ['max' => 5000, 'rate' => 0.10],
            ['max' => null, 'rate' => 0.15],
        ],
    ],
    ['name' => 'Platform', 'rate' => 0.05],
], 'ladder');
// 5000 hits the second tier: Agent A = 5000 × 10% = 500, Platform = 5000 × 5% = 250
```

### Recursive Split `recursive`

[](#recursive-split-recursive)

Multi-level commission chain — each level takes a percentage of the previous level's earnings.

```
$result = Splitter::split(10000, [
    ['name' => 'Level 1', 'rate' => 0.2],
    ['name' => 'Level 2', 'rate' => 0.2],
    ['name' => 'Level 3', 'rate' => 0.2],
], 'recursive');
// Level 1 net: 1600, Level 2 net: 320, Level 3 net: 80
```

### Ceiling Split `ceiling`

[](#ceiling-split-ceiling)

Proportional split with optional per-participant caps (`max`). Overflow from capped participants is redistributed among uncapped ones.

```
$result = Splitter::split(1000, [
    ['name' => 'A', 'rate' => 0.5, 'max' => 300],   // capped at 300
    ['name' => 'B', 'rate' => 0.5, 'max' => null],   // uncapped, receives overflow
], 'ceiling');
// A: 300 (cap reached), B: 700 (includes A's overflow of 200)
```

### Priority Split `priority`

[](#priority-split-priority)

Participants are processed in array order, each deducting from the remaining pool. First in line gets served first.

```
$result = Splitter::split(1000, [
    ['name' => 'Gateway Fee', 'fixed' => 5],
    ['name' => 'Commission',  'rate' => 0.05],
    ['name' => 'Merchant',    'residual' => true],
], 'priority');
// Gateway Fee: 5, Commission: 49.75, Merchant: 945.25
```

Typed Participants
------------------

[](#typed-participants)

Each strategy has a corresponding Participant class. **Recommended for production use.** Compared to plain arrays, typed participants offer:

- **Constructor-time validation**: invalid parameters throw immediately, not deep inside the calculation
- **IDE-friendly**: autocomplete for property names and factory methods
- **Clear semantics**: different strategy participants have distinct types, preventing mix-ups (e.g. `PercentageParticipant` vs `RecursiveParticipant`)

StrategyParticipant ClassKey Parameterspercentage`PercentageParticipant``name`, `rate`fixed`FixedParticipant``name`, `amount`weighted`WeightedParticipant``name`, `weight`equal`EqualParticipant``name`ladder`LadderParticipant``name`, `rate` or `ladders`recursive`RecursiveParticipant``name`, `rate`ceiling`CeilingParticipant``name`, `rate`, `max` (optional)priority`PriorityParticipant``fixed()` / `rate()` / `residual()` factory methods```
use Hejunjie\TradeSplitter\Participants\LadderParticipant;
use Hejunjie\TradeSplitter\Participants\Ladder;
use Hejunjie\TradeSplitter\Participants\PriorityParticipant;

// Ladder participant (ladder mode + fixed-rate mode mixed)
Splitter::split(5000, [
    new LadderParticipant('Agent A', ladders: [
        new Ladder(1000, 0.05),
        new Ladder(5000, 0.10),
        new Ladder(null, 0.15),
    ]),
    new LadderParticipant('Platform', rate: 0.05),
], 'ladder');

// Priority participant (all three modes combined)
Splitter::split(1000, [
    PriorityParticipant::fixed('Gateway Fee', 5),
    PriorityParticipant::rate('Commission', 0.05),
    PriorityParticipant::residual('Merchant'),
], 'priority');
```

Custom Strategy
---------------

[](#custom-strategy)

Implement `StrategyInterface` and register it with `registerStrategy()`:

```
use Hejunjie\TradeSplitter\Contracts\StrategyInterface;
use Hejunjie\TradeSplitter\Models\SplitContext;
use Hejunjie\TradeSplitter\Models\Allocation;
use Hejunjie\TradeSplitter\Splitter;

class MyStrategy implements StrategyInterface
{
    public function split(SplitContext $context): array
    {
        // $context->total       — total amount to split
        // $context->participants — participant configuration array
        return [
            new Allocation('someone', $context->total, 1.0),
        ];
    }
}

Splitter::registerStrategy('my_strategy', MyStrategy::class);
$result = Splitter::split(1000, [], 'my_strategy');
```

Tip

Use `Splitter::availableStrategies()` to list all currently registered strategy names.

Core Concepts
-------------

[](#core-concepts)

ConceptDescription**Splitter**Main entry point — manages strategy registration and dispatching**StrategyInterface**Strategy contract; all strategies must implement `split(SplitContext): Allocation[]`**SplitContext**Split context containing `total` (amount to split) and `participants` (participant configuration)**Allocation**Split result with `name`, `amount`, and `ratio`; provides `toArray()` and getter methods**Participant**Abstract base class for typed participants; subclasses validate fields on construction and bridge to the strategy layer via `toArray()`Directory Structure
-------------------

[](#directory-structure)

```
src/
├── Contracts/
│   ├── Participant.php          # Abstract participant base class
│   └── StrategyInterface.php    # Strategy interface
├── Exceptions/
│   └── SplitException.php       # Unified exception class
├── Models/
│   ├── Allocation.php           # Split result object
│   └── SplitContext.php         # Split context
├── Participants/                # Typed participants (recommended)
│   ├── PercentageParticipant.php
│   ├── FixedParticipant.php
│   ├── WeightedParticipant.php
│   ├── EqualParticipant.php
│   ├── LadderParticipant.php
│   ├── Ladder.php               # Ladder rule value object
│   ├── RecursiveParticipant.php
│   ├── CeilingParticipant.php
│   └── PriorityParticipant.php
├── Strategies/                  # Strategy implementations
│   ├── PercentageStrategy.php
│   ├── FixedStrategy.php
│   ├── WeightedStrategy.php
│   ├── EqualStrategy.php
│   ├── LadderStrategy.php
│   ├── RecursiveStrategy.php
│   ├── CeilingStrategy.php
│   └── PriorityStrategy.php
└── Splitter.php                 # Split dispatcher

```

Run the Demo
------------

[](#run-the-demo)

```
php tests/demo.php
```

Motivation
----------

[](#motivation)

This component was born from frustration with rigid, hard-coded profit-sharing logic across various projects. The goal was a clear, pluggable, and reusable split component that could be integrated into any project — without reinventing the wheel.

If you've encountered unusual or complex split scenarios in your own work, feel free to collaborate and make this tool better.

Contributing
------------

[](#contributing)

Questions, suggestions, or bugs? PRs and Issues are always welcome.

If you find this project helpful, please give it a ⭐ Star — that's the biggest motivation to keep improving it!

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance94

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity46

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

Every ~275 days

Total

2

Last Release

33d ago

PHP version history (2 changes)v1.0.0PHP &gt;=8.1

v1.1.0PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/5b65d4b40ae456172fb38f63f84bf737ac88031484b1f228b1cc8d71baa80adf?d=identicon)[苏青安](/maintainers/%E8%8B%8F%E9%9D%92%E5%AE%89)

---

Top Contributors

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

---

Tags

composer-packagefinancial-calculationsmulti-level-commissionphpprofit-sharingrevenue-splitstrategy-pattern

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/hejunjie-trade-splitter/health.svg)

```
[![Health](https://phpackages.com/badges/hejunjie-trade-splitter/health.svg)](https://phpackages.com/packages/hejunjie-trade-splitter)
```

###  Alternatives

[amranidev/laracombee

Recommendation system for laravel

11539.8k1](/packages/amranidev-laracombee)

PHPackages © 2026

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