---
title: Flutter Mobile SDKs
slug: sdks/mobile-sdks/flutter
excerpt: >-
  Integrate Pine Labs payments into your mobile application using our Flutter
  Native SDK.
hidden: false
sidebar_order: 99
sidebar_label: Flutter
metadata:
  title: Flutter Mobile SDKs for Secure Payment Integration | Pine Labs
  description: >-
    Integrate secure mobile payments in Flutter apps using Pine Labs Flutter
    Mobile SDKs with seamless checkout and payment verification support.
  keywords: 'Flutter Mobile SDKs, Mobile SDKs, Online Payment SDKs'
---
## Integration Steps

Learn how you can integrate with Pine Labs native SDK across all platforms. The SDK provides a secure hosted checkout experience using backend APIs and client-side SDKs for seamless payment processing.

1. <a href ="#prerequisites" >Prerequisites</a>
2. <a href ="#integrate-apis-in-your-backend" >Integrate APIs in Your Backend</a>
3. <a href ="#sdk-installation--initialization" >SDK Installation and Initialization</a>
4. <a href ="#handle-payments" >Handle Payments</a>
5. <a href ="#manage-transactions" >Manage Transactions</a>

---

> ### ❗️ Security Best Practices:
>
> - Ensure you store your `client_id` and `client_secret` in your Backend securely.
> - Integrate our APIs on your backend system.
> - We strictly recommend not to call our APIs from the frontend.
> - Failure to adhere to the above guidelines may result in legal implications. In such cases, you will be held responsible for any damage or loss arising from non-compliance.

---

<h2 id="prerequisites">1. Prerequisites </h2>

Before integrating the SDK, ensure your Flutter project meets the following requirements:
| Requirement | Version                             |
| ----------- | ----------------------------------- |
| Flutter     | 3.3.0 or later                      |
| Dart SDK    | 3.11.1 or later                     |
| Android     | Android 8.0+, API level 26 or later |
| iOS         | iOS 13.0 or later                   |

### Minimum Compatibility Matrix
#### Platform and Build Environment
| Category              | Minimum Supported    | Notes                                 |
| --------------------- | -------------------- | ------------------------------------- |
| Flutter Version       | 3.3.0                | Apps must use Flutter 3.3.0 or higher |
| Dart SDK              | 3.11.1               | Required for null-safe SDK APIs       |
| Android minSdk        | API 26 (Android 8.0) | Required by the Pine Labs Android SDK |
| Android compileSdk    | API 34+              | Recommended to use latest stable      |
| iOS Deployment Target | iOS 13.0             | Required by the Pine Labs iOS SDK     |
| Xcode Version         | Xcode 15.0+          | Required to build iOS 13+ apps        |
| Swift Version         | 5.0+                 | Required for the iOS plugin           |
| Kotlin                | 2.x+                 | Used in the Android plugin            |
| Java Compatibility    | 17                   | Required for Android compilation      |

#### Supported Platforms
| Platform               | Supported       |
| ---------------------- | --------------- |
| Android (phone/tablet) | ✅               |
| iOS (iPhone/iPad)      | ✅               |
| Web                    | ❌ Not supported |
| macOS Desktop          | ❌ Not supported |
| Windows Desktop        | ❌ Not supported |
| Linux Desktop          | ❌ Not supported |

#### Native SDK Dependencies
The following packages are included automatically as transitive dependencies. You do not need to add them manually to your app.

| Platform | Dependency                                        | Version | Source             |
| -------- | ------------------------------------------------- | ------- | ------------------ |
| Android  | `com.github.plural-pinelabs:Pinelabs-Android-SDK` | 1.1.6   | JitPack            |
| iOS      | `PineLabsOnline_IOS_SDK.xcframework`              | Bundled | Vendored in plugin |

#### Flutter Dependencies

| Package                     | Version | Purpose                      |
| --------------------------- | ------- | ---------------------------- |
| `plugin_platform_interface` | ^2.0.2  | Platform channel abstraction |

#### Unsupported Configurations
The SDK does not support the following configurations:

- Flutter versions below 3.3.0
- Dart SDK versions below 3.11.1
- Android apps with minSdk below 26
- iOS apps targeting below iOS 13.0
- Web, Desktop (macOS, Windows, Linux) platforms

<h2 id="integrate-apis-in-your-backend"> 2. Integrate APIs in Your Backend </h2>

Start a payment by triggering the payment flow. To start a payment follow the below steps:

### 2.1. Generate Auth Token

Integrate our Generate Token API in your backend servers to generate the auth token. Use the token generated to authenticate Pine Labs Online APIs.

Below are the sample requests and response for the Generate Token API.

```curl cURL – UAT
curl --location 'https://pluraluat.v2.pinepg.in/api/auth/v1/token' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--data '
{
  "client_id": "a17ce30e-f88e-4f81-ada1-c3b4909ed232",
  "client_secret": "fgwei7egyhuggwp39w8rh",
  "grant_type": "client_credentials"
}
'
```
```curl cURL – PROD
curl --location 'https://api.pluralpay.in/api/auth/v1/token' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--data '
{
  "client_id": "a17ce30e-f88e-4f81-ada1-c3b4909ed232",
  "client_secret": "fgwei7egyhuggwp39w8rh",
  "grant_type": "client_credentials"
}
'
```
```json Sample Response
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
  "expires_in": 3600
}
```


Refer to our [Generate Token API](/api/authentication/generate-token) documentation to learn more.

### 2.2. Generate Checkout Link

Use this API to Generate a checkout link, for authentication use the generated access token in the headers of the API request.

Below are the sample requests and response for a Generate Checkout Link API.

```curl cURL - UAT
curl --location 'https://pluraluat.v2.pinepg.in/api/checkout/v1/orders' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
--header 'Content-Type: application/json' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--header 'accept: application/json' \
--data '
{
  "merchant_order_reference": "f4c45dbd-6eba-453d-b317-158c6ba12825",
  "order_amount": {
    "value": 500,
    "currency": "INR"
  },
  "purchase_details": {
    "customer": {
      "email_id": "joe.sam@gmail.com",
      "first_name": "joe",
      "last_name": "kumar",
      "customer_id": "192212",
      "mobile_number": "192192883",
      "country_code": "91",
      "billing_address": {
        "address1": "H.No 15, Sector 17",
        "address2": "",
        "address3": "",
        "pincode": "61232112",
        "city": "CHANDIGARH",
        "state": "PUNJAB",
        "country": "INDIA"
      },
      "shipping_address": {
        "address1": "H.No 15, Sector 17",
        "address2": "",
        "address3": "",
        "pincode": "144001123",
        "city": "CHANDIGARH",
        "state": "PUNJAB",
        "country": "INDIA"
      }
    },
    "merchant_metadata": {
      "key1": "value1",
      "key2": "value2"
    },
    "integration_mode": "SDK"
  }
}
'
```
```curl cURL - PROD
curl --location 'https://plural.v2.pinepg.in/api/checkout/v1/orders' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
--header 'Content-Type: application/json' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--header 'accept: application/json' \
--data '
{
  "merchant_order_reference": "f4c45dbd-6eba-453d-b317-158c6ba12825",
  "order_amount": {
    "value": 500,
    "currency": "INR"
  },
  "purchase_details": {
    "customer": {
      "email_id": "joe.sam@gmail.com",
      "first_name": "joe",
      "last_name": "kumar",
      "customer_id": "192212",
      "mobile_number": "192192883",
      "country_code": "91",
      "billing_address": {
        "address1": "H.No 15, Sector 17",
        "address2": "",
        "address3": "",
        "pincode": "61232112",
        "city": "CHANDIGARH",
        "state": "PUNJAB",
        "country": "INDIA"
      },
      "shipping_address": {
        "address1": "H.No 15, Sector 17",
        "address2": "",
        "address3": "",
        "pincode": "144001123",
        "city": "CHANDIGARH",
        "state": "PUNJAB",
        "country": "INDIA"
      }
    },
    "merchant_metadata": {
      "key1": "value1",
      "key2": "value2"
    },
    "integration_mode": "SDK"
  }
}
'
```
```json Sample Response
{
  "token": "REDIRECT TOKEN",
  "order_id": "ORDER ID",
  "redirect_url": "https://api.pluralonline.com/api/v3/checkout-bff/redirect/checkout?token=REDIRECT TOKEN",
  "response_code": 200,
  "response_message": "Order Creation Successful."
}
```

Refer to our [Generate Checkout Link](/api/checkout/generate-checkout-link) documentation to learn more.

<h2 id="sdk-installation--initialization"> 3. SDK Installation & Initialization </h2>

### Installation

#### Add the SDK Dependency

Open your Flutter project’s `pubspec.yaml` file and add `pinelabs_native` under `dependencies`.

```YAML
dependencies:
  flutter:
    sdk: flutter
  pinelabs_native: ^1.0.0
```

Then run:

```Bash
flutter pub get
```

This downloads the SDK and all required dependencies automatically.

### Android Configuration
The SDK requires **Android 8.0 or later**, which corresponds to **API level 26**.

#### Set Minimum SDK Version
Open your app’s `android/app/build.gradle.kts` or `android/app/build.gradle` file and ensure `minSdk` is set to `26` or higher:

```Kotlin
android {
    defaultConfig {
        minSdk = 26
        // ...
    }
}
```


> 📘 Note:
> 
> - If your app already targets `minSdk 26` or higher, no changes are needed. If your current `minSdk` is lower than 26, you must update it to 26.

### Add JitPack Repository
The Android component of the SDK depends on the Pine Labs Android SDK hosted on JitPack.

Open your project-level:

```
settings.gradle.kts
```

and ensure JitPack is added under repositories.

```Kotlin
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven("https://jitpack.io")
    }
}
```

For older Gradle projects that use `allprojects` in the root `build.gradle`, add JitPack there instead.

```gradle
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}
```

#### Add Internet Permission
The SDK requires internet access. Most Flutter apps already include this permission, but verify that your `android/app/src/main/AndroidManifest.xml` file contains:

```XML
<uses-permission android:name="android.permission.INTERNET" />
```


> 📘 Note:
> 
> - The native Android SDK handles UPI intent schemes and payment app queries internally.


### iOS Configuration
The SDK requires **iOS 13.0 or later**.

#### Set iOS Deployment Target
Open your `ios/Podfile` and ensure the platform version is set to `13.0` or higher:

```Ruby
platform :ios, '13.0'
```

```Bash
cd ios && pod install && cd ..
```

#### Add UPI Query Schemes in Info.plist
To allow the SDK to detect and launch UPI payment apps installed on the user’s device, declare the required URL schemes in your app’s `Info.plist`.

Open `ios/Runner/Info.plist` and add:

```XML
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>bhim</string>
    <string>cred</string>
    <string>credpay</string>
    <string>tez</string>
    <string>paytmmp</string>
    <string>phonepe</string>
    <string>kiwi</string>
    <string>mobikwik</string>
</array>
```

Starting from iOS 9, apps must declare the URL schemes they intend to query using `LSApplicationQueriesSchemes`.

Without these entries, the SDK will not be able to detect installed UPI apps, and payment app redirection may fail silently.


### Initialize the SDK
Import the SDK in your Dart file:

```dart
import 'package:pinelabs_native/pinelabs_native.dart';
```

Create an SDK instance:

```dart
final sdk = const PinelabsFlutterSdk();
```

The SDK instance is lightweight and can be created as `const`. You can create it once and reuse it across multiple payment calls.

#### Start a Payment
Use the `startPayment` method to initiate a payment.

Your backend creates an order using the **Generate Checkout Link API** and receives a `token` in the response. Pass this token directly to the SDK.

```dart
Future<void> startPayment(String orderToken) async {
  final sdk = const PinelabsFlutterSdk();

  final result = await sdk.startPayment(
    PinelabsPaymentRequest(
      orderToken: orderToken,
      environment: PinelabsEnvironment.uat, // Use .prod for production
    ),
  );

  if (result.isSuccess) {
    // Payment was successful
    print('Order ID: ${result.orderId}');
  } else {
    // Payment failed, was cancelled, or user pressed back
    print('Status: ${result.status.name}');
    print('Message: ${result.message}');
  }
}
```

**Parameters**
| Parameter     | Type                  | Required | Description                                                                                                                                                |
| ------------- | --------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Token`  | `String`              | Yes      | The token returned in the response of our Generate Checkout Link API. This token authenticates and identifies the payment session. |
| `environment` | `PinelabsEnvironment` | Yes      | The SDK environment. Use `PinelabsEnvironment.uat` for testing and `PinelabsEnvironment.prod` for production.                                              |

#### Extract the Token

You will get Token in the response of the Generate Checkout Link api, use this token to initiate the SDK.

Example `token`:

```
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "order_id": "v1-5757575757-aa-hU1rUd",
  "redirect_url": "https://api.pluralonline.com/api/v3/checkout-bff/redirect/checkout?token=REDIRECT_TOKEN",
  "response_code": 200,
  "response_message": "Order Creation Successful."
}
```

In this case, the `Token` is:

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

#### Complete Flutter Integration Example
Below is an example showing how to integrate the SDK in a Flutter app:

```dart
import 'package:flutter/material.dart';
import 'package:pinelabs_native/pinelabs_native.dart';

class CheckoutScreen extends StatefulWidget {
  const CheckoutScreen({super.key});

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

class _CheckoutScreenState extends State<CheckoutScreen> {
  final _sdk = const PinelabsFlutterSdk();
  final _tokenController = TextEditingController();

  bool _isProcessing = false;
  PinelabsPaymentResult? _lastResult;

  @override
  void dispose() {
    _tokenController.dispose();
    super.dispose();
  }

  Future<void> _startPayment() async {
    final token = _tokenController.text.trim();

    if (token.isEmpty) return;

    setState(() => _isProcessing = true);

    try {
      final result = await _sdk.startPayment(
        PinelabsPaymentRequest(
          orderToken: token,
          environment: PinelabsEnvironment.uat, // Use .prod for production
        ),
      );

      if (!mounted) return;

      setState(() => _lastResult = result);

      switch (result.status) {
        case PinelabsPaymentStatus.success:
          _showMessage('Payment Successful! Order: ${result.orderId}');

          // Important:
          // Verify the payment on your backend using the Order Inquiry API
          // before fulfilling the order.
          break;

        case PinelabsPaymentStatus.failure:
          _showMessage('Payment Failed: ${result.message}');
          break;

        case PinelabsPaymentStatus.cancelled:
          _showMessage('Payment Cancelled: ${result.message}');
          break;

        case PinelabsPaymentStatus.backPressed:
          _showMessage('User pressed back');
          break;
      }
    } catch (e) {
      if (!mounted) return;
      _showMessage('Error: $e');
    } finally {
      if (mounted) {
        setState(() => _isProcessing = false);
      }
    }
  }

  void _showMessage(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Checkout'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            TextField(
              controller: _tokenController,
              decoration: const InputDecoration(
                labelText: 'Order Token',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 16),
            FilledButton(
              onPressed: _isProcessing ? null : _startPayment,
              child: Text(_isProcessing ? 'Processing...' : 'Pay Now'),
            ),
          ],
        ),
      ),
    );
  }
}
```

> 📘 Important:
> 
> - Always confirm the final payment status from your backend by calling the Get Order by Order ID API with the order ID. Do not use the client-side SDK response alone to fulfil or complete an order.

<h2 id="handle-payments">4. Handle Payments </h2>

You need to implement call-back methods to handle your payment responses. This will provide the payment status and reason for transaction failures. Based on the reasons for failures, handling can be built at your end. Transaction callbacks can be listened to via overriding methods of EdgeResponseCallback.

### PaymentResult
Both SDK payment methods return a PaymentResult object when the payment flow ends.

```
class PinelabsPaymentResult {
  final PinelabsPaymentStatus status;  // Payment outcome status
  final String? orderId;               // Order ID from the payment gateway
  final String? code;                  // Response code from the gateway
  final String? message;               // Descriptive message from the gateway

  bool get isSuccess;                  // Convenience getter: true if status == success
}
```

### PaymentStatus
The SDK returns one of the following standard status values:
| Status                              | Value         | Description                                                                                         |
| ----------------------------------- | ------------- | --------------------------------------------------------------------------------------------------- |
| `PinelabsPaymentStatus.success`     | `success`     | Payment completed successfully. The order ID is available in `result.orderId`.                      |
| `PinelabsPaymentStatus.failure`     | `failure`     | Payment failed due to bank decline, timeout, or other error. Check `result.message` for details.    |
| `PinelabsPaymentStatus.cancelled`   | `cancelled`   | User cancelled the payment from within the native payment UI.                                       |
| `PinelabsPaymentStatus.backPressed` | `backPressed` | User pressed the system back button (Android) or swipe-to-dismiss (iOS) to exit the payment screen. |

### Handling Payment Outcomes
Use the status field in PaymentResult to handle each payment outcome.

```dart
final result = await sdk.startPayment(
  PinelabsPaymentRequest(
    orderToken: orderToken,
    environment: PinelabsEnvironment.uat,
  ),
);

switch (result.status) {
  case PinelabsPaymentStatus.success:
    // Payment succeeded.
    // result.orderId contains the gateway order ID.
    // ALWAYS call your backend Order Inquiry API to confirm.
    break;

  case PinelabsPaymentStatus.failure:
    // Payment failed.
    // result.code contains the error code from the gateway.
    // result.message contains a human-readable error description.
    // Show an appropriate message and offer retry.
    break;

  case PinelabsPaymentStatus.cancelled:
    // User cancelled the payment within the payment UI.
    // result.message describes the cancellation reason.
    // Offer the user an option to retry.
    break;

  case PinelabsPaymentStatus.backPressed:
    // User pressed the back button to exit the payment screen.
    // result.message contains a description of the action.
    // Offer the user an option to retry or go back.
    break;
}
```

#### UPI and External App Handling

The native SDKs handle all UPI intent routing internally. When the payment flow requires opening an external UPI app (GPay, PhonePe, Paytm, CRED, etc.):

- The native SDK detects and launches the appropriate UPI app.
- When the user completes or cancels the payment in the external app, the native SDK receives the callback.
- The result is returned to Flutter automatically.

**No additional code is required in your Flutter app**. UPI app handling is fully managed by the native SDK.

### Error Handling

#### PlatformException

If the native SDK encounters an internal error before it can return a structured result, the `startPayment` call will throw a `PlatformException`. Always wrap your SDK call in a try-catch:

```dart
try {
  final result = await sdk.startPayment(request);
  // Handle result...
} on PlatformException catch (e) {
  // Native SDK error
  print('Error code: ${e.code}');
  print('Error message: ${e.message}');
} catch (e) {
  // Unexpected error
  print('Unexpected error: $e');
}
```

#### Common Error Codes

| Code                            | Description                                         | Resolution                                                                                  |
| ------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `INVALID_ARGS`                  | Arguments are missing or malformed.                 | Ensure you are passing a valid `PinelabsPaymentRequest`.                                    |
| `INVALID_TOKEN`                 | The `orderToken` is empty or null.                  | Ensure the order token is not empty. Check your Create Hosted Checkout API response.        |
| `INVALID_ENVIRONMENT`           | The `environment` value is not recognized.          | Use only `PinelabsEnvironment.uat` or `PinelabsEnvironment.prod`.                           |
| `NO_ACTIVITY` (Android)         | No Android activity is available to launch the SDK. | Ensure you are calling `startPayment` from a widget that is attached to a running activity. |
| `NO_VIEW_CONTROLLER` (iOS)      | No root view controller is available.               | Ensure you are calling `startPayment` after the app UI is fully loaded.                     |
| `PAYMENT_IN_PROGRESS` (Android) | Another payment is already in progress.             | Wait for the current `startPayment` call to complete before starting a new one.             |
| `NULL_RESPONSE`                 | Native SDK returned no response.                    | This is unexpected. Retry the payment or contact support.                                   |

<h2 id="manage-transactions">5. Manage Transactions </h2>

Track and verify transaction status using Pine Labs APIs. To retrieve the latest status, use the Fetch APIs or subscribe to webhooks for real-time transaction updates.

<div className="not-prose card-grid-2">
  <div className="card-grid-item">
    <h3>Get Order by Order ID</h3>
    <p>Fetch real-time transaction status by order ID.</p>
    <p>[Learn More →](/api/orders/get-order-by-id)</p>
  </div>
  <div className="card-grid-item">
    <h3> Webhooks </h3>
    <p>Configure webhook events for automatic transaction updates.</p>
    <p>[Learn More →](/developer-tools/webhooks)</p>
  </div>
</div>
