Skip to main content

Overview

The Yuno Flutter SDK provides a cross-platform payment integration that works on both iOS and Android from a single Dart codebase. It wraps the native iOS and Android SDKs, providing a Flutter-idiomatic API with widgets and streams.
The Flutter SDK maintains SAQ-A PCI compliance on both platforms. Card data never touches your servers.

Prerequisites

  • Flutter 3.16+
  • Dart 3.2+
  • iOS 14.0+ deployment target
  • Android minimum SDK: API 21 (Android 5.0)
  • Xcode 15+ (for iOS builds)
  • Android Studio (for Android builds)
  • Yuno API keys (Authentication)
  • At least one payment method enabled in your Dashboard

Installation

1

Add the dependency

Add yuno_sdk_flutter to your pubspec.yaml:
dependencies:
  yuno_sdk_flutter: ^1.5.0
Then run:
flutter pub get
2

iOS platform setup

Set the minimum iOS deployment target in ios/Podfile:
platform :ios, '14.0'
Install iOS dependencies:
cd ios && pod install && cd ..
If using Apple Pay, add the following to ios/Runner/Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
3

Android platform setup

Set the minimum SDK version in android/app/build.gradle.kts:
android {
    defaultConfig {
        minSdk = 21
    }
}
Add the Yuno Maven repository in android/build.gradle.kts:
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://yunopayments.jfrog.io/artifactory/snapshots") }
    }
}
If using Google Pay, add to android/app/src/main/AndroidManifest.xml:
<application>
    <meta-data
        android:name="com.google.android.gms.wallet.api.enabled"
        android:value="true" />
</application>

SDK Initialization

Initialize the SDK early in your app, typically in main.dart:
import 'package:flutter/material.dart';
import 'package:yuno_sdk_flutter/yuno_sdk_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await YunoSDK.initialize(
    config: YunoConfig(
      publicApiKey: 'your-public-api-key',
      environment: YunoEnvironment.sandbox, // Use .production for live
    ),
  );

  runApp(const MyApp());
}

Configuration options

ParameterTypeRequiredDescription
publicApiKeyStringYesYour Yuno public API key
environmentYunoEnvironmentYessandbox or production
languageYunoLanguageNoUI language. Defaults to device locale
cardFlowCardFlowNooneStep (default) or multiStep
Never embed your private secret key in the Flutter app. Use only the public API key for client-side initialization.

Full Checkout

Full Checkout renders all enabled payment methods with a single widget.
1

Create a checkout session (server-side)

Create a session from your backend:
// POST https://api-sandbox.y.uno/v1/checkout/sessions
{
  "amount": { "currency": "USD", "value": 50.00 },
  "country": "CO",
  "merchant_order_id": "order-123",
  "workflow": "SDK_CHECKOUT"
}
Pass the checkout_session token to your Flutter app.
2

Launch Full Checkout

import 'package:yuno_sdk_flutter/yuno_sdk_flutter.dart';

class CheckoutScreen extends StatefulWidget {
  final String checkoutSession;

  const CheckoutScreen({super.key, required this.checkoutSession});

  @override
  State<CheckoutScreen> createState() => _CheckoutScreenState();
}

class _CheckoutScreenState extends State<CheckoutScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Checkout')),
      body: Center(
        child: ElevatedButton(
          onPressed: _startCheckout,
          child: const Text('Pay Now'),
        ),
      ),
    );
  }

  Future<void> _startCheckout() async {
    final result = await YunoSDK.startFullCheckout(
      context: context,
      checkoutSession: widget.checkoutSession,
      countryCode: 'CO',
    );

    if (!mounted) return;

    switch (result.status) {
      case PaymentStatus.succeeded:
        _navigateToConfirmation(result.paymentId!);
      case PaymentStatus.failed:
        _showError(result.errorMessage ?? 'Payment failed');
      case PaymentStatus.processing:
        _showProcessingState();
      case PaymentStatus.cancelled:
        _showCancelledState();
    }
  }
}
3

Verify payment (server-side)

Always confirm the payment status from your backend:
// Server-side verification
// GET https://api-sandbox.y.uno/v1/payments/{payment_id}
Client-side callbacks indicate the UI outcome only. Always verify the final payment status server-side via webhooks or the GET Payment API.

Using the YunoCheckout widget

For declarative integration, use the YunoCheckout widget:
class CheckoutPage extends StatelessWidget {
  final String checkoutSession;

  const CheckoutPage({super.key, required this.checkoutSession});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Checkout')),
      body: YunoCheckout(
        checkoutSession: checkoutSession,
        countryCode: 'CO',
        onPaymentSuccess: (result) {
          Navigator.pushReplacementNamed(
            context,
            '/confirmation',
            arguments: result.paymentId,
          );
        },
        onPaymentError: (error) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text(error.message)),
          );
        },
        onPaymentProcessing: () {
          // Show processing indicator
        },
        onCancel: () {
          Navigator.pop(context);
        },
      ),
    );
  }
}

Seamless Checkout

Seamless Checkout gives you control over payment method selection while Yuno handles the payment form.
1

Retrieve available payment methods

final methods = await YunoSDK.getPaymentMethods(
  checkoutSession: sessionToken,
  countryCode: 'CO',
);

// Display methods in your custom UI
setState(() {
  paymentMethods = methods;
});
2

Launch Seamless Checkout for selected method

Future<void> _onMethodSelected(YunoPaymentMethod method) async {
  final result = await YunoSDK.startSeamlessCheckout(
    context: context,
    checkoutSession: sessionToken,
    paymentMethodType: method.type,
    countryCode: 'CO',
    onCreatePayment: (oneTimeToken, tokenData) async {
      // Create payment on your server
      final success = await PaymentApi.createPayment(oneTimeToken);
      if (success) {
        YunoSDK.continuePayment();
      }
    },
  );

  _handleResult(result);
}

Payment callbacks via streams

For reactive architectures, listen to the payment event stream:
class CheckoutBloc {
  late final StreamSubscription<YunoPaymentEvent> _subscription;

  void init() {
    _subscription = YunoSDK.paymentEvents.listen((event) {
      switch (event) {
        case PaymentSuccessEvent(paymentId: final id):
          _handleSuccess(id);
        case PaymentErrorEvent(error: final error):
          _handleError(error);
        case PaymentProcessingEvent():
          _handleProcessing();
        case PaymentCancelledEvent():
          _handleCancelled();
      }
    });
  }

  void dispose() {
    _subscription.cancel();
  }
}

3DS Handling

The SDK handles 3D Secure authentication automatically on both platforms. When a payment requires 3DS, the SDK presents the authentication challenge within the checkout flow. No additional code is required. The payment result includes the final outcome after 3DS completes.
Test 3DS flows in sandbox using Yuno’s test card numbers. Check the Testing guide for available test credentials.

Platform Channels for Native Features

Apple Pay (iOS only)

if (Platform.isIOS) {
  final result = await YunoSDK.startFullCheckout(
    context: context,
    checkoutSession: sessionToken,
    countryCode: 'US',
    applePay: YunoApplePayConfig(
      merchantId: 'merchant.com.yourapp.pay',
      countryCode: 'US',
    ),
  );
}

Google Pay (Android only)

if (Platform.isAndroid) {
  final result = await YunoSDK.startFullCheckout(
    context: context,
    checkoutSession: sessionToken,
    countryCode: 'US',
    googlePay: YunoGooglePayConfig(
      merchantName: 'Your Store Name',
      countryCode: 'US',
    ),
  );
}

Customization

Customize the checkout appearance to match your app’s design:
await YunoSDK.initialize(
  config: YunoConfig(
    publicApiKey: 'your-public-api-key',
    environment: YunoEnvironment.sandbox,
    appearance: YunoAppearance(
      primaryColor: const Color(0xFF6200EE),
      backgroundColor: Colors.white,
      textColor: Colors.black,
      cornerRadius: 12.0,
      fontFamily: 'Roboto',
      buttonStyle: YunoButtonStyle.rounded,
    ),
  ),
);
PropertyTypeDescription
primaryColorColorPrimary accent color for buttons and highlights
backgroundColorColorBackground color of the checkout sheet
textColorColorPrimary text color
cornerRadiusdoubleCorner radius for cards and buttons
fontFamilyStringFont family name
buttonStyleYunoButtonStylerounded or rectangular

Localization

The SDK supports automatic localization based on the device locale. Override with:
await YunoSDK.initialize(
  config: YunoConfig(
    publicApiKey: 'your-public-api-key',
    environment: YunoEnvironment.sandbox,
    language: YunoLanguage.spanish,
  ),
);
Supported languages: English, Spanish, Portuguese, Indonesian, Malay, Thai.

Error Handling

Future<void> _startCheckout() async {
  try {
    final result = await YunoSDK.startFullCheckout(
      context: context,
      checkoutSession: sessionToken,
      countryCode: 'CO',
    );

    _handleResult(result);
  } on YunoException catch (e) {
    switch (e.code) {
      case YunoErrorCode.networkError:
        _showRetryAlert('Check your internet connection');
      case YunoErrorCode.authenticationFailed:
        _showError('Invalid API key. Verify your configuration.');
      case YunoErrorCode.sessionExpired:
        _refreshSessionAndRetry();
      default:
        _showError(e.message);
    }
  }
}

Common error codes

CodeDescriptionResolution
networkErrorNetwork connectivity issueCheck device connectivity and retry
authenticationFailedInvalid public API keyVerify key in Dashboard > API Keys
sessionExpiredCheckout session has expiredCreate a new checkout session
invalidConfigurationSDK misconfigurationCheck initialization parameters
providerErrorPayment provider rejectedReview error details, check payment method

Testing in Sandbox

1

Use sandbox environment

Set environment: YunoEnvironment.sandbox during initialization.
2

Use test credentials

Use sandbox API keys from Dashboard > API Keys > Sandbox.
3

Test on both platforms

Run tests on both iOS simulator and Android emulator to verify platform-specific behavior:
flutter run -d ios
flutter run -d android
4

Test with Yuno test cards

Use Yuno-provided test card numbers to simulate different outcomes. See Testing.
Sandbox transactions use simulated providers. Some payment methods may have limited availability in sandbox compared to production.

Troubleshooting

SDK not initializing

  • Verify YunoSDK.initialize() is called before runApp() in main.dart
  • Ensure WidgetsFlutterBinding.ensureInitialized() is called first
  • Confirm the public API key is correct and matches your environment

iOS build fails

  • Verify platform :ios, '14.0' is set in ios/Podfile
  • Run cd ios && pod install --repo-update && cd ..
  • Clean the build: flutter clean && flutter pub get

Android build fails

  • Verify minSdk = 21 is set in android/app/build.gradle.kts
  • Ensure the Yuno Maven repository is added to project-level build.gradle.kts
  • Sync Gradle: cd android && ./gradlew clean && cd ..

Checkout not appearing

  • Ensure the checkout session token is valid and not expired
  • Verify context is from a mounted widget when calling startFullCheckout
  • Check that at least one payment method is enabled in Dashboard for the specified country

Platform-specific issues

  • Use flutter doctor to verify your development environment
  • Test on physical devices in addition to simulators/emulators
  • Check platform-specific logs:
    • iOS: Xcode console
    • Android: Logcat with tag filter YunoSDK

Hot reload limitations

SDK initialization state is preserved during hot reload but not hot restart. If checkout behavior is unexpected during development, perform a full restart:
flutter run --no-hot

Next steps

Mobile SDK Overview

Compare all mobile SDK options.

iOS SDK

Need native iOS integration instead?

Android SDK

Need native Android integration instead?

Testing

Test card numbers and sandbox setup.