Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Document Signer Symfony Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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:

  1. Check your PHP version. The release requires PHP 8.5+, which is ahead of most Laravel applications. If you're on 8.2–8.4, Composer will refuse the install — do not paper over it with --ignore-platform-reqs.
  2. 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.
  3. Create config/document-signer.php mirroring the bundle's provider list shape: an array of ['class' => ..., 'config' => [...]] entries.
  4. Register a singleton driver manager in a service provider, resolving the configured provider classes from container bindings.
  5. Define a 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.

Implementation Patterns

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.

Gotchas and Tips

  • The Symfony bundle's DI extension, config tree, and routes do not exist in Laravel. No auto-discovery. You are porting: config file, service provider bindings, route registration. Budget for it explicitly.
  • PHP 8.5+ is a hard floor. Sail, Herd, and most base images may lag. Verify your runtime before planning the upgrade; --ignore-platform-reqs in production is how you get a runtime fatal in a webhook handler.
  • The bundle resolves no driver on its own. You must install a provider package. A manager with an empty provider list throws at send time, not at boot — add a config validation check.
  • The verifiers likely take Symfony's 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.
  • Always verify against $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().
  • DocuSign and ValidSign sign different things with different headers. Do not share one verifier, and do not share one secret config key.
  • Exempt webhook routes from CSRF and auth. In Laravel 11+ that's bootstrap/app.php (validateCsrfTokens(except: [...])) plus keeping them outside your auth middleware group. Provider egress has no session and no token.
  • Respond fast. Non-2xx triggers provider retries; slow 200s trigger duplicate deliveries. Make handlers idempotent on the provider event ID.
  • Recipient overrides are a production hazard. Guard with both an environment check and an explicit config flag, and log at warning level on every send while active — otherwise a staging deploy pointed at prod credentials emails real signers.
  • Event translation is display-only. Persist the raw name; when a webhook lands in the wrong branch, the translated label tells you nothing.
  • Debugging verification failures: log the signature header's hash (not the secret), the body length, and the provider name before returning 401. Nine times out of ten it's a re-encoded body or the wrong provider's secret.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankkessler/guzzle-oauth2-middleware
delatbabel/site-config
delatbabel/applog
eusonlito/laravel-gettext
henzeb/housepedia-intelligence-laravel
aliqsm/laravel-authkit
itsjustvita/laravel-wp-migrator
uslanozan/laravel-cache-contract
adilazhari/laravel-trace
ulsyairil/ai-local-guardrails
dzwallet/laravel-sdk
glc36/maxloop-kernel
ikhbarnuridev/filament-mega-menu
mydnic/volet-chatbot
enadstack/laravel-localization
mydnic/volet-feature-board-filament-plugin
wmde/email-address
refinery29/test-util
dees040/laravel-api-responses
teamzac/geocoder