hejunjie/promotion-engine

一个 灵活可扩展 的 PHP 促销策略引擎,让购物车的各种复杂促销逻辑(满减、打折、阶梯优惠、会员折扣…)实现更优雅、可维护 | A flexible and scalable PHP promotional strategy engine that enables various complex promotional logics (money off, discounts, tiered offers, member discounts, etc.) in shopping carts to be implemented more elegantly and maintainably.

v1.0.0 2025-07-29 09:54 UTC

This package is auto-updated.

Last update: 2025-07-29 10:14:08 UTC


README

English简体中文

A flexible and scalable PHP promotional strategy engine that enables various complex promotional logics (money off, discounts, tiered offers, member discounts, etc.) in shopping carts to be implemented more elegantly and maintainably.

This project has been parsed by Zread. If you need a quick overview of the project, you can click here to view it:Understand this project

📦 Installation method

Install this library using Composer:

composer require hejunjie/promotion-engine

🚀 Usage

use Hejunjie\PromotionEngine\Models\Cart;
use Hejunjie\PromotionEngine\Models\User;
use Hejunjie\PromotionEngine\PromotionEngine;
use Hejunjie\PromotionEngine\Rules;

// Create user (VIP)
$user = new User(vip: true);

// Create a shopping cart (with 10 items, and the total price can cover various rules)
$cart = new Cart();
$cart->addItem('商品A', 50, 1, ['snacks']);
$cart->addItem('商品B', 60, 1, ['clothes']);
$cart->addItem('商品C', 40, 1, ['clothes']);
$cart->addItem('商品D', 100, 1, ['promo']);
$cart->addItem('商品E', 30, 3, ['snacks']);
$cart->addItem('商品F', 200, 1, ['electronics']);

// Initialize the promotion engine
$engine = new PromotionEngine();

// Register all rules
$engine->addRule(new Rules\FullDiscountRule(200, 0.9));  // 10% off for purchases over 200
$engine->addRule(new Rules\FullQuantityReductionRule(3, 20));  // Buy 3 or more and get 20% off
$engine->addRule(new Rules\NthItemDiscountRule(3, 0.5));  // Third item is 50% off
$engine->addRule(new Rules\TieredDiscountRule([
    100 => 0.95,
    300 => 0.9,
    500 => 0.85
]));  // Ladder full fold
$engine->addRule(new Rules\VipDiscountRule(0.95));  // 5% discount for VIP
$engine->addRule(new Rules\FullQuantityDiscountRule(5, 0.9));  // 10% off for purchases of 5 items or more
$engine->addRule(new Rules\FullReductionRule(100, 20));  // 20 off for purchases over 100
$engine->addRule(new Rules\NthItemReductionRule(3, 9.9));  // The third item is priced at 9.9 yuan
$engine->addRule(new Rules\TieredReductionRule([
    100 => 10,
    200 => 30,
    500 => 80
]));  // Ladder full reduction
$engine->addRule(new Rules\VipReductionRule(5));  // VIP: 5 yuan off

// Perform calculation
$result = $engine->calculate($cart, $user);

// Print result (for manual verification)
echo "\n=== test result ===\n";
echo "original price: ¥{$result['original']}\n";
echo "discount: -¥{$result['discount']}\n";
echo "final: ¥{$result['final']}\n";
echo "Discount Details:\n";
foreach ($result['details'] as $detail) {
    echo "- {$detail}\n";
}

// 输出内容:
// === test result ===
// original price: ¥540
// discount: -¥391.1
// final: ¥148.9
// Discount Details:
// - 指定商品满200元打0.9折 (-¥54)
// - 指定商品满3件减20 (-¥20)
// - 指定商品第3件打5折 (-¥20)
// - 指定商品满500元打8.5折 (-¥81)
// - VIP 0.95 折 (-¥27)
// - 指定商品满5件打9折 (-¥54)
// - 指定商品满100减20 (-¥20)
// - 指定商品第3件特价9.9元 (-¥30.1)
// - 指定商品满500减80 (-¥80)
// - VIP 立减 5 元 (-¥5)

🛠️ Rule type (out-of-box)

方法 说明
FullDiscountRule For purchases over X yuan, receive a Z% discount (Example: For purchases over 200 yuan, receive a 20% discount)
FullQuantityReductionRule Buy X items and get Y yuan off (Example: Buy 3 items and get 20 yuan off)
NthItemDiscountRule The Nth item is discounted by Z (Example: the 3rd item is discounted by 50%)
TieredDiscountRule Enjoy a Z% discount when your purchase reaches X yuan (Example: 10% discount when your purchase reaches 100 yuan, 20% discount when your purchase reaches 200 yuan, 30% discount when your purchase reaches 500 yuan (whichever is the highest))
VipDiscountRule VIP users enjoy discounts (Example: 10% off for VIP)
FullQuantityDiscountRule Buy X items and get Z% off (Example: Buy 3 items and get 10% off)
FullReductionRule For purchases over X yuan, get Y yuan off (Example: for purchases over 100 yuan, get 20 yuan off)"
NthItemReductionRule The Nth item is on special offer (Example: the 3rd item is priced at 9.9 yuan)
TieredReductionRule Step discount: subtract Y yuan when the purchase reaches X yuan (Example: subtract 10 yuan when the purchase reaches 100 yuan, subtract 30 yuan when the purchase reaches 200 yuan, and subtract 80 yuan when the purchase reaches 500 yuan (whichever is the highest level))
VipReductionRule VIP users enjoy immediate discounts (Example: a discount of 5 yuan when ordering as a VIP)

🎯 Purpose & Original Intent

Why write this package?

In e-commerce, B2C, and retail systems, promotion logic is often scattered across Controller, Service, and if-else statements. Once there are numerous rules, maintenance becomes a nightmare.

✅ I hope to solve this problem using a 'rule engine + extensible policy class':

  • Write each discount as a separate class → clear, easy to test, reusable
  • Supports multiple calculation modesoriginal (based on original price) / stack (discount on discount)
  • Easy to extend → Adding new rules only requires extends PromotionRule

👉 In this way, for future integrations of new activities / membership levels / specific product category discounts, it will only be necessary to create a new Rule, without the need for major code modifications.

🤝 Welcome PR & contributions

📢 All pull requests (PRs) are welcome:

  • New promotional rules (such as coupons, buy-one-get-one-free, flash sales, etc.)
  • Optimize core logic (such as more flexible rule priorities, discount stacking strategies)
  • Unit testing (PHPUnit) or performance optimization

👉 Fork this repository and submit a Pull Request, or create an issue to exchange ideas.