SUQODocs
User GuideAPI ReferenceSDKs

Create checkout sessions

Collecting a one-off payment, for up to ten billing periods or your own priced lines, with the SUQO PHP SDK.

$suqo->checkoutSessions opens a session that collects one payment for up to ten of your own billing periods, or for lines you price yourself. It is the route to a payment that is not a subscription. A subscription's first payment comes from subscriptions->create(), and its next from subscriptions->renew() — both of which hand back a checkout URL of their own.

Opening a session

use Suqo\Params\CheckoutItem;
use Suqo\Params\CreateCheckoutSessionParams;

$session = $suqo->checkoutSessions->create(new CreateCheckoutSessionParams(
    items: [CheckoutItem::billingPeriod('pbp_3n9k2x')],
    returnUrl: 'https://partner.example.com/orders/1234',
));

echo $session->checkoutUrl, PHP_EOL;   // send this to the buyer
echo $session->expiresAt, PHP_EOL;

Returns Suqo\Model\CheckoutSession — publicId (cks_…), expiresAt, checkoutUrl.

Throws ValidationError on 400 (an item, the customer or the return URL was rejected), RateLimitError on 429, AuthenticationError on 401, KycRequiredError / PermissionDeniedError on 403.

The two item shapes

An item is either an existing billing period or an inline, partner-priced line — never a mixture. The API rejects any inline field sent beside a pbp_id, because the plan behind a billing period sets the price, the cadence and the discount. CheckoutItem has one named constructor per shape, so the invalid combination cannot be expressed:

use Suqo\IntervalType;

CheckoutItem::billingPeriod('pbp_3n9k2x');

CheckoutItem::inline(
    name: 'Setup fee',
    amount: '2500.00',              // a decimal string, never a float
    intervalType: IntervalType::OneTime,
    intervalCount: 0,               // must be 0 for one_time
    discountAmount: '500.00',       // optional
);

intervalCount is bounded per unit: 1–1095 for day, 1–156 for week, 1–36 for month, 1–3 for year, and exactly 0 for one_time. intervalType accepts an IntervalType case or a raw string.

Each item's price is snapshotted when the session opens, so a plan edited afterwards cannot change what the buyer already saw. If two items resolve to the same product — through different pbp_ids, say — only the last one is kept.

The customer is optional

customerIdWhat happens
omitted or nullThe buyer identifies themselves by OTP at checkout.
cus_…Must already exist on your account — see Customers.

The SDK sends customer_id only when you set it.

This is the one rate-limited endpoint

20 sessions a minute per account by default. Over that, the API answers 429 and the SDK raises RateLimitError with retryAfter set from the response header:

use Suqo\Exception\RateLimitError;

try {
    $session = $suqo->checkoutSessions->create($params);
} catch (RateLimitError $e) {
    sleep((int) ceil($e->retryAfter ?? 1.0));
}

It is not retried for you. Creating a session is a write, and writes are never retried while there is no idempotency key — a blind resend would open a second session. Back off and resend yourself, deliberately.

Reading a session back

$detail = $suqo->checkoutSessions->read($session->publicId);

echo $detail->sellerDetails?->businessName ?? '-', PHP_EOL;
echo $detail->isExpired ? 'expired' : 'open', PHP_EOL;

Returns Suqo\Model\CheckoutSessionDetail — publicId, lineItems (kept exactly as it arrived — openapi describes no members), customerId (null when the buyer identified by OTP), sellerDetails (?CheckoutSeller: id, phone, businessLogo, businessName), returnUrl, expiresAt, completedAt (null while open), isExpired (?bool — openapi types it as a string; both spellings decode).

Only an open session is served. Once it is paid, or once it has expired, reading it answers 404 exactly as an unknown id does — see Handle errors for recovering the buyer's return_url from that 404.

See also

  • Manage subscriptions — subscriptions->renew() opens a session of its own, for a payment on an existing subscription.
  • Handle errors — the rate limit and the 404-on-expiry in full.

On this page