25: Integrations
Most apps eventually talk to something outside themselves: a chat service, a calendar, an accounting back-end, a card terminal, a payment processor. Phlo ships two families for this, both in the runtime, so there is no SDK to bolt on and no separate client to wire up. Connectors are thin, uniform wrappers around JSON REST APIs; the payment resources wrap Stripe and SumUp.
25.1: Connectors
A connector is a small class over one third-party REST API. Every connector extends Connector, reads its own credentials section, and returns the same result shape, so calling Slack looks like calling Shopify.
$slack = Slack::make()
$res = $slack->send('#general', 'Deploy finished')
if ($res->ok) log('sent') else log($res->error)
make() loads the connector's credentials from its section in data/creds.ini (Slack reads [Slack], Shopify reads [Shopify], and so on). Every call returns a normalized object, so you branch on ->ok and read ->data instead of parsing a different error format per service:
obj(ok: true, status: 200, data: {...}) // success
obj(ok: false, status: 401, error: '...') // failure
Under the hood Connector handles the request verbs (get/post/put/patch/del/form), pagination (paginate()) and auth headers. Each connector also exposes a static fields credential schema, so a settings screen or the CMS can configure it without hard-coding.
Available connectors
| Group | Connectors |
|---|---|
| Messaging | Slack, Telegram, Twilio, MessageBird, Resend |
| Cloud | GoogleCalendar, GoogleSheets, MicrosoftGraph |
| Finance | ExactOnline, Moneybird, EBoekhouden |
| Commerce | Lightspeed, Shopify |
Services that authenticate with OAuth2 (Google, Exact) extend OAuthConnector instead of Connector. It keeps an auto-refreshed bearer token in a TokenStore (persisted under data/tokens/, refreshed under an exclusive lock), so your code just calls ->events(...) and the token plumbing stays invisible.
Per-connector method lists live in the Manual under connectors. It is generated from the resource files, so it never drifts from the code.
25.2: Payments
Stripe
The Stripe resource is a thin layer over the official stripe/stripe-php SDK (add it with Composer). Call Stripe::boot() once with your secret key, then use the static helpers:
Stripe::boot(%creds->Stripe)
$session = Stripe::checkout([
'mode' => 'subscription',
'line_items' => [['price' => 'price_123', 'quantity' => 1]],
'success_url' => 'https://example.com/done',
'cancel_url' => 'https://example.com/pricing',
])
location($session->url)
checkout() creates a Checkout Session, portal($customerId, $returnUrl) opens the Billing Portal, and verifyWebhook($payload, $sigHeader, $secret) validates an incoming event before you act on it. customer, createCustomer, subscription, subscriptions, product and price cover the rest of the common surface.
SumUp
SumUp handles card-present payments on a paired SumUp Solo reader over the Cloud API. It is a connector (SumUp::make(), [SumUp] credentials), so it returns the same obj(ok, ...) result:
$res = SumUp::make()->createReaderCheckout(1250, 'EUR', description: 'Table 4')
createReaderCheckout($amountMinor, $currency, ...) pushes a checkout to the reader (the amount is in minor units, so 1250 is €12.50). transaction() and transactions() look up the result, and terminateReaderCheckout() cancels a pending one.