---
title: Flutter Web SDKs
slug: sdks/web-sdks/flutter
excerpt: >-
  Integrate Pine Labs payments into your web application using our Flutter Web
  SDK.
hidden: false
metadata:
  robots: index
  title: Flutter Web SDKs for Secure Online Payments | Pine Labs
  description: >-
    Integrate secure online payments in Flutter web applications using Pine Labs
    Flutter Web SDKs with seamless checkout and payment verification support.
sidebar_order: 3
sidebar_label: Flutter
---
## Integration Steps

Learn how you can integrate with Pine Labs Web 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 Flutter SDK, make sure your app and build environment meet the minimum requirements listed below.

### Minimum Compatibility Matrix
#### Platform and Build Environment
| Category              | Minimum Supported    | Notes                                            |
| --------------------- | -------------------- | ------------------------------------------------ |
| Flutter Version       | 3.24.0               | Apps must use Flutter 3.24.0 or higher.          |
| Dart SDK              | 3.9.2                | Required for null-safe SDK APIs.                 |
| Android minSdk        | API 26 / Android 8.0 | Required by WebView and UPI intent dependencies. |
| Android compileSdk    | API 34+              | Recommended to use the latest stable version.    |
| iOS Deployment Target | iOS 13.0             | Required for WKWebView features used by the SDK. |
| Xcode Version         | Xcode 15.0+          | Required to build iOS 13+ apps.                  |

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

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

| Package                     | Version   | Purpose                         |
| --------------------------- | --------- | ------------------------------- |
| `webview_flutter`           | `^4.13.0` | Core WebView widget.            |
| `webview_flutter_android`   | `^4.5.0`  | Android WebView implementation. |
| `webview_flutter_wkwebview` | `^3.23.0` | iOS WKWebView implementation.   |
| `url_launcher`              | `^6.3.2`  | Launching external UPI apps.    |

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

- Flutter versions below `3.24.0`
- Dart SDK versions below `3.9.2`
- Android apps with `minSdk` below API `26`
- iOS apps targeting versions below iOS `13.0`
- Web apps
- Desktop apps, including macOS, Windows, and Linux

<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 --request POST \
     --url https://pluraluat.v2.pinepg.in/api/checkout/v1/orders \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "merchant_order_reference": "112345",
  "order_amount": {
    "value": 1100,
    "currency": "INR"
  },
  "integration_mode": "SDK",
  "pre_auth": false,
  "allowed_payment_methods": [
    "CARD",
    "UPI",
    "NETBANKING",
    "POINTS",
    "WALLET"
  ],
  "notes": "order1",
  "callback_url": "https://sample-callback-url",
  "failure_callback_url": "https://sample-failure-callback-url",
  "purchase_details": {
    "customer": {
      "email_id": "kevin.bob@example.com",
      "first_name": "Kevin",
      "last_name": "Bob",
      "customer_id": "123456",
      "mobile_number": "9876543210",
			"country_code": "91",
      "billing_address": {
        "address1": "10 Downing Street Westminster London",
        "address2": "Oxford Street Westminster London",
        "address3": "Baker Street Westminster London",
        "pincode": "51524036",
        "city": "Westminster",
        "state": "Westminster",
        "country": "London",
        "full_name": "harsh kumar",
        "address_type": "HOME/WORK/OTHER",
        "address_category": "BILLING"
      },
      "shipping_address": {
        "address1": "10 Downing Street Westminster London",
        "address2": "Oxford Street Westminster London",
        "address3": "Baker Street Westminster London",
        "pincode": "51524036",
        "city": "Westminster",
        "state": "Westminster",
        "country": "London",
        "full_name": "harsh kumar",
        "address_type": "HOME/WORK/OTHER",
        "address_category": "BILLING"
      }
    },
    "merchant_metadata": {
      "express_checkout_enabled": "TRUE",
      "express_checkout_allowed_action": "checkoutCollectAddress",
      "key1": "DD",
      "key2": "XOF"
    }
  },
  "cart_details": {
    "cart_items": [
      {
        "item_id": "cart_id_1",
        "item_name": "T Shirt",
        "item_description": "Test Description",
        "item_details_url": "https://chriscross.in/cdn/shop/files/95_800x.jpg",
        "item_image_url": "https://chriscross.in/cdn/shop/files/95_800x.jpg",
        "item_original_unit_price": 1,
        "item_discounted_unit_price": 1,
        "item_quantity": 1,
        "item_currency": "INR"
      }
    ]
  }
}
'
```
```curl cURL - PROD
curl --request POST \
     --url https://plural.v2.pinepg.in/api/checkout/v1/orders \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "merchant_order_reference": "112345",
  "order_amount": {
    "value": 1100,
    "currency": "INR"
  },
  "integration_mode": "SDK",
  "pre_auth": false,
  "allowed_payment_methods": [
    "CARD",
    "UPI",
    "NETBANKING",
    "POINTS",
    "WALLET"
  ],
  "notes": "order1",
  "callback_url": "https://sample-callback-url",
  "failure_callback_url": "https://sample-failure-callback-url",
  "purchase_details": {
    "customer": {
      "email_id": "kevin.bob@example.com",
      "first_name": "Kevin",
      "last_name": "Bob",
      "customer_id": "123456",
      "mobile_number": "9876543210",
			"country_code": "91",
      "billing_address": {
        "address1": "10 Downing Street Westminster London",
        "address2": "Oxford Street Westminster London",
        "address3": "Baker Street Westminster London",
        "pincode": "51524036",
        "city": "Westminster",
        "state": "Westminster",
        "country": "London",
        "full_name": "harsh kumar",
        "address_type": "HOME/WORK/OTHER",
        "address_category": "BILLING"
      },
      "shipping_address": {
        "address1": "10 Downing Street Westminster London",
        "address2": "Oxford Street Westminster London",
        "address3": "Baker Street Westminster London",
        "pincode": "51524036",
        "city": "Westminster",
        "state": "Westminster",
        "country": "London",
        "full_name": "harsh kumar",
        "address_type": "HOME/WORK/OTHER",
        "address_category": "BILLING"
      }
    },
    "merchant_metadata": {
      "express_checkout_enabled": "TRUE",
      "express_checkout_allowed_action": "checkoutCollectAddress",
      "key1": "DD",
      "key2": "XOF"
    }
  },
  "cart_details": {
    "cart_items": [
      {
        "item_id": "cart_id_1",
        "item_name": "T Shirt",
        "item_description": "Test Description",
        "item_details_url": "https://chriscross.in/cdn/shop/files/95_800x.jpg",
        "item_image_url": "https://chriscross.in/cdn/shop/files/95_800x.jpg",
        "item_original_unit_price": 1,
        "item_discounted_unit_price": 1,
        "item_quantity": 1,
        "item_currency": "INR"
      }
    ]
  }
}
'
```
```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 to Your Flutter Project

Before integrating the SDK, ensure your project meets the following requirements:

| Requirement | Version |
| --- | --- |
| Flutter | `3.24.0` or later |
| Dart SDK | `3.9.2` or later |

Add `pine_payment_sdk` to your app’s `pubspec.yaml` file under `dependencies`:

```YAML
dependencies:
  flutter:
    sdk: flutter
  pine_payment_sdk: ^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 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" />
```

#### UPI App Queries
The SDK includes built-in manifest `<queries>` entries for common UPI apps such as Google Pay, PhonePe, Paytm, and others.

No additional configuration is required for standard UPI payments.

If your app uses a custom deep-link scheme for payment callbacks, add an `<intent-filter>` to your `AndroidManifest.xml`:

```XML
<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="yourapp" android:host="payment" />
    </intent-filter>
</activity>
```

Replace `yourapp` and `payment` with your actual callback scheme and host.

### 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>upi</string>
    <string>phonepe</string>
    <string>gpay</string>
    <string>paytm</string>
    <string>tez</string>
    <string>credpay</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.

#### Add Custom Callback Scheme
If your app uses a custom callback scheme, such as:

```
yourapp://payment
```

add a CFBundleURLTypes entry in `Info.plist`:

```XML
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>yourapp</string>
        </array>
    </dict>
</array>
```

Replace `yourapp` with your app’s actual callback scheme.

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

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

The SDK provides two methods to start a payment flow, depending on how your payment gateway handles callbacks.

#### Pass the Redirect URL to SDK

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

```dart
Future<void> startPayment(String redirectUrl) async {
  final PaymentResult result = await PinePaymentSdk.startPaymentFromRedirectUrl(
        redirectUrl: redirectUrl,
        appBarTitle: 'Complete Payment',
      );

  if (result.success) {
    // Payment was successful
    print('Transaction ID: ${result.transactionId}');
  } else {
    // Payment failed or was cancelled
    print('Status: ${result.status}');
    print('Message: ${result.message}');
  }
}
```

**Parameters**
| Parameter     | Type   | Required | Description                                                                                                                                                                                               |
| ------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `redirectUrl` | String | Yes      | The callback URL the payment gateway redirects to after payment completion. For Plural checkout, this is the same as `paymentUrl`. The SDK monitors this URL to detect when the payment flow is complete. |
| `appBarTitle` | String | No       | Title displayed in the app bar at the top of the payment screen. Defaults to `Complete Payment`.                                                                                                          |

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

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

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

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

class _CheckoutScreenState extends State<CheckoutScreen> {
  bool _isProcessing = false;

  Future<void> _startPayment() async {
    // The redirect URL from your Create Hosted Checkout API response
    const redirectUrl = 'https://pluraluat.v2.pinepg.in/api/v3/checkout-bff/redirect/checkout?token=YOUR_TOKEN';

    setState(() => _isProcessing = true);

    try {
      final result = await PinePaymentSdk.startPaymentWithRedirect(
        paymentUrl: redirectUrl,
        redirectUrl: redirectUrl,
        appBarTitle: 'Complete Payment',
      );

      if (!mounted) return;

      switch (result.status) {
        case PaymentStatus.success:
          _showMessage('Payment Successful!');
          // IMPORTANT: Verify the payment on your backend using the Order Inquiry API
          break;
        case PaymentStatus.failure:
          _showMessage('Payment Failed: ${result.message}');
          break;
        case PaymentStatus.cancelled:
          _showMessage('Payment Cancelled');
          break;
        case PaymentStatus.invalidRequest:
          _showMessage('Invalid Request: ${result.message}');
          break;
        default:
          _showMessage('Unknown status: ${result.status}');
      }
    } catch (e) {
      _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: Center(
        child: ElevatedButton(
          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.

```dart
class PaymentResult {
  final bool success;          // true if the payment succeeded
  final String status;         // payment status value
  final String finalUrl;       // final WebView URL when the flow ended
  final String? transactionId; // transaction ID, if available
  final String? message;       // additional message, if available
}
```

**Parameters**
| Parameters           | Type      | Description                                                                |
| --------------- | --------- | -------------------------------------------------------------------------- |
| `success`       | `bool`    | Indicates whether the payment was successful.                              |
| `status`        | `String`  | Final payment status returned by the SDK.                                  |
| `finalUrl`      | `String`  | The final URL loaded in the WebView when the payment flow ended.           |
| `transactionId` | `String?` | Transaction ID received from the callback URL, if available.               |
| `message`       | `String?` | Additional message describing the result, failure reason, or cancellation. |


> 📘 Note:
> 
> - 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.

### PaymentStatus
The SDK returns one of the following standard status values:
| Status                         | Value             | Description                                                    |
| ------------------------------ | ----------------- | -------------------------------------------------------------- |
| `PaymentStatus.success`        | `success`         | Payment completed successfully.                                |
| `PaymentStatus.failure`        | `failure`         | Payment failed due to bank decline, timeout, or another error. |
| `PaymentStatus.cancelled`      | `cancelled`       | User pressed the back button or cancelled the payment flow.    |
| `PaymentStatus.invalidRequest` | `invalid_request` | Invalid payment URL or parameters were passed to the SDK.      |

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

```dart
final result = await PinePaymentSdk.startPaymentWithRedirect(
  paymentUrl: redirectUrl,
  redirectUrl: redirectUrl,
);

switch (result.status) {
  case PaymentStatus.success:
    // Payment succeeded.
    // Use result.transactionId and result.finalUrl for backend verification.

    // Important:
    // Always call your backend Order Inquiry API using the order ID
    // before fulfilling or completing the order.
    break;

  case PaymentStatus.failure:
    // Payment failed.
    // result.message may contain the failure reason,
    // such as "declined", "timeout", or another error message.

    // Show an appropriate failure message to the user
    // and provide an option to retry the payment.
    break;

  case PaymentStatus.cancelled:
    // User cancelled the payment flow or pressed the back button.
    // result.message may contain:
    // "Payment was cancelled by the user."

    // Allow the user to retry the payment if required.
    break;

  case PaymentStatus.invalidRequest:
    // The SDK could not process the provided payment URL or parameters.
    // result.message describes what went wrong.

    // Check the paymentUrl, redirectUrl, successUrl, or failureUrl
    // and try again with valid values.
    break;
}
```

### How Callback Detection Works
The SDK automatically detects payment callbacks. You do not need to write custom URL-matching logic.

During the payment flow, the SDK performs the following actions:
| Step              | Description                                                                                                                                                                          |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| URL monitoring    | The SDK monitors every URL change inside the WebView, including navigation requests, page loads, and redirects.                                                                      |
| Callback matching | When the current URL matches the configured `redirectUrl`, `successUrl`, or `failureUrl`, the SDK identifies it as the payment callback.                                             |
| Parameter parsing | The SDK parses query parameters from the callback URL. It checks for common payment parameters such as `status`, `transactionId`, `txnId`, `orderId`, `message`, and their variants. |
| Status detection  | If the callback URL contains a status parameter, the SDK uses it to determine the result. If no status is available, the SDK infers the result based on the matched callback URL.    |
| Flow completion   | The SDK automatically closes the WebView and returns a `PaymentResult` to your app. No manual WebView dismissal is required.                                                         |

### External UPI and Intent Handling
When the payment flow requires opening an external UPI app such as Google Pay, PhonePe, Paytm, or another supported app, the SDK handles the redirection automatically.

The SDK automatically:

- Detects external payment URL schemes such as `upi://`, `intent://`, `phonepe://`, `gpay://`, `paytm://`, `tez://`, `credpay://`, and other non-web schemes.
- Prevents these URLs from opening inside the WebView.
- Launches the relevant external payment app installed on the user’s device.
- Resumes callback URL monitoring when the user returns to your app.
- Uses the browser fallback URL for `intent://` links when the target app is not installed.

No additional code is required for UPI app handling. External UPI and intent-based redirections are fully managed by the SDK.


<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>
