PaymentPayPageTrait
Dedicated filtered action for the payment page.
The front end page "payment execution" (/platba/p-{id}) loads the payment unconditionally, even for an anonymous
visitor (PaymentShowTrait::isPaymentAccessible() deliberately leaves an account-less payment open: anyone with
the link may pay). A grant on :Api:System:Payment:get is the wrong answer for two reasons:
gethasactionGet(fromCrudTrait), so it is reachable externally over HTTP; a grant would open reading of all payments to every holder of a valid token.Paymentcarriesemail,ip,encryptedSignature,gatewayPaymentIdandcallback, and the read path has no field tier, sogetwould issue the wholeextract().
This action closes both:
- It has no
actionPayPage()and must not get one. Withoutaction*Nette rejects an external request with 404 before anything loads, so the key:Api:System:Payment:payPageis a decision about the internal path only; aguest/registratedgrant on it opens no external HTTP. - The server authorizes, not the caller: the rule is the same as the front end
isPaymentAccessible(), evaluated inside the Api. A completed/refunded payment is not issued at all and an account-bound payment only to its owner; the identity comes only fromgetJwtPayload(), never from a parameter. - The payload is narrowed positively (self::PAY_PAGE_FIELDS): a new entity column never reaches the response by itself (fail-closed; a negative list would fail open).
Table of Contents
Constants
- PAY_PAGE_FIELDS : array<string|int, mixed> = ['id', 'status', 'userId', 'paymentGateId', 'pa...
- IMPORTANT: allowlist of payment page fields. Exactly what `PaymentShowTrait` (Front) and the `paymentMobilniplatby` component need. A key added here is opened to an anonymous visitor.
- PAY_PAGE_CLOSED_STATUSES : array<string|int, mixed> = [\App\UI\Api\System\Enums\PaymentStatus::COMPLE...
- States in which the payment page must not be opened (parity with `actionExecute()` and the front end `isPaymentAccessible()`): reopening the URL would overwrite `gatewayPaymentId` of a finished payment.
Methods
- assertPaymentPayable() : array<string, mixed>|null
- Single definition of "may this payment be opened / started at the gateway right now".
- internalActionPayPage() : array<string, mixed>
- isPaymentTargetPayable() : bool
- Module-level payability ({@see PaymentCallbackRegistry::PAYABILITY_CHECKS}) by the payment `callback_key`.
- payPageFailure() : array<string, mixed>
- Failure in the same shape the guard returns (`Result::failure(...)->toArray()`), so the fail-soft boundary recognizes it by one predicate {@see Result::isFailureEnvelope()} and the caller gets "no data" instead of an entity with meaningless fields.
Constants
PAY_PAGE_FIELDS
IMPORTANT: allowlist of payment page fields. Exactly what `PaymentShowTrait` (Front) and the `paymentMobilniplatby` component need. A key added here is opened to an anonymous visitor.
public
array<string|int, mixed>
PAY_PAGE_FIELDS
= ['id', 'status', 'userId', 'paymentGateId', 'paymentRegionId', 'currencyId', 'totalPrice', 'variableSymbol', 'code', 'name', 'description', 'successUrl', 'cancelUrl', 'insertDatetime', 'paymentDatetime', 'requirePaymentEet', 'paymentGateFeePrice', 'paymentGateFeePercents', 'paymentGateFeeTotal', 'paymentGateFeePercentsPrice']
Deliberately missing: email, ip, encryptedSignature, gatewayPaymentId, callback, notifyUrl,
paymentCallbackId, paymentEetId - the page does not need them and they are either personal data or gateway secrets.
PAY_PAGE_CLOSED_STATUSES
States in which the payment page must not be opened (parity with `actionExecute()` and the front end `isPaymentAccessible()`): reopening the URL would overwrite `gatewayPaymentId` of a finished payment.
private
array<string|int, mixed>
PAY_PAGE_CLOSED_STATUSES
= [\App\UI\Api\System\Enums\PaymentStatus::COMPLETED, \App\UI\Api\System\Enums\PaymentStatus::REFUNDED]
Methods
assertPaymentPayable()
Single definition of "may this payment be opened / started at the gateway right now".
public
assertPaymentPayable(Payment $payment) : array<string, mixed>|null
Used by self::internalActionPayPage() and PaymentCreateTrait::internalActionCreate(); two copies of the conditions are exactly how authorization drifts apart.
The rule equals the front end check
(App\UI\Front\System\Presenters\Traits\Details\PaymentShowTrait::isPaymentAccessible()), evaluated
server-side, so it also holds for a caller that bypassed the front end:
- A completed/refunded payment is not reopened (repeated GET would overwrite
gatewayPaymentIdof a finished payment; parity withactionExecute()). - A payment bound to an account belongs only to its owner (identity only from the JWT, never from a parameter).
- A payment without an account (
user_id IS NULL) stays open on purpose: it does not matter who pays, whoever has the link.
A privileged role (currently admin) bypasses the rule, because the admin must be able to open a foreign
payment. PermissionResolver::isGrantedByPrivilegedRole() decides it, not a
hardcoded role name.
Parameters
- $payment : Payment
Return values
array<string, mixed>|null —null = may continue; array = return it to the caller
internalActionPayPage()
public
internalActionPayPage([int|null $id = null ]) : array<string, mixed>
Parameters
- $id : int|null = null
Return values
array<string, mixed> —allowlist from self::PAY_PAGE_FIELDS, or a failed Result envelope (which the
fail-soft boundary turns into "no data": EntityHydrator::rejectFailureEnvelope() / FailSoftReadTrait)
isPaymentTargetPayable()
Module-level payability ({@see PaymentCallbackRegistry::PAYABILITY_CHECKS}) by the payment `callback_key`.
private
isPaymentTargetPayable(Payment $payment) : bool
Fail-open: no callback, no registered check or an unresolvable service means "nothing to object", the usual guards above/below still apply.
Parameters
- $payment : Payment
Return values
boolpayPageFailure()
Failure in the same shape the guard returns (`Result::failure(...)->toArray()`), so the fail-soft boundary recognizes it by one predicate {@see Result::isFailureEnvelope()} and the caller gets "no data" instead of an entity with meaningless fields.
private
static payPageFailure(ResultCode $code, string $message) : array<string, mixed>
Parameters
- $code : ResultCode
- $message : string