Flutter SDK
Integrate Plexy with Flutter mobile applications
Flutter SDK
The official Plexy SDK for Flutter. Build a full checkout with Drop-in, or compose individual payment Components. Works on both iOS and Android, with the Sessions flow (recommended) or the Advanced flow.
Requirements
- Flutter 3.16 or later
- Dart 3.2 or later
- iOS 12 or later
- Android API level 21 (Android 5.0) or later
- Plexy Checkout API v2 or later
Installation
Add the package to your app:
flutter pub add plexy_checkoutOr add it manually to pubspec.yaml:
dependencies:
plexy_checkout: ^2.0.0Then run flutter pub get.
Platform Setup
Android
- Set
minSdkVersionto21inandroid/app/build.gradle:
android {
defaultConfig {
minSdkVersion 21
}
}-
Make sure your project uses Kotlin 1.8.22 or later and Android Gradle Plugin 8.1 or later.
-
Change your
MainActivityto extendFlutterFragmentActivity— the native Plexy Android SDK requires it:
// android/app/src/main/kotlin/.../MainActivity.kt
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity()iOS
- Set the iOS deployment target to 12.0 or later in
ios/Podfile:
platform :ios, '12.0'- Add a custom URL scheme for your return URL in
ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>com.yourcompany.yourapp</string>
</array>
</dict>
</array>- Forward the return URL to the SDK in
ios/Runner/AppDelegate.swift:
override func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
RedirectComponent.applicationDidOpen(from: url)
return true
}If you handle other URL schemes in the same AppDelegate, combine the results (e.g. return handledByOther || true) instead of falling through to super, which returns false.
Get the matching return URL at runtime with await PlexyCheckout.instance.getReturnUrl(). Pass this value to your backend when creating a session or payment.
Quick Start: Drop-in with Sessions Flow
The Sessions flow is the simplest way to accept payments. Your backend creates a session, your app renders Drop-in.
1. Create a session on your backend. Call POST /sessions on the Plexy Checkout API with the amount, country, and return URL, and return id and sessionData to your app.
2. Open Drop-in:
import 'package:plexy_checkout/plexy_checkout.dart';
Future<void> startCheckout({
required String sessionId,
required String sessionData,
}) async {
final configuration = DropInConfiguration(
environment: Environment.test,
clientKey: 'YOUR_CLIENT_KEY',
countryCode: 'KZ',
shopperLocale: 'en-US',
amount: Amount(currency: 'KZT', value: 10000),
cardConfiguration: const CardConfiguration(holderNameRequired: true),
);
final checkout = await PlexyCheckout.session.create(
sessionId: sessionId,
sessionData: sessionData,
configuration: configuration,
);
final result = await PlexyCheckout.session.startDropIn(
dropInConfiguration: configuration,
checkout: checkout,
);
handleResult(result);
}See Handling the payment result for handleResult.
Drop-in with Advanced Flow
Use the Advanced flow when you need to control each /payments and /payments/details call yourself.
final configuration = DropInConfiguration(
environment: Environment.test,
clientKey: 'YOUR_CLIENT_KEY',
countryCode: 'KZ',
amount: Amount(currency: 'KZT', value: 10000),
);
// 1. Fetch payment methods from your backend: POST /paymentMethods
final paymentMethods = await backend.fetchPaymentMethods();
// 2. Wire up callbacks that forward to your backend.
final advancedCheckout = AdvancedCheckout(
onSubmit: (data, [extra]) async {
// Backend calls POST /payments, returns the raw response.
final response = await backend.postPayments(data);
return Finished(resultCode: response['resultCode'] as String);
// Return Action(actionResponse: …) if the response requires 3DS/redirect.
},
onAdditionalDetails: (details) async {
final response = await backend.postPaymentsDetails(details);
return Finished(resultCode: response['resultCode'] as String);
},
);
final result = await PlexyCheckout.advanced.startDropIn(
dropInConfiguration: configuration,
paymentMethods: paymentMethods,
checkout: advancedCheckout,
);onSubmit and onAdditionalDetails both return a PaymentEvent:
Finished(resultCode)— terminal; Drop-in closes.Action(actionResponse)— Drop-in runs the additional action (3DS, redirect, QR).Update(paymentMethodsJson, orderJson)— refresh Drop-in (used for gift cards / partial payments).Error(errorMessage, reason, dismissDropIn)— show an error.
Configuring Drop-in
DropInConfiguration accepts per-method configs:
final configuration = DropInConfiguration(
environment: Environment.test,
clientKey: 'YOUR_CLIENT_KEY',
countryCode: 'KZ',
shopperLocale: 'en-US',
amount: Amount(currency: 'KZT', value: 10000),
cardConfiguration: CardConfiguration(
holderNameRequired: true,
addressMode: AddressMode.postalCode,
showStorePaymentField: true,
onBinLookup: (results) { /* co-branded cards, etc. */ },
onBinValue: (bin) { /* first digits of the PAN */ },
),
applePayConfiguration: ApplePayConfiguration(
merchantId: 'merchant.com.yourcompany.yourapp',
merchantName: 'Your Store',
),
googlePayConfiguration: const GooglePayConfiguration(
googlePayEnvironment: GooglePayEnvironment.test,
billingAddressRequired: true,
shippingAddressRequired: false,
),
paymentMethodNames: {'scheme': 'Credit card'},
);Also available on DropInConfiguration: cashAppPayConfiguration, storedPaymentMethodConfiguration (showing and deleting saved methods), and skipListWhenSinglePaymentMethod. See the plugin source for the full field list of each configuration.
Card Component
Render the Card Component as a Flutter widget inside your own screen. It works with either SessionCheckout or AdvancedCheckout.
final configuration = CardComponentConfiguration(
environment: Environment.test,
clientKey: 'YOUR_CLIENT_KEY',
countryCode: 'KZ',
shopperLocale: 'en-US',
cardConfiguration: const CardConfiguration(holderNameRequired: true),
);
final SessionCheckout checkout = await PlexyCheckout.session.create(
sessionId: sessionId,
sessionData: sessionData,
configuration: configuration,
);
// Find the "scheme" payment method inside the session response.
final paymentMethod = (checkout.paymentMethods['paymentMethods'] as List)
.firstWhere((pm) => pm['type'] == 'scheme') as Map<String, dynamic>;
// …in your widget tree:
PlexyCardComponent(
configuration: configuration,
paymentMethod: paymentMethod,
checkout: checkout,
onPaymentResult: (result) async {
Navigator.pop(context);
handleResult(result);
},
)For the Advanced flow, construct an AdvancedCheckout (as shown above) and pass it as checkout. Fetch the list of payment methods from POST /paymentMethods and extract the scheme entry the same way.
Apple Pay Component
final configuration = ApplePayComponentConfiguration(
environment: Environment.test,
clientKey: 'YOUR_CLIENT_KEY',
countryCode: 'KZ',
applePayConfiguration: ApplePayConfiguration(
merchantId: 'merchant.com.yourcompany.yourapp',
merchantName: 'Your Store',
),
);
PlexyApplePayComponent(
configuration: configuration,
paymentMethod: applePayPaymentMethod, // the "applepay" entry from /paymentMethods or the session
checkout: checkout, // SessionCheckout or AdvancedCheckout
onPaymentResult: handleResult,
)Requires the Apple Pay entitlement (Merchant IDs capability) on your iOS app.
Google Pay Component
final configuration = GooglePayComponentConfiguration(
environment: Environment.test,
clientKey: 'YOUR_CLIENT_KEY',
countryCode: 'KZ',
googlePayConfiguration: const GooglePayConfiguration(
googlePayEnvironment: GooglePayEnvironment.test,
),
);
PlexyGooglePayComponent(
configuration: configuration,
paymentMethod: googlePayPaymentMethod, // the "googlepay" entry from /paymentMethods or the session
checkout: checkout,
onPaymentResult: handleResult,
)Google Pay requires Play Services Wallet, which is bundled by the plugin — no extra Gradle changes needed.
Handling the Payment Result
PaymentResult is a sealed class. Switch on it:
void handleResult(PaymentResult result) {
switch (result) {
case PaymentSessionFinished(:final resultCode):
if (resultCode == ResultCode.authorised) {
// Success. Confirm on your backend via the webhook or GET /sessions/{id}.
} else {
// Refused, Cancelled, Error, etc.
}
case PaymentAdvancedFinished(:final resultCode):
// Same resultCode handling as above; confirm via webhook.
case PaymentCancelledByUser():
// Shopper dismissed the sheet.
case PaymentError(:final reason):
// reason is nullable; may be empty for user-visible errors.
}
}ResultCode values: authorised, refused, pending, cancelled, error, received, redirectShopper, identifyShopper, challengeShopper, presentToShopper, partiallyAuthorised, authenticationFinished, authenticationNotRequired, unknown.
Never treat the client-side resultCode as a source of truth for "payment completed" — always confirm on the server via webhooks or a GET /sessions/{id} poll.
Return URL
Pass the value returned by PlexyCheckout.instance.getReturnUrl() into the returnUrl field of your POST /sessions or POST /payments request. On iOS this value matches the URL scheme you registered in Info.plist; on Android the plugin uses an auto-generated scheme.
final returnUrl = await PlexyCheckout.instance.getReturnUrl();Debugging
Enable plugin console logging in debug builds:
PlexyCheckout.instance.enableConsoleLogging(enabled: true);The call is a no-op in release builds.