payments

object

%Mollie

/phlo/resources/payments/Mollie.phlo

Mollie connector: payments by iDEAL, Bancontact, card and the rest, with refunds

pay() starts a payment and Mollie::checkout($payment) is where you send the customer. Mollie then calls your webhook with only the id of the payment, and that is on purpose: ask payment($id) what happened instead of trusting what was posted. The webhook carries no cookie and no browser header, so the CSRF guard lets it through without an except. An amount is in euros or the currency you pass, never in cents. A test key (test_...) runs the whole flow without money.

molliepaymentsidealbancontactcheckoutconnector
const

Mollie :: section

line 10
'Mollie'
const

Mollie :: api

line 11
'https://api.mollie.com/v2'
method

%Mollie -> headers:array

line 13
[static::bearer($this->config['api_key'] ?? void)]
static

Mollie :: fields:array

line 15
arr(
	section: 'Mollie',
	config: arr(),
	secret: arr(api_key: 'API key (my.mollie.com > Developers > API keys), test_ or live_'),
)
static

Mollie :: amount (int|float|string $amount, string $currency):array

line 22
As a number, and with the decimals of the currency: 12,50 would otherwise be charged as 12.00.
is_numeric($amount) || error("Not an amount: $amount", 422)
return ['currency' => $currency, 'value' => number_format((float)$amount, in_array(strtoupper($currency), ['JPY', 'ISK'], true) ? 0 : 2, dot, void)]
method

%Mollie -> pay (int|float|string $amount, string $description, string $redirectUrl, ?string $webhookUrl = null, string $currency = 'EUR', array $metadata = [], ?string $method = null):obj

line 27
if ($m = $this->missing('api_key')) return $m
return $this->post('payments', array_filter(['amount' => static::amount($amount, $currency), 'description' => $description, 'redirectUrl' => $redirectUrl, 'webhookUrl' => $webhookUrl, 'metadata' => $metadata, 'method' => $method]))
method

%Mollie -> payment (string $id):obj

line 32
if ($m = $this->missing('api_key')) return $m
return $this->get('payments/'.rawurlencode($id))
method

%Mollie -> refund (string $id, int|float|string $amount, string $description = void, string $currency = 'EUR'):obj

line 37
if ($m = $this->missing('api_key')) return $m
return $this->post('payments/'.rawurlencode($id).'/refunds', array_filter(['amount' => static::amount($amount, $currency), 'description' => $description]))
method

%Mollie -> methods:obj

line 42
if ($m = $this->missing('api_key')) return $m
return $this->get('methods')
static

Mollie :: checkout (obj $payment):?string

line 48
Where to send the customer, from what pay() answered.
$payment->ok ? ($payment->data->_links->checkout->href ?? null) : null
static

Mollie :: paid (obj $payment):bool

line 51
Whether a payment is paid, from what payment() answered.
$payment->ok && ($payment->data->status ?? null) === 'paid'
object

%Stripe

/phlo/resources/payments/Stripe.phlo

Thin Stripe wrappers: Checkout, Billing Portal, prices, customers, subscriptions and webhook verification. Call Stripe::boot($secret) first. Requires the Stripe PHP SDK (composer: stripe/stripe-php).

A thin layer over the Stripe SDK, which has to be installed and booted with Stripe::boot($secret) before anything else. Checkout and the billing portal are hosted by Stripe, so card details never touch your server. Verify a webhook before you read it: an unverified POST is a stranger claiming a payment, and verifyWebhook() is what turns it into a fact.

stripepaymentscheckoutsubscriptionbillingwebhook
static

Stripe :: boot ($secret, $version = '2025-09-30.clover'):void

line 8
Initializes the Stripe API with the provided secret key and optionally sets the API version.
$secret = trim((string)$secret)
if (!$secret) error('Stripe secret key not configured', 500)
\Stripe\Stripe::setApiKey($secret)
$version && \Stripe\Stripe::setApiVersion($version)
static

Stripe :: price ($lookupKey)

line 15
Retrieves the price object from Stripe based on the provided lookup key, returning the first active price found or null if none exists.
$prices = \Stripe\Price::all(['lookup_keys' => [$lookupKey], 'limit' => 1, 'active' => true])
return $prices->data[0] ?? null
static

Stripe :: customer ($id)

line 20
Retrieves a Stripe customer by their ID, returning null if the customer is deleted or if an error occurs during the API call.
try {
	$c = \Stripe\Customer::retrieve((string)$id)
	return ($c->deleted ?? false) ? null : $c
}
catch (\Stripe\Exception\ApiErrorException $e){
	return null
}
static

Stripe :: createCustomer (array $data)

line 30
Creates a new customer in Stripe using the provided data.
\Stripe\Customer::create($data)
static

Stripe :: checkout (array $params)

line 32
Creates a new checkout session in Stripe, allowing for the processing of payments and managing customer interactions during the checkout process.
\Stripe\Checkout\Session::create($params)
static

Stripe :: portal ($customerId, $returnUrl)

line 34
Creates a new session for the Stripe Billing Portal, allowing a customer to manage their billing details and subscriptions.
\Stripe\BillingPortal\Session::create(['customer' => (string)$customerId, 'return_url' => (string)$returnUrl])
static

Stripe :: verifyWebhook ($payload, $sigHeader, $secret)

line 36
Verifies a Stripe webhook by constructing an event using the provided payload, signature header, and secret. It throws an error if the secret is not configured.
$secret = trim((string)$secret)
if (!$secret) error('Stripe webhook secret not configured', 500)
return \Stripe\Webhook::constructEvent((string)$payload, (string)$sigHeader, $secret)
static

Stripe :: subscriptions ($customerId, $status = 'all', $limit = 10)

line 42
Retrieves a list of subscriptions for a specified customer from Stripe, filtered by status and limited to a specified number of results.
\Stripe\Subscription::all(['customer' => (string)$customerId, 'status' => $status, 'limit' => $limit])
static

Stripe :: subscription ($id)

line 44
Retrieves a subscription object from Stripe using the specified subscription ID.
\Stripe\Subscription::retrieve((string)$id)
static

Stripe :: product ($id)

line 46
Retrieves a Stripe product by its ID.
\Stripe\Product::retrieve((string)$id)
object

%SumUp

/phlo/resources/payments/SumUp.phlo

SumUp connector: card-present checkouts on paired Solo readers via the Cloud API, transaction lookup and history

Cloud-initiated terminal payments: the POS creates a checkout for a paired reader over HTTPS, the reader wakes up with the amount, and the result arrives on the return_url webhook or by polling transaction(). A checkout must start on the device within 60 seconds and only one checkout can be active per reader at a time. Treat the webhook as a wake-up call, never as the verdict: re-fetch the transaction with transaction() before recording a payment, so a forged POST can never book money.

sumuppaymentsterminalreadercard-presentcheckoutconnector
const

SumUp :: section

line 10
'SumUp'
const

SumUp :: api

line 11
'https://api.sumup.com'
method

%SumUp -> headers:array

line 13
[static::bearer($this->config['api_key'] ?? void)]
static

SumUp :: fields:array

line 15
arr(
	section: 'SumUp',
	config: arr(
		merchant_code: 'Merchant code (profile > merchant profile)',
		reader_id: 'Default paired reader id (optional; every call accepts an explicit one)',
	),
	secret: arr(api_key: 'API key (developer.sumup.com > API keys)'),
)
method

%SumUp -> merchant:string

line 24
'v0.1/merchants/'.($this->config['merchant_code'] ?? void)
method

%SumUp -> readers:obj

line 29
The merchant's paired readers.
if ($m = $this->missing('merchant_code', 'api_key')) return $m
return $this->get($this->merchant.'/readers')
method

%SumUp -> reader (?string $readerId = null):string

line 34
(string)($readerId ?? $this->config['reader_id'] ?? void)
method

%SumUp -> createReaderCheckout (int $amountMinor, string $currency = 'EUR', ?string $readerId = null, ?string $description = null, ?string $returnUrl = null):obj

line 39
Start a card-present checkout on a reader. The amount is in minor units (cents).
if ($m = $this->missing('merchant_code', 'api_key')) return $m
$reader = $this->reader($readerId)
if ($reader === void) return static::fail('SumUp reader id not configured')
$body = ['total_amount' => ['value' => $amountMinor, 'currency' => $currency, 'minor_unit' => 2]]
if ($description !== null) $body['description'] = $description
if ($returnUrl !== null) $body['return_url'] = $returnUrl
return $this->post($this->merchant.'/readers/'.$reader.'/checkout', $body)
method

%SumUp -> terminateReaderCheckout (?string $readerId = null):obj

line 52
Stops the active checkout on a reader.
if ($m = $this->missing('merchant_code', 'api_key')) return $m
$reader = $this->reader($readerId)
if ($reader === void) return static::fail('SumUp reader id not configured')
return $this->post($this->merchant.'/readers/'.$reader.'/terminate')
method

%SumUp -> transaction (string $clientTransactionId):obj

line 62
Looks up a transaction by the client transaction id a checkout returned.
if ($m = $this->missing('merchant_code', 'api_key')) return $m
return $this->get('v0.1/me/transactions', ['client_transaction_id' => $clientTransactionId])
method

%SumUp -> transactions (array $query = []):obj

line 67
if ($m = $this->missing('merchant_code', 'api_key')) return $m
return $this->get('v0.1/me/transactions/history', $query)

Last updated on 4 November 2026

We use essential cookies to make this site work. With your permission we also use analytics to improve the site.