PHPackages                             laulamanapps/document-signer-symfony - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. laulamanapps/document-signer-symfony

ActiveSymfony-bundle[API Development](/categories/api)

laulamanapps/document-signer-symfony
====================================

Symfony bundle for the document signer SDK: configuration, a driver manager, and verified webhook routes for ValidSign and DocuSign.

v2.3.1(2d ago)00proprietaryPHP ^8.5

Since Jul 7Compare

[ Source](https://github.com/LauLamanApps/document-signer-symfony)[ Packagist](https://packagist.org/packages/laulamanapps/document-signer-symfony)[ RSS](/packages/laulamanapps-document-signer-symfony/feed)WikiDiscussions Synced today

READMEChangelogDependencies (13)Versions (3)Used By (0)

Symfony bundle for the document signer SDK
==========================================

[](#symfony-bundle-for-the-document-signer-sdk)

A Symfony bundle around the [Document Signer SDK](https://github.com/LauLamanApps/document-signer-sdk): configuration, a driver manager, and verified webhook routes for ValidSign and DocuSign. It mirrors the [Laravel package](https://github.com/LauLamanApps/document-signer-laravel) — same providers-list config, same webhook event.

Contents
--------

[](#contents)

- [Install](#install)
- [Configure](#configure)
- [Sending an envelope](#sending-an-envelope)
- [Recipient override (dev / staging)](#recipient-override-dev--staging)
    - [Strategy](#strategy)
    - [Scope](#scope)
- [PDF renderer](#pdf-renderer)
- [Webhooks](#webhooks)
    - [Translated labels](#translated-labels)
- [Custom providers](#custom-providers)
- [Requirements](#requirements)

Install
-------

[](#install)

```
composer require laulamanapps/document-signer-symfony
```

Add at least one provider package (both are optional):

```
composer require laulamanapps/document-signer-validsign   # for the validsign provider
composer require laulamanapps/document-signer-docusign    # for the docusign provider
composer require spatie/browsershot                       # default PDF renderer
```

Register the bundle (Symfony Flex does this automatically):

```
// config/bundles.php
return [
    // ...
    LauLamanApps\DocumentSigner\Symfony\DocumentSignerBundle::class => ['all' => true],
];
```

Configure
---------

[](#configure)

```
# config/packages/document_signer.yaml
document_signer:
    # Provider NAME to use when none is given. Omit to auto-select the sole
    # configured provider; required when more than one is configured.
    default: '%env(default::DOCUMENT_SIGNER_DRIVER)%'

    providers:
        - class: 'LauLamanApps\DocumentSigner\ValidSign\ValidSignProvider'
          config:
              api_key: '%env(VALIDSIGN_API_KEY)%'
              base_url: '%env(default:https://my.validsign.nl/api:VALIDSIGN_BASE_URL)%'
          webhook:
              callback_secret: '%env(VALIDSIGN_CALLBACK_SECRET)%'

        - class: 'LauLamanApps\DocumentSigner\DocuSign\DocuSignProvider'
          config:
              integration_key: '%env(DOCUSIGN_INTEGRATION_KEY)%'
              user_id: '%env(DOCUSIGN_USER_ID)%'
              account_id: '%env(DOCUSIGN_ACCOUNT_ID)%'
              private_key_path: '%env(DOCUSIGN_PRIVATE_KEY_PATH)%'
              # Optional: nudge every field's vertical placement (px, + = down).
              # The driver already lands fields on their placeholder; default 0.
              anchor_y_offset_pixels: '%env(int:default::DOCUSIGN_ANCHOR_Y_OFFSET_PIXELS)%'
          webhook:
              hmac_secret: '%env(DOCUSIGN_CONNECT_HMAC_SECRET)%'
```

Each entry co-locates the provider `class`, its `config`, and its `webhook`secret. A provider's short name — used by `default` and the webhook URL — is the `NAME` constant on its class (`validsign`, `docusign`).

Sending an envelope
-------------------

[](#sending-an-envelope)

Type-hint the manager:

```
use LauLamanApps\DocumentSigner\Symfony\DocumentSignerManager;
use LauLamanApps\DocumentSigner\Sdk\Document\Document;
use LauLamanApps\DocumentSigner\Sdk\Envelope\Envelope;
use LauLamanApps\DocumentSigner\Sdk\Signer\Signer;

public function __construct(private DocumentSignerManager $signer) {}

public function send(): void
{
    $receipt = $this->signer->send(new Envelope(
        name:         'NDA',
        documents:    [new Document(id: 'nda', name: 'NDA', html: '{[signature:party:sig]}')],
        signers:      [new Signer(key: 'party', name: 'Jane Doe', email: 'jane@example.com')],
        emailSubject: 'Please sign the NDA',
    ));

    // Switch provider at runtime:
    $this->signer->driver('docusign')->getStatus($receipt->providerEnvelopeId);
}
```

Recipient override (dev / staging)
----------------------------------

[](#recipient-override-dev--staging)

Seeded and development data is full of reserved domains — `example.com`, `*.test`, and friends — that providers like ValidSign reject outright, so an otherwise-valid envelope fails the moment you try to send it. The recipient override rewrites every signer's email **before** the envelope reaches the provider, redirecting those addresses to a real inbox you control instead.

It is off by default and adds zero overhead when disabled — the real provider is used directly, no wrapper installed. **Keep it off in production** (bind it to your environment, e.g. only in `config/packages/dev/`).

```
# config/packages/dev/document_signer.yaml
document_signer:
    recipient_override:
        enabled: true
        to: 'you@yourdomain.test'
```

That's the whole setup for the default (`catch_all`) strategy. It applies on every send path — `$manager->send()`, `$manager->driver('validsign')->send()`, and a manually resolved instance alike.

### Strategy

[](#strategy)

`strategy` chooses *how* each address is rewritten. Given `to: dev@you.test`(or `domain: you.test`):

Strategy`alice@example.com` becomesNotes`catch_all` *(default)*`dev+alice=example.com@you.test`Everyone lands in one inbox, but the original address is folded into a `+tag` so each signer stays a **distinct** recipient. ValidSign rejects duplicate recipients on one package, so this is the safe default for multi-signer envelopes. Requires `to`.`domain``alice@you.test`Keeps the local part, swaps only the domain. Recipients stay unique; you need a catch-all inbox on that domain to actually receive the mail. Requires `domain`.`redirect``dev@you.test`Sends everyone to `to` verbatim. Simplest, but multiple signers on one envelope collapse to the same address (a provider may reject that) — use for single-signer flows only. Requires `to`.A misconfigured-but-enabled override (e.g. `catch_all` with no `to`) throws a clear `InvalidArgumentException` on the first send, rather than silently misrouting mail.

### Scope

[](#scope)

By default (`only_domains` empty) **every** signer address is rewritten once the override is enabled — the `enabled` flag is the only guard, so keep it off in production. Add entries only to *narrow* the rewrite to specific domains and let all others pass through untouched:

```
document_signer:
    recipient_override:
        enabled: true
        strategy: domain
        domain: 'you.test'
        only_domains:           # rewrite only seeded/test data, leave the rest
            - 'example.com'
            - '*.test'
            - '*.local'
```

Entries match case-insensitively; a `*.` prefix does a suffix match (`*.test`matches `foo.test`).

PDF renderer
------------

[](#pdf-renderer)

The manager wires the SDK's `BrowsershotPdfRenderer` into every provider by default. To use a different engine, register your own `PdfRenderer` service and alias the interface to it — the bundle only sets the default alias if you haven't:

```
services:
    LauLamanApps\DocumentSigner\Sdk\Pdf\PdfRenderer:
        alias: App\Pdf\GotenbergRenderer
```

Webhooks
--------

[](#webhooks)

Import the bundle's webhook route with the prefix you want:

```
# config/routes/document_signer.yaml
document_signer:
    resource: '@DocumentSignerBundle/config/routes.php'
    prefix: /document-signer/webhooks
```

That exposes `POST /document-signer/webhooks/{provider}`. The endpoint verifies the shared-secret signature (set the `webhook` secret to enable it) and dispatches a `DocumentSignerWebhookReceived` event; unverified requests get a 401. Listen for it:

```
use LauLamanApps\DocumentSigner\Symfony\Event\DocumentSignerWebhookReceived;
use LauLamanApps\DocumentSigner\ValidSign\ValidSignProvider;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;

#[AsEventListener]
final class HandleSignerWebhook
{
    public function __invoke(DocumentSignerWebhookReceived $event): void
    {
        // Originating provider is always present, even when $event->event is null:
        if ($event->provider === ValidSignProvider::class) { /* ... */ }

        match (true) {
            $event->event?->isCompleted() => $this->onCompleted($event->payload),
            $event->event?->isDeclined()  => $this->onDeclined($event->payload),
            default                       => null,
        };
    }
}
```

`$event->event` is a `WebhookEvent` enum (or `null` for a provider without one). Its short name is `$event->provider::NAME`, and the raw body is on `$event->payload` / `$event->request`.

### Translated labels

[](#translated-labels)

```
use LauLamanApps\DocumentSigner\Symfony\Translation\EventTranslator;

public function __invoke(DocumentSignerWebhookReceived $event, EventTranslator $labels): void
{
    if ($event->event !== null) {
        // "Package complete" (en), "Pakket voltooid" (nl)
        $label = $labels->label($event->event, $event->provider);
    }
}
```

The bundle ships English and Dutch labels under the `document-signer`translation domain (`translations/document-signer.{en,nl}.php`).

Custom providers
----------------

[](#custom-providers)

Any `SignatureProvider` works. Register it as a service (it's autoconfigured with the `document_signer.provider` tag), and add a `providers` entry pointing at its class:

```
document_signer:
    providers:
        - class: 'App\Signing\AcmeSignProvider'
          webhook:
              secret: '%env(ACME_WEBHOOK_SECRET)%'
```

- Declare a `public const string NAME` on the provider — its short name.
- Wire the provider's own dependencies (credentials, the `PdfRenderer`) through normal Symfony service config.
- Implement `Webhook\ProvidesWebhook` to get an auto-verified webhook route.

Requirements
------------

[](#requirements)

- PHP 8.5
- Symfony 6.4, 7.x or 8.x
- `laulamanapps/document-signer-sdk` ^2.0
- `laulamanapps/document-signer-validsign` *or* `-docusign` (each optional)
- Node.js + Puppeteer (for the default Browsershot renderer)

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance99

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

2

Last Release

2d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/930ca3b1756d00a63c8ff3363e81f16f961cf3ef79ff0c9d362670c36d97e456?d=identicon)[LauLaman](/maintainers/LauLaman)

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/laulamanapps-document-signer-symfony/health.svg)

```
[![Health](https://phpackages.com/badges/laulamanapps-document-signer-symfony/health.svg)](https://phpackages.com/packages/laulamanapps-document-signer-symfony)
```

###  Alternatives

[symfony/framework-bundle

Provides a tight integration between Symfony components and the Symfony full-stack framework

3.6k251.7M11.7k](/packages/symfony-framework-bundle)[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.9M389](/packages/easycorp-easyadmin-bundle)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.5k5.9M750](/packages/sylius-sylius)[symfony/security-bundle

Provides a tight integration of the Security component into the Symfony full-stack framework

2.5k185.6M2.4k](/packages/symfony-security-bundle)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M582](/packages/shopware-core)[contao-community-alliance/dc-general

Universal data container for Contao

1680.8k92](/packages/contao-community-alliance-dc-general)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
