Repository navigation
[Payments]: Guest online-payment page & shared gateway pipeline #653
Description
Recreate v1's anonymous "pay this invoice" surface and the gateway-agnostic plumbing both gateway issues depend on. v1 behavioral spec: application/modules/guest/controllers/Payment_information.php (branch prep/v180).
Specific
- Guest pay page at
GET /pay/{urlKey}(no auth): resolves the invoice byurl_keyonly if guest-visible; redirects to the guest invoice view withinvoice_already_paidwhen balance is 0 for a known user, 404 otherwise (v1:Payment_information.php:31-48). Lists available gateways: a gateway is offered whensettings.<gateway>_enabledis on AND (invoice has no payment method OR gateway has no mapped method OR they match — v1:Payment_information.php:56-69usinggateway_<d>_payment_method). When exactly one gateway qualifies it is auto-selected and its pay UI is served directly (v1: :72-74). Page shows invoice summary, payment method, and overdue state (v1: :77-93). Gateway sub-views: Stripe embedded checkout mount (publishable key; v1:112-120) and PayPal JS SDK buttons (client id, currency; v1:130-140). - Shared pipeline used by both gateway callbacks:
- Migration: add
payment_external_id(string 255, nullable, unique) topayments(Modules/Payments/Database/Migrations/); add toModules/Payments/Models/Payment.php. Modules/Payments/Services/Gateways/GatewayPaymentRecorder.php(name indicative): given invoice, amount, currency, external id, method, note — performs dedup lookup onpayment_external_id, already-paid guard, currency match vs the gateway's configured currency, amount ≥ balance − 0.0001 check, creates thePaymentviaPaymentService, and writes theGatewayTransactionjournal row ([Payments]: Online Payments Journal #144) for success/failure/exception alike (v1 spec:Stripe.php:124-204,Paypal.php:163-258).- Master toggle
settings.enable_online_payments(v1:partial_settings_online_payment.php:24-27) added to the Online Payment tab inModules/Core/Filament/Admin/Pages/Settings.php; when off,/pay/{urlKey}returns 404 regardless of per-gateway toggles. - Make the two
online_payment_methodselects inSettings.php(:650-656,:680-686, currently['' => '']+ TODO) list the company's payment methods so the chooser's method-matching rule can work.
- Migration: add
Measurable
- Anonymous request to
/pay/{urlKey}renders the chooser for a payable invoice with ≥2 enabled gateways; auto-serves the single gateway when only one qualifies; 404s for unknown keys or when online payments are disabled. - Method-matching rule verified for all three branches (invoice method 0, gateway method 0, exact match) and the negative case.
payments.payment_external_idexists, is unique, and dedup behaves as specified.
Achievable
- Controller
Modules/Payments/Http/Controllers/GuestPaymentController.php+ Blade views underModules/Payments/resources/views/guest/; routes inModules/Payments/routes/web.php(currently<?phponly). - No dependency on [Clients]: User Accounts for Customers (Client Portal) #392 (client portal) — v1 parity requires the anonymous flow, and [Clients]: User Accounts for Customers (Client Portal) #392 explicitly keeps anonymous links working. Requires a guest invoice view to link back to (Invoices-domain guest view issue, if tracked separately).
- Blocks: Stripe gateway issue, PayPal gateway issue, [Feature Request]: PayPal Complete Payments - Venmo and Advanced Credit Cards #517.
Relevant
This is the entry point for every online payment in v1; without it the gateway integrations have no surface, and without payment_external_id neither gateway can guarantee idempotent payment recording.
Time-Bound
First of the three online-payment issues; before both gateway issues.
Arrange
Company with enable_online_payments=1; invoices: payable with method A, payable with method 0, fully paid; gateways: stripe enabled mapped to method A, paypal enabled mapped to method 0 (any).
Act
GET /pay/{urlKey} for each invoice; toggle enable_online_payments=0 and repeat; call GatewayPaymentRecorder twice with the same external id.
Assert
- Payable invoice with method A shows both gateways (stripe matches, paypal is method-agnostic).
- Disabling stripe leaves exactly one gateway and the page auto-serves the PayPal UI without a chooser.
- Fully paid invoice: 404 for anonymous visitors.
-
enable_online_payments=0→ 404 even with gateways enabled. -
paymentstable has a uniquepayment_external_idcolumn; second recorder call with the same id creates no payment and reports duplicate. - Recorder writes a
GatewayTransactionrow on both the success and the duplicate call. -
stripe_online_payment_method/paypal_online_payment_methodselects list real payment methods (no longer the['' => '']stub).
Activity