storefactory/zoho-subscription-bundle
StoreFactory's Zoho Subscription bundle brings Zoho Subscriptions billing into your PHP application, wrapping the API so you can manage plans, customers, and recurring payments directly from your own code. A lightweight integration layer for teams already running on Zoho's subscription platform.
Before writing a line of integration code, understand that this is a Symfony bundle, not a Laravel package. There is no auto-discovery, no ServiceProvider, and no Laravel-facing config. Composer will install it, but Laravel will not boot it for you. For a Laravel codebase the realistic path is to treat this repo as a reference implementation and use the underlying zoho-subscription-api client directly.
Minimal steps:
composer.json first. You need to know the PHP and Symfony version constraints and the Guzzle version it pins — this is the single most likely reason the install fails on a modern Laravel app.composer require storefactory/zoho-subscription-bundle in a throwaway directory (not your app) just to inspect the resolved vendor tree and the actual client class names.Configuration class — that is where the credential keys (client id, client secret, refresh token, org/account id) are defined and where you learn the exact config shape the client expects.composer require the underlying API client and wrap it yourself.First real use case: authenticate via Zoho OAuth and pull a customer or subscription by id. Get that one round-trip working in a tinker/php artisan tinker style REPL before building anything around it.
Laravel-native wrapping (recommended). Bind the underlying client as a singleton in a service provider, driven by config/services.php entries fed from .env:
// config/services.php
'zoho' => [
'client_id' => env('ZOHO_CLIENT_ID'),
'client_secret' => env('ZOHO_CLIENT_SECRET'),
'refresh_token' => env('ZOHO_REFRESH_TOKEN'),
'org_id' => env('ZOHO_ORG_ID'),
'dc' => env('ZOHO_DC', 'com'), // com, eu, in, com.au, jp
],
Then a small ZohoServiceProvider that constructs the client once and shares it. Inject it into jobs, listeners, and controllers rather than new-ing it per call.
Port the bundle's service definitions by hand. The bundle's value is its wiring, not its logic. Copy its service definition arguments into your own provider — that is the "integration tip" for a Laravel dev: you are translating XML/YAML service definitions into bindings.
Symfony-side pattern (if you actually are on Symfony). Register the bundle in bundles.php, then configure under the bundle's root key in config/packages/*.yaml with values from environment variables. Expose the client as a constructor dependency of your domain services; don't reach into the container.
Keep credentials out of code. Zoho refresh tokens are long-lived and effectively permanent secrets. Load them from env/secret manager, never commit them, and rotate client secrets on a schedule.
Webhook ingress. Zoho Subscriptions posts events (subscription created, cancelled, payment failed). Put the webhook route behind api middleware, exclude it from CSRF, verify the payload signature if the endpoint supports it, persist the raw event, then dispatch a queued job. Never do domain work synchronously in the webhook handler.
Repository/service layer. Wrap the client in your own ZohoSubscriptionService so your app never imports vendor classes. This makes the inevitable migration off this package a one-file change.
composer resolution failures. Use composer why-not to diagnose; do not force --ignore-platform-reqs in production..com, .eu, .in, .com.au, .jp). The client must target the correct API host or every call returns auth or 404 errors even with valid tokens. This is a common and confusing failure.INVALID_TOKEN / invalid_oauth errors. Cache the access token and refresh proactively.ZohoClientInterface) with only the methods you use. That lets you swap to Zoho's REST API or a maintained client without touching call sites — which, given the maintenance status here, you should plan for from day one.How can I help you explore Laravel packages today?