creagia/laravel-sign-pad
Laravel package for capturing handwritten signatures via a sign pad, storing them with Eloquent models, and optionally generating certified signed PDFs. Includes install command, configurable storage/redirects, and publishable JS assets for a full signing flow.
Install the package:
composer require creagia/laravel-sign-pad
php artisan sign-pad:install
This publishes the config, migrations, and assets to public/vendor/sign-pad/.
Configure your model:
Add the RequiresSignature trait and implement CanBeSigned:
use Creagia\LaravelSignPad\Concerns\RequiresSignature;
use Creagia\LaravelSignPad\Contracts\CanBeSigned;
class Contract extends Model implements CanBeSigned
{
use RequiresSignature;
}
First use case: Render the signature pad in a Blade view:
@if (!$contract->hasBeenSigned())
<form action="{{ $contract->getSignatureRoute() }}" method="POST">
@csrf
<x-creagia-signature-pad />
</form>
<script src="{{ asset('vendor/sign-pad/sign-pad.min.js') }}"></script>
@endif
config/sign-pad.php to specify signature_disk and document_disk.redirect_route_name to control post-signature navigation.Signature Collection:
getSignatureRoute() method to generate the form endpoint.disabled-without-signature attribute) and stores signatures in the signatures table.PDF Generation:
ShouldGenerateSignatureDocument and define getSignatureDocumentTemplate():
public function getSignatureDocumentTemplate(): SignatureDocumentTemplate
{
return new SignatureDocumentTemplate(
signaturePositions: [
new SignaturePosition(page: 1, x: 20, y: 25),
],
template: new BladeDocumentTemplate('pdf.contract_template'),
);
}
Certified PDFs:
certify_documents: true in config/sign-pad.php.tcpdf.crt) in storage/app/certificates/.certificate_info (e.g., issuer, subject).@inject or view composers.SignaturePosition objects for multi-page PDFs.Signature model to fetch signed documents:
$contract->signature->getSignedDocumentAbsolutePath();
SignatureDocumentGeneratorJob) for large files.| Use Case | Implementation Pattern |
|---|---|
| Contract signing | CanBeSigned + Blade template |
| Invoice approvals | ShouldGenerateSignatureDocument + PDF merge |
| HR onboarding forms | Custom SignaturePosition for multi-signature |
| Audit trails | Log signed_at timestamp in model |
Double Submissions:
@csrf and avoid duplicate form submissions (fixed in v2.0.1).disabled-without-signature to prevent empty submissions.Signature Storage:
storage/app/signatures/) may fill up. Configure signature_disk (e.g., public) for web-accessible storage.PDF Generation Failures:
storage_path('pdf/template.pdf')).storage/logs/laravel.log for TCPDF errors (e.g., missing fonts or certificates).Certificate Issues:
openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout certificate.crt -out certificate.crt
certificate_info['validity'].Component Customization:
pad-classes="rounded-xl") are applied to the canvas. Test responsiveness.clear-name and submit-name for localization (e.g., submit-name="{{ __('Sign') }}").Signature Not Saved:
signatures table for records. Verify the model_type and model_id match your Eloquent model.CanBeSigned.PDF Not Generated:
getSignatureDocumentTemplate() returns a valid SignatureDocumentTemplate.@extends('pdf.base')
<div>@include('signature-pad::signature', ['model' => $model])</div>
JavaScript Errors:
php artisan vendor:publish --tag=sign-pad-assets).sign-pad.min.js 404 errors (verify public/vendor/sign-pad/ exists).Custom Storage:
Override the Signature model’s getSignaturePath() or getDocumentPath() methods for custom paths.
Signature Validation:
Extend the Signature model to add rules (e.g., minimum signature length):
public function validateSignature($signatureData)
{
return parent::validateSignature($signatureData)
->after(function ($validator) {
$validator->requireMinSignatureLength(100); // Custom rule
});
}
Webhook Triggers: Dispatch events after signature submission:
// In your model's trait or controller
event(new SignatureSubmitted($model, $signature));
Multi-Tenant Support:
Scope the signatures table by tenant ID in the Signature model’s boot() method:
protected static function boot()
{
parent::boot();
static::addGlobalScope('tenant', function (Builder $builder) {
$builder->where('tenant_id', auth()->user()->tenant_id);
});
}
SignatureDocumentGeneratorJob) to avoid timeouts.sign-pad.min.js or use Laravel Mix to bundle it with your app’s JS.@csrf in forms.getSignedDocumentPath() via middleware (e.g., signed-documents policy).tcpdf.crt outside the web root and restrict permissions (chmod 600).How can I help you explore Laravel packages today?