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

Pingen Bundle Laravel Package

ticketpark/pingen-bundle

Symfony bundle that integrates the Pingen API for sending physical letters and postcards directly from your application. Configure your credentials, then generate, upload, and dispatch mail through simple services, ideal for automating transactional or bulk postal correspondence.

View on GitHub
Deep Wiki
Context7

Getting Started

This bundle is a Symfony 2 wrapper around the ticketpark/pingen PHP client, which talks to pingen.com — a print-and-mail service that turns a PDF plus an address into a physically posted letter. The bundle's only real job is registering that client in the DI container from a small config block, so treat it as a service-provider shim, not a framework integration.

Minimal setup in a Symfony 2 project:

composer require ticketpark/pingen-bundle
// app/AppKernel.php
public function registerBundles()
{
    $bundles = [
        // ...
        new Ticketpark\PingenBundle\TicketparkPingenBundle(),
    ];
}
# app/config/config.yml
ticketpark_pingen:
    client_id:     "%pingen_client_id%"
    client_secret: "%pingen_client_secret%"
# app/config/parameters.yml — keep credentials out of VCS
parameters:
    pingen_client_id:     "%env(PINGEN_CLIENT_ID)%"
    pingen_client_secret: "%env(PINGEN_CLIENT_SECRET)%"

Do not trust those key names blindly — the bundle predates standardised config and was iterated on. Open DependencyInjection/Configuration.php in the installed package first; that file is the authoritative list of supported keys, and the DependencyInjection/*Extension.php file tells you the exact service ids it registers. Then confirm at runtime:

php app/console container:debug | grep -i pingen

First use case: send an invoice PDF. Load the client service from the container, create a letter, attach the PDF, set the recipient address, and send. Do this from a console command or a queue worker, never from a controller that a user is waiting on — you are making a synchronous HTTP call to a third-party API that can take several seconds and will fail occasionally.

If container:debug shows only one service and it's the client, that's the whole surface area of this bundle. Everything else you do is the ticketpark/pingen library itself, so read its README and src/Endpoints/* classes — that's where the real API lives.

Implementation Patterns

Wrap the client in your own service. This is the single highest-value pattern for this package, because the package is archived and you will migrate off it. Never type-hint the vendor client in your controllers or domain services. Introduce a narrow interface and one adapter:

// AppBundle/Pingen/LetterSenderInterface.php
interface LetterSenderInterface
{
    /**
     * @return string The provider's letter id, for later status polling.
     */
    public function send(Letter $letter): string;
}
// AppBundle/Pingen/PingenLetterSender.php
class PingenLetterSender implements LetterSenderInterface
{
    private $client;
    private $logger;

    public function __construct($client, LoggerInterface $logger)
    {
        $this->client = $client;
        $this->logger = $logger;
    }

    public function send(Letter $letter): string
    {
        // Endpoint/method names vary by library version — check
        // vendor/ticketpark/pingen/src/Endpoints before copying this.
        $endpoint = $this->client->letters();

        $created = $endpoint->createLetter([
            'address' => $letter->getRecipientAddress(),
            'country' => $letter->getCountryCode(),
        ]);

        $endpoint->uploadFile($created->getId(), $letter->getPdfPath());
        $endpoint->sendLetter($created->getId());

        return $created->getId();
    }
}

Register it as a service with the bundle's client id injected, and mark it public: false.

Persist the provider id, not the outcome. Store pingen_letter_id, the requested sent_at, and a local status column on your entity. Letter delivery is asynchronous and postal — the API's "sent" means "accepted for printing". Status transitions arrive via polling the letter endpoint (or, on newer Pingen versions, webhooks). Your domain should own a LetterStatus enum and map the provider's strings onto it, so a future migration to Pingen's v2 API doesn't ripple through your codebase.

Queue the send. A SendLetterJob with the local letter id, processed by a worker, gives you retries, backoff, and a dead-letter path. Idempotency matters: if you retry after a timeout you may create a duplicate physical letter, which costs real money. Guard it by persisting the pingen_letter_id immediately after the create call and checking for it before retrying.

Group multi-page documents yourself. Pingen bills per letter/page and enforces PDF constraints. Build the PDF locally (dompdf, wkhtmltopdf, TCPDF) and validate page count, A4 dimensions, and embedded fonts before you ever hit the API — a failed upload after a slow round-trip is the most annoying failure mode.

Test with a fake. Because the bundle only exposes the configured client, the cleanest testing story is to inject a stub LetterSenderInterface for functional tests, and in unit tests mock the vendor client or its endpoints. If the library supports pointing at a non-production base URL, wire that in config_test.yml via the bundle's endpoint/host config key (again: check Configuration.php); otherwise keep all Pingen calls behind the interface and stub it.

Legacy integration in a modern app. If you're on Symfony 3/4+ and must use this bundle, you'll be fighting AppKernel conventions and old constraint files. A better move is to drop the bundle entirely and register the underlying ticketpark/pingen client as a plain service in services.yml, configured from environment variables. You lose nothing — the bundle adds almost no behaviour.

Gotchas and Tips

  • Archived and unmaintained. Last release was October 2015, and the repository is archived. There will be no fixes for PHP 7.2+ deprecations, PSR-4 issues, or Symfony compatibility. Treat any use as a frozen dependency: pin exact versions in composer.json, commit composer.lock, and expect composer update to break things.
  • Symfony 2 only, effectively. The bundle predates Symfony 3's service/bundle changes. Don't assume it works on Symfony 4+ autowiring; it doesn't ship service definitions in the modern sense. If you're on a newer kernel, skip the bundle and use the library directly.
  • The upstream API is the real risk, not the PHP code. Pingen has moved on to a v2 API with OAuth2 client credentials and different endpoints. A 2015-era client may hit deprecated routes or be rejected outright. Verify against Pingen's current API docs and check whether your account still has v1 access before building anything on top of it.
  • Config keys are undocumented. There is essentially no README-level documentation for the bundle config. DependencyInjection/Configuration.php and the extension class are your only sources of truth, plus container:debug for service ids. Budget an hour to read those files before writing integration code.
  • Credentials in parameters, not YAML. Older Symfony projects habitually committed parameters.yml. Use %env()% (or a parameters.yml.dist + local override) so client secrets never land in git.
  • Don't call the API in the request cycle. Slow third-party HTTP in a controller means user-visible timeouts and half-completed sends. Console command or message queue, always.
  • Physical mail cannot be unsent. There's no DELETE that recalls a printed letter. Validate recipient addresses hard before submitting; a typo costs money and produces a support ticket. Consider a human review step for anything above a trivial value.
  • Log the raw request/response ids. When a letter goes missing, the provider's letter id and timestamp are the only things their support can act on. Log them at info on create and on every status transition.
  • Extension point if you must patch it: the bundle's DI extension is small. You can override the client service definition in your app config (same id, later definition wins) to inject a decorated client, add logging middleware, or swap in a different HTTP transport — all without forking the package. That's usually better than vendoring a fork of an archived bundle.
  • Migration target: plan to replace this with either the current ticketpark/pingen library registered as a plain service, or Pingen's official v2 SDK. The LetterSenderInterface wrapper above makes that a one-class change.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings