Flutter Mobile SDKs
Integrate Pine Labs payments into your mobile application using our Flutter Native SDK.
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.
- Prerequisites
- Integrate APIs in Your Backend
- SDK Installation and Initialization
- Handle Payments
- Manage Transactions
❗️ Security Best Practices:
- Ensure you store your
client_idandclient_secretin 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.
1. Prerequisites
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
2. Integrate APIs in Your Backend
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 --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"
}
'
Refer to our Generate Token API 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 --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"
}
}
'
Refer to our Generate Checkout Link documentation to learn more.
3. SDK Installation & Initialization
Installation
Add the SDK Dependency
Open your Flutter project’s pubspec.yaml file and add pinelabs_native under dependencies.
dependencies:
flutter:
sdk: flutter
pinelabs_native: ^1.0.0
Then run:
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:
android {
defaultConfig {
minSdk = 26
// ...
}
}
📘 Note:
- If your app already targets
minSdk 26or higher, no changes are needed. If your currentminSdkis 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.
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://jitpack.io")
}
}
For older Gradle projects that use allprojects in the root build.gradle, add JitPack there instead.
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:
<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:
platform :ios, '13.0'
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:
<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:
import 'package:pinelabs_native/pinelabs_native.dart';
Create an SDK instance:
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.
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:
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.
4. Handle Payments
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.
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:
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. |
5. Manage Transactions
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.
