la-souris/document-signer-symfony
Symfony bundle integrating the la-souris document signer, so you can digitally sign PDFs and verify signatures directly from your Symfony services. Configure certificates and signing profiles in bundle config, then sign or validate documents in a few lines of PHP.
This is a Symfony bundle, not a Laravel package — treat the release notes as a reference implementation rather than something you composer require and go. The first decision is whether you need the bundle at all: if your app is Laravel, you almost certainly want the SDK plus the individual signature-verification primitives, and you'll port the bundle's wiring yourself.
Minimal steps:
--ignore-platform-reqs.composer require la-souris/document-signer-sdk plus at least one provider package (la-souris/document-signer-validsign and/or la-souris/document-signer-docusign). The bundle/SDK alone resolves no driver.config/document-signer.php mirroring the bundle's provider list shape: an array of ['class' => ..., 'config' => [...]] entries.POST /webhooks/document-signer/{provider} route, exempt from CSRF and auth middleware, and verify the signature before doing anything else.Where to look first: DocumentSignerManager (how drivers are resolved and what config each provider receives), the two signature verifier classes (what request shape they expect — this determines whether you can reuse them from Laravel at all), and the bundle's routing definition for the webhook endpoints so you can replicate the paths and methods exactly.
First use case worth building: send one envelope to yourself, receive the completion webhook, verify the signature, dispatch an event, and mark the document signed. That exercises config, driver resolution, webhook verification, and event translation in a single pass.
Driver manager as a registry. The bundle's DocumentSignerManager is a lazy resolver keyed by provider. In Laravel, bind it as a singleton in a service provider and read the provider list from config so you can add or swap providers per environment without touching code:
$this->app->singleton(DocumentSignerManager::class, fn ($app) => new DocumentSignerManager(
array_map(fn ($p) => $app->make($p['class']), config('document-signer.providers'))
));
Keep the class + config split from the YAML — it maps directly onto a Laravel config array.
Config translation. Move the YAML example into config/document-signer.php with env() calls, including the offset:
'anchor_y_offset_pixels' => (int) env('DOCUSIGN_ANCHOR_Y_OFFSET_PIXELS', 0),
Keep it nested under the provider entry, not at the top level — it's per-provider config, and putting it in the wrong place silently does nothing.
Webhook endpoint per provider. One route, provider as a segment, or two explicit routes. The flow: verify signature against the raw body → normalize the provider payload → dispatch your event → return 2xx immediately. Do the actual work (fetch envelope, update records, send mail) in a queued listener so a slow downstream doesn't cause provider retries.
Event translation. The bundle exposes DocumentSignerWebhookReceived plus a translation layer producing human-readable event names. Mirror that: persist the raw provider event name alongside the translated label, dispatch a Laravel event, and branch business logic on the raw value. Use the friendly name only for logs, notifications, and admin UIs.
Recipient overrides as a decorator. OverridingSignatureProvider + RecipientRewriter is a decorator around the real provider. Register the override in the container only when ! app()->isProduction(), wrapping the real provider instance. That keeps the redirect logic entirely out of your sending code and guarantees production cannot reach it.
Anchor offset per template, not globally. anchor_y_offset_pixels exists because DocuSign anchor strings render slightly off once converted to PDF coordinates. Positive moves fields down the page, negative up, default 0. Send one envelope with the offset, inspect the signed PDF visually, then record the value against that template. A single global value will be wrong for the next template.
Testing. Bind a fake provider in the container for send-path tests. For the webhook, assert 401 on a bad signature and 200 + event dispatch on a good one, using a captured payload fixture per provider.
--ignore-platform-reqs in production is how you get a runtime fatal in a webhook handler.Request/HeaderBag. Read the source before assuming reuse. In Laravel you either adapt Illuminate\Http\Request or reimplement verification from the SDK primitives: raw body bytes + the provider-specific signature header.$request->getContent(). Any middleware that decodes and re-encodes the JSON changes the bytes and breaks HMAC verification. Same trap applies if you type-hint a form request or use $request->all().bootstrap/app.php (validateCsrfTokens(except: [...])) plus keeping them outside your auth middleware group. Provider egress has no session and no token.How can I help you explore Laravel packages today?