tourze/payment-contracts
tourze/payment-contracts provides shared PHP interfaces and contracts for building payment integrations within the Tourze ecosystem, giving developers a consistent foundation for gateways, transactions, and payment flows across projects.
tourze/payment-contracts is an interface/contract package: it ships abstractions, not a working payment gateway. The 1.0.0 tag is the first stable release, which mainly signals that the contract surface is intended to be frozen — treat it as "the interfaces you code against," not "a payment library you call."
Minimal steps:
composer require tourze/payment-contracts (it should be a normal require dependency, not require-dev, since your implementations will type-hint against it).vendor/tourze/payment-contracts/src/ is the documentation. Use composer show -a tourze/payment-contracts and then open every interface file — the whole package is usually a handful of small interfaces.// app/Payments/StripeGateway.php
final class StripeGateway implements \Tourze\PaymentContracts\PaymentInterface
{
public function __construct(private \Stripe\Client $client) {}
// implement each method defined by the interface
}
// app/Providers/PaymentServiceProvider.php
public function register(): void
{
$this->app->bind(
\Tourze\PaymentContracts\PaymentInterface::class,
\App\Payments\StripeGateway::class,
);
}
Resolve via the container (app(PaymentInterface::class) or constructor injection) rather than instantiating gateways directly — that is the entire point of depending on a contracts package.
Adapter-per-provider, resolve-by-interface. Bind one implementation per provider and select between them with a context-aware binding or a small gateway registry keyed by a payment_method / channel string. Keep the registry in your own code; contracts packages generally do not ship a manager.
Wrap the provider SDK, do not leak it. The contract exists so controllers, jobs, and listeners never import Stripe/Alipay classes. Confine SDK exceptions to your adapter and translate them into the package's own exception types (if it defines any) or into domain exceptions of your own. This is what makes swapping providers cheap later.
Value objects at the boundary. Contracts packages usually define request/response DTOs. Build them in a form request or a small application service, not inside a controller closure. If the DTOs are immutable, use named constructors or fromArray() helpers in your own layer rather than scattering new calls.
Test doubles are the main day-to-day win. Because you depend on an interface, every consumer becomes trivially testable:
$this->mock(PaymentInterface::class, function ($mock) {
$mock->shouldReceive('charge')->once()->andReturn(/* response DTO */);
});
Write one "contract test" (a shared abstract test case) that every adapter must pass — success, decline, timeout, refund, idempotent retry. That is the single highest-value artefact when working with a contracts-only dependency.
Integration tips. Pair the contracts with a concrete driver package if one exists in the same organisation's ecosystem, otherwise expect to own all adapters. Hook the contract into Laravel's payment lifecycle at the service layer, not in models; fire domain events (PaymentSucceeded, PaymentFailed) from your adapter's callers so the interface stays free of framework concerns.
BindingResolutionException deep in a worker.composer show -a, or fetch the tag) before upgrading past 1.0.x; interface packages break consumers silently at the type level rather than at runtime.^1.0 and let the constraints do the work.Tourze\... vs a PSR-4 quirk) and whether the package declares illuminate/support or stays framework-agnostic. Framework-agnostic contracts need manual service-provider wiring and are not auto-discovered — check for a extra.laravel.providers block in composer.json.raw property or a log entry) so you can debug reconciliation without re-querying the provider.match.How can I help you explore Laravel packages today?