Help us improve
Share bugs, ideas, or general feedback.
From acc
Generates PHP 8.4 idempotent consumer components for message deduplication, including IdempotencyKey, Database/Redis stores, middleware, and unit tests.
npx claudepluginhub dykyi-roman/awesome-claude-code --plugin accHow this skill is triggered — by the user, by Claude, or both
Slash command
/acc:create-idempotent-consumerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Creates Idempotent Consumer pattern infrastructure for exactly-once message processing guarantees.
Generates PHP 8.4 idempotency handler: PSR-15 middleware with Redis deduplication, IdempotencyKey value object, storage interface, exceptions, and unit tests for safe API retries.
Handles duplicate message delivery safely using idempotency keys and deduplication stores. Useful for at-least-once message delivery systems, payment processing, and retry logic.
Provides patterns for designing idempotent APIs with keys to handle retries safely, prevent duplicates, and ensure at-most-once semantics in payments/orders.
Share bugs, ideas, or general feedback.
Creates Idempotent Consumer pattern infrastructure for exactly-once message processing guarantees.
| Scenario | Example |
|---|---|
| Payment processing | Prevent double charges on message replay |
| Inventory updates | Avoid duplicate stock deductions |
| Event handlers | Process domain events exactly once |
| Webhook processing | Handle duplicate webhook deliveries |
| Message queue consumers | Deduplicate redelivered messages |
Determine:
Domain Layer (src/Domain/Shared/Idempotency/)
IdempotencyKey.php — Key value objectProcessingResult.php — Result value object with statusProcessingStatus.php — Status enum (Processed/Duplicate/Failed)Application Layer (src/Application/Shared/Idempotency/)
IdempotencyStoreInterface.php — Storage portIdempotentConsumerMiddleware.php — Middleware wrapperInfrastructure Layer (src/Infrastructure/Idempotency/)
DatabaseIdempotencyStore.php — PDO implementationRedisIdempotencyStore.php — Redis implementationTests
IdempotencyKeyTest.phpProcessingResultTest.phpIdempotentConsumerMiddlewareTest.phpDatabaseIdempotencyStoreTest.php| Layer | Path |
|---|---|
| Domain Types | src/Domain/Shared/Idempotency/ |
| Application Port | src/Application/Shared/Idempotency/ |
| Infrastructure | src/Infrastructure/Idempotency/ |
| Unit Tests | tests/Unit/{Layer}/{Path}/ |
| Component | Pattern | Example |
|---|---|---|
| Key VO | IdempotencyKey | IdempotencyKey |
| Result VO | ProcessingResult | ProcessingResult |
| Status Enum | ProcessingStatus | ProcessingStatus |
| Store Interface | IdempotencyStoreInterface | IdempotencyStoreInterface |
| DB Store | DatabaseIdempotencyStore | DatabaseIdempotencyStore |
| Redis Store | RedisIdempotencyStore | RedisIdempotencyStore |
| Middleware | IdempotentConsumerMiddleware | IdempotentConsumerMiddleware |
| Test | {ClassName}Test | IdempotencyKeyTest |
final readonly class IdempotencyKey
{
public function __construct(
public string $messageId,
public string $handlerName,
) {}
public static function fromMessage(string $messageId, string $handlerName): self;
public function toString(): string;
public function equals(self $other): bool;
}
interface IdempotencyStoreInterface
{
public function has(IdempotencyKey $key): bool;
public function mark(IdempotencyKey $key, \DateTimeImmutable $expiresAt): void;
public function remove(IdempotencyKey $key): void;
}
$middleware = new IdempotentConsumerMiddleware($store);
$result = $middleware->process(
key: IdempotencyKey::fromMessage($message->id, 'handle_payment'),
handler: fn() => $paymentHandler->handle($message),
ttl: new \DateTimeImmutable('+7 days')
);
match ($result->status) {
ProcessingStatus::Processed => $logger->info('Processed'),
ProcessingStatus::Duplicate => $logger->info('Skipped duplicate'),
ProcessingStatus::Failed => $logger->error('Failed', ['error' => $result->error]),
};
CREATE TABLE idempotency_keys (
key VARCHAR(255) PRIMARY KEY,
handler_name VARCHAR(255) NOT NULL,
processed_at TIMESTAMP(6) NOT NULL,
expires_at TIMESTAMP(6) NOT NULL
);
CREATE INDEX idx_idempotency_expires ON idempotency_keys (expires_at);
For complete PHP templates and test examples, see:
references/templates.md — All component templatesreferences/examples.md — Payment handler example and unit tests