CoolCMS API

MobilniplatbyGateway extends AbstractPaymentGateway
in package
implements ExpressSmsGatewayInterface

FinalYes

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

Methods

getExpressKeyword()

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
string

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|null

amountFromMinorUnits()

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|null

amountFromUnits()

protected amountFromUnits(mixed $value) : float|null
Parameters
$value : mixed
Return values
float|null

createFailure()

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
GatewayCreateResult

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
string

settingString()

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
string

encodeCode()

private encodeCode(int $paymentId) : string
Parameters
$paymentId : int
Return values
string

isIpAllowed()

private isIpAllowed(string $allowList) : bool
Parameters
$allowList : string
Return values
bool

matchesCidr()

private matchesCidr(string $ip, string $cidr) : bool
Parameters
$ip : string
$cidr : string
Return values
bool
On this page

Search results