SUQODocs
User GuideAPI ReferenceSDKs

Quickstart

Mount the provider and open your first payment with the SUQO Flutter SDK.

1. Install

flutter pub add suqo_checkout

2. Mount the provider

Unlike the React Native SDK, the provider is optional — it holds defaults and nothing else. Mount it once, at your app root, to state them in one place:

import 'package:flutter/material.dart';
import 'package:suqo_checkout/suqo_checkout.dart';

void main() {
  runApp(
    const SuqoProvider(
      mode: SuqoMode.sandbox,
      child: MaterialApp(home: ShopScreen()),
    ),
  );
}

SuqoMode.live points at app.suqo.ai; the default, SuqoMode.sandbox, points at test.suqo.ai — so an app that names neither cannot accidentally take a live payment. Pass baseUrl to go somewhere else entirely (a staging mirror, a local mock server); it overrides mode when set. Every option can also be passed per call. See Configure the provider.

3. Create a checkout session on your backend

Your backend creates the session and passes its id to the app. The session must already have a customer assigned when you create it — this package has no way to attach one. See Create a checkout session.

4. Open a payment

Open a payment from anywhere with a BuildContext:

class PayButton extends StatelessWidget {
  const PayButton({
    super.key,
    required this.sessionId,
    required this.onPaid,
    required this.onError,
  });

  final String sessionId;
  final VoidCallback onPaid;
  final ValueChanged<String> onError;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async {
        final outcome = await SuqoCheckout.open(context, sessionId: sessionId);

        outcome.when(
          success: (s) => onPaid(),
          failure: (f) {
            if (f.status != SuqoFailureStatus.cancelled) {
              onError(f.message ?? 'Payment failed');
            }
          },
          closed: (_) {},
        );
      },
      child: const Text('Pay'),
    );
  }
}

open completes with exactly one outcome — SuqoSuccess, SuqoFailure or SuqoClosed. There are no onSuccess / onFailure / onClose callbacks, as the JavaScript SDKs have: the awaited outcome is the result. when takes one callback per case, so the compiler catches a case you forgot. See Handle outcomes.

5. On Android, declare your gateways' schemes

When a gateway hands off to a bank or wallet app, this package passes the deeplink to the OS. On Android 11+ (API 30) the OS will not open a scheme your app has not declared, and a package cannot add manifest entries for you. Add one <intent> per scheme your gateways use to android/app/src/main/AndroidManifest.xml, inside <manifest>:

<queries>
    <intent>
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="fonepayApp"/>
    </intent>
</queries>
Without it the hand-off fails silently. The buyer taps through to their bank and nothing happens; you see a handoff_failed event on onEvent. If a gateway sends an Android intent:// URI, declare the scheme named by its scheme= part (for example fonepayApp), not intent.

iOS needs no configuration for this — no LSApplicationQueriesSchemes entry.

There is nothing about the buyer here

The checkout session is created on your own backend with your API key, and it must already carry the buyer by the time you pass its id to open — their name, email and address never pass through this SDK, never reach the device, and never end up in a log.

open throws SuqoConfigError before anything is shown, for one of three reasons: an empty session id, a base URL that is not http(s), or a base URL and checkoutPath that together do not form a valid URL (for example https://a b.com:xy, with a non-numeric port). Catch it with on SuqoConfigError rather than matching its message.

There is no "a checkout is already open" guard. Two overlapping open calls push two routes and settle two independent sessions — if your UI can fire the same button twice, disable it while the future is pending.

What the buyer sees

A full-height screen slides in from the trailing edge with the SUQO payment block inside it: a back button, a title, and the block filling the rest. The block scrolls — a seller with NPS enabled lists every supported bank, which runs well past a phone screen.

It is pushed as an ordinary route on your root navigator, so Android's hardware back button works with nothing wired up. The header's back button, hardware back, or your own Navigator.of(context, rootNavigator: true).pop() all dismiss it and complete as SuqoClosed (see Handle outcomes).

When the buyer's bank needs its own app, the screen hands the deeplink to the OS and the bank app opens over yours. Coming back leaves the screen exactly as it was, mid-payment.

On this page