reachweb/statamic-resrv-vouchers
Composer 安装命令:
composer require reachweb/statamic-resrv-vouchers
包简介
QR-code voucher generation, email delivery, and CP scanning for Statamic Resrv reservations.
README 文档
README
QR-code voucher generation, email delivery, and CP scanning for Statamic Resrv reservations.
When a reservation is confirmed in a voucher-enabled collection, the addon:
- Generates a signed-token QR code for the reservation.
- Attaches an inline PNG (for the email body) and a single-page PDF to Resrv's existing confirmation email.
- Exposes a CP page where staff can scan the QR with a phone camera (or paste the token manually), validate the voucher, and mark it used.
- Sends a "thank you for attending" email when the voucher is marked used (when the customer has an email on file).
- Invalidates the voucher when the underlying reservation is cancelled, refunded, or expires.
Requirements
- Statamic 6.x
- PHP 8.4+ — the Vouchers package itself allows 8.3, but Resrv v6 requires 8.4, so that is the effective floor.
- Laravel 12.x or 13.x
- Resrv v6. It ships the
Reach\StatamicResrv\Events\BuildingReservationEmailevent the addon listens to in order to attach the QR / PDF to the confirmation mailable. use resrvpermission for any CP user who should scan, list, or resend vouchers. No new permission is introduced — the addon piggy-backs on Resrv's existing one.- A running queue worker. Voucher generation and the attended email are queued jobs. Without a worker no vouchers are created and no attended emails go out — alternatively set
QUEUE_CONNECTION=syncfor fully synchronous behavior.
How it works
The addon is purely event-driven and never modifies Resrv. When Resrv confirms a reservation, a queued listener issues exactly one voucher per reservation (re-fired events are a no-op). While Resrv builds its confirmation email, a synchronous listener attaches the voucher PDF and embeds the inline QR PNG into that same mailable — there is no separate voucher email. At the venue, staff scan the QR (or paste the token) on a CP page; marking the voucher used triggers the attended email. Cancelling, refunding, or expiring the reservation invalidates the voucher, and any voucher past date_end + grace_days reports as expired on its own.
Installation
composer require reachweb/statamic-resrv-vouchers
php artisan resrv-vouchers:install # also available as: php please resrv-vouchers:install
The install command, in order:
- Publishes
config/resrv-vouchers.php(tagresrv-vouchers-config). - Runs the addon's migrations (
resrv_vouchersandresrv_voucher_scanstables). - Asks "Publish the email templates? (only needed if you wish to edit them)" — publishes to
resources/views/vendor/statamic-resrv-vouchers/email/(tagresrv-vouchers-emails). - Asks "Publish the language files? (only needed if you wish to edit them)" — publishes to
lang/vendor/statamic-resrv-vouchers/(tagresrv-vouchers-language).
After installing:
- Add the collection handles you want vouchers issued for to
enabled_collectionsinconfig/resrv-vouchers.php. - If you want the QR inline in the email body (the PDF is always attached), add the include snippet to Resrv's published confirmation template — see Email customization.
- Make sure a queue worker is running on the host.
Configuration
Edit config/resrv-vouchers.php after installation:
return [ // Reservations in these collection handles are voucher-eligible. 'enabled_collections' => ['accommodation', 'activities'], // Days added on top of `reservation.date_end` before a voucher reports as expired. 'grace_days' => 1, // HMAC signing key. Falls back to APP_KEY when null. 'signing_key' => env('RESRV_VOUCHERS_SIGNING_KEY'), 'email' => [ 'attended' => [ 'subject' => null, // override default "Thank you for attending!" subject 'from' => ['address' => null, 'name' => null], 'markdown' => null, // override the default template handle ], ], ];
enabled_collections defaults to an empty array — reservations in collections not listed here are silently skipped, no voucher is created, and the confirmation email goes out unchanged.
Tokens have the form base64url(uuid) . '.' . base64url(hmac_sha256(uuid, key)) and are verified in constant time. The key falls back to APP_KEY (the base64: prefix is handled) when RESRV_VOUCHERS_SIGNING_KEY is unset; set a dedicated key in your .env if you want to be able to rotate it without rotating APP_KEY.
Email customization
The QR is attached to Resrv's existing confirmation email as a PDF on every send. To also show the QR inline in the email body, publish Resrv's confirmation template and include this addon's snippet:
php artisan vendor:publish --tag=resrv-emails # Resrv's publish tag, not the addon's
Then add the include to resources/views/vendor/statamic-resrv/email/reservations/confirmed.blade.php wherever you want the QR to appear:
@include('statamic-resrv-vouchers::email.vouchers.partials.qr')
The snippet uses <img src="cid:voucher-qr"> against the inline image the addon embeds at send time — no extra wiring required. Its caption ("Show this QR code at check-in.") is translatable via the statamic-resrv-vouchers::email.qr_caption language key.
To customize the "attended" email's subject, from, or markdown template, set the relevant email.attended key in config/resrv-vouchers.php. The mailable extends Reach\StatamicResrv\Mail\Mailable, so it picks up Resrv's published theme components automatically. If you published the addon's email templates during install, the attended template itself is editable at resources/views/vendor/statamic-resrv-vouchers/email/vouchers/attended.blade.php.
CP usage
Both CP pages live under the Resrv → Vouchers nav section and require the use resrv permission:
- Vouchers / List (
/cp/resrv-vouchers) — a paginated listing (25 per page, sorted by issue date, newest first) with columns for ID, status, reservation reference, expiry, used-at, and issued-at, plus a status filter. Each row has a Resend email action that re-sends the reservation confirmation (with the voucher attached) through Resrv's email dispatcher — it honors Resrv's own reservation-email settings, so if the confirmation email is disabled there the resend fails with "Email could not be sent." - Vouchers / Scan (
/cp/resrv-vouchers/scan) — an html5-qrcode camera scanner with a "Switch camera" button (shown only when the device has more than one camera) and an "Or paste a token" text fallback with a Validate button. On a successful decode the page shows a result card: status badge, a color-coded banner ("Voucher is valid." / "Voucher has already been used." / "Voucher has been invalidated." / "Voucher has expired."), and the reservation's reference, guest name, dates, and quantity. Buttons gate by status: Mark as used when the voucher isissued, Un-mark when it isused, Scan another always.
Every lookup, mark-used, un-mark, and resend is audit-logged — see Developer reference.
Voucher lifecycle
| Trigger | Result |
|---|---|
Resrv ReservationConfirmed (eligible collection) |
Voucher created with status=issued, expires at date_end + grace_days. Queued; idempotent — one voucher per reservation, re-fired events are a no-op. |
Resrv BuildingReservationEmail |
Synchronous listener attaches the voucher PDF (A6 page: QR, guest name, reference, date range) and embeds the inline PNG. No voucher → the email sends normally without attachments. |
| CP "Mark as used" | status=used, audit-logged, attended email queued to the customer (skipped if the customer has no email). |
| CP "Un-mark" | status=issued, audit-logged, no customer email. |
Resrv ReservationCancelled / Refunded / Expired |
status=invalidated, reason recorded (cancelled / refunded / expired-reservation). A voucher already used stays used — the customer did attend. Repeat events are a no-op. |
now() > expires_at while issued |
Reports as expired (lazily — no DB write, no cron). Attempting "Mark as used" on an expired voucher fails with a 422. |
Voucher generation and the attended email run on the queue — see Requirements.
Troubleshooting
- Vouchers aren't generated / attended emails don't arrive — no queue worker is running. Start one (
php artisan queue:work) or setQUEUE_CONNECTION=sync. - "Mark as used" returns an error on a valid-looking voucher — the voucher is past
expires_at(lazily expired) or has been invalidated; neither can be marked used. Check the status banner on the scan result. - Camera access blocked on
http://— browsers require HTTPS (orlocalhost) forgetUserMedia. Deploy behind TLS or use the manual token-input fallback. npm run cp:builderrors about@statamic/cmsnot found — the file: dep resolves from the host site'svendor/(file:../../../vendor/statamic/cms/resources/dist-package). Runcomposer installin the host first, thennpm install --install-linksso npm copies the package rather than symlinking it (transitive deps like@vitejs/plugin-vueresolve from the addon's ownnode_modulesonly when copied).- Email arrives without the inline QR but with the PDF attachment — you have not published / included the QR snippet in Resrv's confirmation template. The PDF attachment is always sent regardless. Add
@include('statamic-resrv-vouchers::email.vouchers.partials.qr')to your publishedconfirmed.blade.php. - Voucher generation seems to be missing for some reservations — confirm the reservation's collection handle is listed in
config/resrv-vouchers.phpenabled_collections. Vouchers are silently skipped for any reservation whose entry lives outside that list.
Developer reference
Events you can listen to (all in Reach\StatamicResrvVouchers\Events):
VoucherUsed— fired when a voucher transitions toused(carries the voucher + acting user id).VoucherUnmarked— fired when a used voucher is reverted toissued.VoucherInvalidated— fired when a voucher is invalidated (carries the reason).
The addon's integration surface with Resrv is consumed, never modified: it listens to ReservationConfirmed, BuildingReservationEmail, and ReservationCancelled / ReservationRefunded / ReservationExpired.
Audit table — every scan-page and listing action writes a row to resrv_voucher_scans: action ∈ scan | mark-used | un-mark | resend, result ∈ success | not-found | already-used | invalidated | expired | invalid-transition | not-sent, plus the acting user id, IP address, and user agent.
Voucher table — resrv_vouchers: string UUID primary key, unique reservation_id (the one-voucher-per-reservation guarantee) and token, indexed status, expires_at, used_at / used_by_user_id, invalidated_reason.
CP endpoints (internal — may change without notice; all behind can:use resrv):
| Method | Path | Purpose |
|---|---|---|
| GET | /cp/resrv-vouchers/list |
Paginated voucher JSON for the listing |
| POST | /cp/resrv-vouchers/lookup |
Validate a token, return voucher + reservation + status banner |
| PATCH | /cp/resrv-vouchers/mark-used |
Transition issued → used |
| PATCH | /cp/resrv-vouchers/un-mark |
Transition used → issued |
| POST | /cp/resrv-vouchers/resend/{voucher} |
Re-send the confirmation email with the voucher attached |
Frontend development — only needed when developing the addon itself; production installs ship the prebuilt resources/dist/build/. The @statamic/cms dependency resolves from the host site's vendor directory, so install with npm install --install-links (copies instead of symlinking). Then npm run cp:dev for Vite HMR or npm run cp:build to produce the deployable build the Statamic CP picks up via the protected $vite declaration in VouchersProvider. Stack: Vite 8, Tailwind v4, Vue 3 Inertia pages registered via Statamic.$inertia.register(...).
Tests
vendor/bin/pest # full suite vendor/bin/pest --filter X # single test vendor/bin/pint # code style
The suite extends Statamic\Testing\AddonTestCase and runs against SQLite in-memory with the Resrv sibling addon resolved as dev-main via the ../statamic-resrv path repo.
License
Proprietary. Licensed for use on Reach Web client projects; not for redistribution. Support: info@reach.gr.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 3
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: proprietary
- 更新时间: 2026-06-11