Search by

tilttek / exchange

phil

Exchanges for gamification: rewards bought with points in two steps, a reservation that spends the points and holds the stock then a confirmation or a cancellation; points given between members; items traded by offers, counter-offers and acceptance in one transaction; guards against self-exchange, n

0.1.0-alpha 2026-11-11 14:25 UTC

This package is auto-updated.

Last update: 2026-11-11 14:51:35 UTC


README

Rewards your members buy with points, points they give each other, and items they trade, safely and once.

tilttek/exchange handles the three exchanges of a gamified site. A member exchanges points for a reward of the catalogue — a discount, a digital item, an access, a seat at an event — in two steps: a reservation spends the points on the ledger of tilttek/points and holds the unit, then the application confirms once it has delivered, or cancels and the points come back; a reservation left unconfirmed expires. A reward has its price in a currency of points, its stock, its period of availability, a limit per member and conditions, like a badge or a level, that the application checks. Members may give each other points, and trade items of their collections — virtual cards, the dice of a set — by offers, counter-offers and acceptance: the items offered are held from the offer on, and on acceptance both sides change hands in one transaction, or nothing moves. Guards keep it fair: no exchange with oneself, a delay for a new account, an allowance per period, and a journal of transfers the application examines. Every step is safe to run twice, and points are never turned into money.

It is for an application that lets its members spend and share what they earn — a store's loyalty programme, a community site, a game site — and wants exchanges that never lose a point or an item. tilttek/exchange-bundle integrates it with Symfony and keeps everything in Doctrine DBAL; an application without Symfony takes the library alone and writes its own stores, or uses the ones in memory.

It is a plain PHP library whose dependencies are tilttek/points, the ledger that rewards and transfers move points on, phil/resource, the opaque references of members and items, and psr/clock. Items live in the application's collections, reached through an Inventory it implements; the conditions of the rewards and the joining dates of the members come from the application too: the library reads no account, no badge, no level.

The design is small: Redemptions reserves, confirms, cancels and expires the Reservations of the Rewards of a Catalog; Transfers gives points within Guards and writes the Transfers to a TransferLog; Trades opens, accepts, declines, withdraws, counters and expires Offers kept by an OfferStore; an Announcer is told each event; ExchangeErasure erases a member.

Status

Part State
Reward, Catalog, InMemoryCatalog, Requirements: prices, stock, availability, limits and conditions Implemented and tested
Redemptions and ReservationStore: reservations in two steps, stock and limits checked as they are written, expiry Implemented and tested
Transfers and TransferLog: points given between members, the journal Implemented and tested
Trades, OfferStore and Inventory: offers holding their items, acceptance in one transaction, counter-offers, expiry Implemented and tested
Guards and Allowance: no self-exchange, delay for new accounts, allowance per period Implemented and tested
Events through an Announcer Implemented and tested
ExchangeErasure: the erasure of a member Implemented and tested
Exceptions under one ExchangeException interface Implemented and tested

Documentation

Document What it covers
Installation Requirements and installing.
Rewards The catalogue, reservations, confirmations, cancellations, expiry, stock, limits and conditions.
Transfers Points given between members and the journal.
Trades Offers, counter-offers, acceptance, the held items and the inventory.
Guards Self-exchange, new accounts, allowances.
Events What the announcer is told, and when.
Erasure What erasing a member removes and what it keeps.
Writing a store The contracts of the stores, the transaction and the inventory.
Extension points What an application gives the library: stores, the transaction, the inventory, the catalogue, conditions, the announcer, the clock.
Exceptions ExchangeException and its exceptions, with their messages.
Testing The library's tests and mutation bar, and how to test code that exchanges.
Reference Every type of the library, with its role.

Quick start

composer require tilttek/exchange
use Phil\Resource\ResourceReference;
use Symfony\Component\Clock\NativeClock;
use Tilttek\Exchange\Reservation\InMemoryReservationStore;
use Tilttek\Exchange\Reservation\Redemptions;
use Tilttek\Exchange\Reward\InMemoryCatalog;
use Tilttek\Exchange\Reward\Reward;
use Tilttek\Points\Currencies;
use Tilttek\Points\Currency;
use Tilttek\Points\Ledger\InMemoryLedger;
use Tilttek\Points\Points;

$clock = new NativeClock();
$currencies = new Currencies(new Currency('points'));
$points = new Points(new InMemoryLedger(), $clock, $currencies);
$redemptions = new Redemptions(new InMemoryCatalog(new Reward('discount-10', 100, stock: 50)), new InMemoryReservationStore(), $points, $currencies, $clock);

$member = ResourceReference::of('member', '44');
$points->grant($member, 150, 'welcome:44');

$redemptions->reserve($member, 'discount-10', 'order:2026-0193:reward'); // 100 points spent, one unit held
$redemptions->confirm('order:2026-0193:reward');                         // delivered: the points stay spent

Requirements

PHP 8.5 or later.

Contributing

Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.

License

MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Tilttek project. See LICENSE.