Resolve conflict in database/seeders/PaymentLibrariesSeeder.php: upstream
hid LawPay (gateway id 66) while this branch added payware MOBILE_PAYMENT
(gateway id 67) on the same visible-gateways line. Drop 66, keep 67.
Webhook integrity (security hardening alignment with server-side updates):
- Verify body via SHA-256 hash carried in JWT contentSha256 header. The
driver was looking up contentMd5 against an md5() hash; the server now
emits SHA-256 per recent server-side hardening, so the prior check was
silently a no-op. Fail-closed if the header is absent.
- Tighten iat freshness to asymmetric +60s/-300s (was symmetric 300s,
allowed future-dated tokens).
- Reject empty transactionId webhooks early (was defaulting to '' and
would have poisoned the new dedup query).
- Filter on callbackType == TRANSACTION_FINALIZED so PROCESSED callbacks
no longer overwrite local status mid-flight.
Idempotency (PR review item 1):
- Check existing Payment by transaction_reference + company_id before
createPayment in the CONFIRMED branch. Prevents duplicate Payment rows
when payware retries up to 15 times on slow IN responses.
Status enum:
- Drop local 'PENDING' string in favour of server's 'ACTIVE'. Aligns the
polling, frontend, and webhook handler on one set of names
(ACTIVE / CONFIRMED / DECLINED / FAILED / CANCELLED / EXPIRED).
Browser compatibility (PR review item 2 plus broader audit):
- Pass event explicitly to copy handler. Was relying on deprecated
window.event, which is undefined in Firefox inside a Promise.then -
the copy feedback was already silently broken there.
- Feature-detect navigator.clipboard + isSecureContext, fall back to
document.execCommand('copy') for plain-HTTP self-hosters.
- Vendor qrcode.js into public/vendor/qrcodejs/ instead of loading from
cdnjs (no SRI, no fallback, blocked under strict CSP). Added an
onerror fallback that displays the payware:// URL as text.
- Drift-free countdown via Date.now() instead of a 1s setInterval that
browsers throttle in background tabs.
- Chained setTimeout polling with AbortController instead of overlapping
setInterval(fetch, 3000). Cancels in-flight fetches on beforeunload.
- 'No compatible app installed' helper text under the mobile pay button.
- Inline English fallback strings replaced with ctrans keys.
Confirmation flow:
- On CONFIRMED, JS now submits #server-response so paymentResponse is
the live confirmation handler that performs the redirect (matches
btcpay/razorpay livewire pattern). Removes the dead AJAX-poll branch
and closes the gap where the redirect URL was client-controlled.
Currency precision:
- PaywareApi::createTransaction accepts currencyPrecision; driver pulls
from client->currency()->precision. JPY (0 decimals) and BHD/KWD
(3 decimals) now serialize correctly.
Login circuit breaker:
- 60s cooldown after a failed /vpos/login. Caps cascading attempts
against payware's 5-strike vPOS lockout when credentials are
misconfigured (without it, 5 customer page loads can lock out the
merchant's vPOS for 15 minutes across all channels).
New 'Mobile Payment' payment type (id 53):
- Generic payment_type for mobile-initiated A2A payments. Mirrors the
existing GatewayType::MOBILE_PAYMENT (id 30) and follows the
precedent of MOLLIE_BANK_TRANSFER (34) and STRIPE_BANK_TRANSFER (50).
Migration extended to seed the row idempotently. Driver now stamps
payments with this type instead of INSTANT_BANK_PAY (which is
GoCardless's brand for their A2A flow). Companion change for the
React side will follow in invoiceninja/ui.
Translations:
- New keys (payment_was_not_completed, no_compatible_app_installed,
payment_type_Mobile Payment) added to lang/en/texts.php as the source
of truth, plus lang/bg/texts.php for completeness. Other locales fall
back to en until community translators sync.
Modified files:
- app/Models/PaymentType.php
- app/PaymentDrivers/Payware/BankTransfer.php
- app/PaymentDrivers/Payware/PaywareApi.php
- app/PaymentDrivers/PaywarePaymentDriver.php
- database/migrations/2026_02_15_000000_add_payware_gateway.php
- lang/bg/texts.php
- lang/en/texts.php
- resources/views/portal/ninja2020/gateways/payware/pay_livewire.blade.php
New file:
- public/vendor/qrcodejs/qrcode.min.js (MIT, qrcodejs 1.0.0)
- Show payware certified badge on the right side of payment method buttons
- Replace footer certified text with badge positioned top-right of payment details
- Use consistent font styling for transaction ID