MobilniplatbyGateway
extends AbstractPaymentGateway
in package
implements
ExpressSmsGatewayInterface
SMS payments through mobilniplatby.cz (Airtoy a.s.).
The gateway calls two of our endpoints over HTTP GET. Section 1 is the SMS receipt
(timestamp, phone, sms, shortcode, country, operator, att, id); the reply is
text/plain with a non-zero Content-Length, HTTP 200, within 20 s, and for MT billing the
text after the semicolon carries the billing level (...;90333149, free of charge
FREE90333149). Section 6 is the delivery report (timestamp, request = the id from
section 1, status (DELIVERED, UNDELIVERED, PENDING, WAITING, UNKNOWN), message = reason
for UNDELIVERED, ord, cnt, att, id); the reply is HTTP 204 within 20 s.
IMPORTANT: the delivery report carries no amount; the price is decided by shortcode, which
arrives only in section 1. The amount is therefore verified in checkPayment() by pairing
shortcode with a PaymentGatePhone (see findPossibleGatewayPhones()). A payment
is found by request only when a confirmed checkPayment() stored its gatewayPaymentId, so
by then the amount is proven and is reported to the central guard as confirmed.
IMPORTANT: the only gateway that cannot be queried back (both interfaces are one-way and the
status arrives in parameters), so the origin must be proven by a shared secret. It is our own
query parameter in the URL registered in the mobilniplatby.cz admin
(.../api/system/payment/delivery?pg=mobilniplatby&secret=<secret>); the name secret does
not collide with any parameter from the specification. It is stored in PaymentGate.settings
as delivery_secret[<secret>]. The secret is enforced in both checkPayment() and
checkDelivery(): the SMS receipt is as one-way as the delivery report, shortcode is
public (the customer reads it on the payment page) and the amount check is a tautology here,
so without the secret a single unauthenticated GET would mark any payment as paid.
IMPORTANT: without a configured secret the gateway fails closed. Every notification is rejected (logged, payment status unchanged); failing open would let two unauthenticated GETs pay any order.
Optionally delivery_ips[1.2.3.4,10.0.0.0/8] adds a sender allowlist as a second layer.
Table of Contents
Interfaces
- ExpressSmsGatewayInterface
- A premium SMS gateway that can receive SMS on one fixed address (`api/system/payment/express-execute`).
Constants
- SECRET_PARAMS : mixed = ['delivery_secret', 'secret']
- Our own query parameter in the registered notification URL (see the class docblock).
Properties
- $currencies : array<int, Currency>
- $currencyManager : CurrencyManager
- $httpRequest : IRequest
- $paymentGatePhoneManager : PaymentGatePhoneManager
- $paymentManager : PaymentManager
Methods
- __construct() : mixed
- allowRedirect() : bool
- checkDelivery() : GatewayCheckResult
- checkPayment() : GatewayCheckResult
- createPayment() : GatewayCreateResult
- getExpressKeyword() : string
- Returns the website keyword at the aggregator (`keyword[...]` in the gateway settings), the first word of every SMS: form (`KEYWORD <code>`) and express (`KEYWORD MODULE SERVICE ID`).
- getScriptName() : string
- loadPayment() : Payment|null
- loadPaymentFromDelivery() : Payment|null
- sendDeliveryResponse() : void
- verifyNotificationOrigin() : string|null
- Verifies the origin of a gateway notification for both one-way interfaces: the SMS receipt ({@see checkPayment()}) and the delivery report ({@see checkDelivery()}). The operator registers both in the mobilniplatby.cz portal, so both may carry our `secret` in the query.
- amountFromMinorUnits() : float|null
- Converts an amount in minor units (cents) to currency units. `null` when the gateway reports none.
- amountFromUnits() : float|null
- createFailure() : GatewayCreateResult
- Handles a gateway client failure while creating a transaction: the redacted detail goes only to the server log, the result carries a stable code. A logging error never breaks the payment flow.
- getSetting() : mixed
- pickGatewayPaymentId() : string
- Picks the transaction id to verify at the gateway.
- settingString() : string
- Typed variant of {@see getSetting()}; settings are always plain strings, this only saves `(string)` casts over `mixed` at call sites.
- encodeCode() : string
- findPossibleGatewayPhones() : array<string|int, PaymentGatePhone>
- isIpAllowed() : bool
- loadCurrencies() : void
- matchesCidr() : bool
- serializePhones() : array<int|string, array<string, mixed>>
Constants
SECRET_PARAMS
Our own query parameter in the registered notification URL (see the class docblock).
private
mixed
SECRET_PARAMS
= ['delivery_secret', 'secret']
The gateway specification defines no such parameter, so it cannot collide.
Properties
$currencies
private
array<int, Currency>
$currencies
= []
$currencyManager read-only
private
CurrencyManager
$currencyManager
$httpRequest read-only
private
IRequest
$httpRequest
$paymentGatePhoneManager read-only
private
PaymentGatePhoneManager
$paymentGatePhoneManager
$paymentManager read-only
private
PaymentManager
$paymentManager
Methods
__construct()
public
__construct(PaymentManager $paymentManager, PaymentGatePhoneManager $paymentGatePhoneManager, CurrencyManager $currencyManager, IRequest $httpRequest) : mixed
Parameters
- $paymentManager : PaymentManager
- $paymentGatePhoneManager : PaymentGatePhoneManager
- $currencyManager : CurrencyManager
- $httpRequest : IRequest
allowRedirect()
public
allowRedirect() : bool
Return values
boolcheckDelivery()
public
checkDelivery(Payment $payment, PaymentGate $paymentGate, array<string, mixed> $params) : GatewayCheckResult
Parameters
- $payment : Payment
- $paymentGate : PaymentGate
- $params : array<string, mixed>
Return values
GatewayCheckResultcheckPayment()
public
checkPayment(Payment $payment, PaymentGate $paymentGate, array<string, mixed> $params) : GatewayCheckResult
Parameters
- $payment : Payment
- $paymentGate : PaymentGate
- $params : array<string, mixed>
Return values
GatewayCheckResultcreatePayment()
public
createPayment(Payment $payment, PaymentGate $paymentGate, array<string|int, mixed> $urls) : GatewayCreateResult
Parameters
- $payment : Payment
- $paymentGate : PaymentGate
- $urls : array<string|int, mixed>
Return values
GatewayCreateResultgetExpressKeyword()
Returns the website keyword at the aggregator (`keyword[...]` in the gateway settings), the first word of every SMS: form (`KEYWORD <code>`) and express (`KEYWORD MODULE SERVICE ID`).
public
getExpressKeyword(PaymentGate $paymentGate) : string
Parameters
- $paymentGate : PaymentGate
Return values
stringgetScriptName()
public
getScriptName() : string
Return values
stringloadPayment()
public
loadPayment(PaymentGate $paymentGate, array<string, mixed> $params) : Payment|null
Parameters
- $paymentGate : PaymentGate
- $params : array<string, mixed>
Return values
Payment|nullloadPaymentFromDelivery()
public
loadPaymentFromDelivery(PaymentGate $paymentGate, array<string, mixed> $params) : Payment|null
Parameters
- $paymentGate : PaymentGate
- $params : array<string, mixed>
Return values
Payment|nullsendDeliveryResponse()
public
sendDeliveryResponse() : void
verifyNotificationOrigin()
Verifies the origin of a gateway notification for both one-way interfaces: the SMS receipt ({@see checkPayment()}) and the delivery report ({@see checkDelivery()}). The operator registers both in the mobilniplatby.cz portal, so both may carry our `secret` in the query.
public
verifyNotificationOrigin(PaymentGate $paymentGate, array<string, mixed> $params) : string|null
Returns null when valid, otherwise the rejection reason.
Parameters
- $paymentGate : PaymentGate
- $params : array<string, mixed>
Return values
string|nullamountFromMinorUnits()
Converts an amount in minor units (cents) to currency units. `null` when the gateway reports none.
protected
amountFromMinorUnits(mixed $value) : float|null
Parameters
- $value : mixed
Return values
float|nullamountFromUnits()
protected
amountFromUnits(mixed $value) : float|null
Parameters
- $value : mixed
Return values
float|nullcreateFailure()
Handles a gateway client failure while creating a transaction: the redacted detail goes only to the server log, the result carries a stable code. A logging error never breaks the payment flow.
protected
createFailure(string $provider, Throwable $e[, int|null $paymentId = null ]) : GatewayCreateResult
Parameters
- $provider : string
- $e : Throwable
- $paymentId : int|null = null
Return values
GatewayCreateResultgetSetting()
protected
getSetting(PaymentGate $paymentGate, string $key[, mixed $default = null ]) : mixed
Parameters
- $paymentGate : PaymentGate
- $key : string
- $default : mixed = null
pickGatewayPaymentId()
Picks the transaction id to verify at the gateway.
protected
pickGatewayPaymentId(Payment $payment, mixed ...$candidates) : string
The id is stored in the DB only by the central guard in PaymentProcessorTrait, and
only after the gateway confirms the transaction; this method just says which id to ask about.
IMPORTANT: the id from the request wins over the stored one, because the gateway reports exactly that transaction. One payment can have several transactions at the gateway (a retried payment overwrites the stored id while the first stays open and payable). Ignoring the request id would drop the paid notification as a conflict: the money arrives and the order stays unpaid.
Security is kept: when the id differs from the stored one, the central guard
(PaymentProcessorTrait::processPayment())
lets the notification through only with a proven referenceId equal to our payment_id,
so a foreign transaction planted by an attacker is rejected, after querying the gateway.
Parameters
- $payment : Payment
- $candidates : mixed
-
request values in priority order (the first non-empty one wins)
Return values
stringsettingString()
Typed variant of {@see getSetting()}; settings are always plain strings, this only saves `(string)` casts over `mixed` at call sites.
protected
settingString(PaymentGate $paymentGate, string $key[, string $default = '' ]) : string
Parameters
- $paymentGate : PaymentGate
- $key : string
- $default : string = ''
Return values
stringencodeCode()
private
encodeCode(int $paymentId) : string
Parameters
- $paymentId : int
Return values
stringfindPossibleGatewayPhones()
private
findPossibleGatewayPhones(Payment $payment, PaymentGate $paymentGate, bool $sortByCurrency) : array<string|int, PaymentGatePhone>
Parameters
- $payment : Payment
- $paymentGate : PaymentGate
- $sortByCurrency : bool
Return values
array<string|int, PaymentGatePhone>isIpAllowed()
private
isIpAllowed(string $allowList) : bool
Parameters
- $allowList : string
Return values
boolloadCurrencies()
private
loadCurrencies() : void
matchesCidr()
private
matchesCidr(string $ip, string $cidr) : bool
Parameters
- $ip : string
- $cidr : string
Return values
boolserializePhones()
private
serializePhones(array<string|int, PaymentGatePhone> $phones) : array<int|string, array<string, mixed>>
Parameters
- $phones : array<string|int, PaymentGatePhone>