PayPal Android SDK with Flutter: Integrate PayPal Checkout Using Platform Channels
Flutter does not need a Dart package for every native Android SDK. When an application depends on an Android-only SDK, Flutter can communicate with Kotlin through platform channels.
This is especially useful for payment SDKs.
In this tutorial, we'll demonstrate the architecture for integrating the PayPal Android SDK through Braintree's PayPal module into a Flutter Android application.
Important: Braintree's current Android documentation recommends the
PayPalmodule for checkout. The olderPayPalNativeCheckoutmodule was deprecated. Braintree also documents certificate-related requirements for older mobile SDKs, so verify the current SDK and migration guidance before production release.
Step 1: Create the Flutter Project
flutter create paypal_flutter_demo
cd paypal_flutter_demo
This tutorial focuses on Android.
Step 2: Add the Android PayPal Dependency
In the Android application's Gradle dependencies:
dependencies {
implementation("com.braintreepayments.api:paypal:5.8.0")
}
Verify the current Braintree version before production use.
Step 3: Understand Credentials
Do not put private credentials in Flutter.
The mobile client can use an appropriate client authorization value such as a client token or tokenization key, depending on the integration.
For production, obtain client authorization from your backend.
Step 4: Create the Flutter MethodChannel
import 'package:flutter/services.dart';
class PayPalBridge {
static const _channel = MethodChannel(
'com.example.paypal/checkout',
);
static Future<String?> startCheckout() {
return _channel.invokeMethod<String>(
'startCheckout',
);
}
}
Flutter platform channels allow Dart to call Android Kotlin code.
Step 5: Implement the Android Channel
In MainActivity.kt:
class MainActivity : FlutterActivity() {
private val channelName = "com.example.paypal/checkout"
override fun configureFlutterEngine(
flutterEngine: FlutterEngine
) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
channelName
).setMethodCallHandler { call, result ->
when (call.method) {
"startCheckout" -> {
startPayPalCheckout(result)
}
else -> result.notImplemented()
}
}
}
}
Step 6: Initialize the Native SDK
Braintree's Android documentation shows creating a PayPalLauncher in Activity.onCreate() and creating a PayPalClient with an authorization value and app-link return URL.
Conceptually:
private lateinit var payPalLauncher: PayPalLauncher
private lateinit var payPalClient: PayPalClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
payPalLauncher = PayPalLauncher()
payPalClient = PayPalClient(
context = this,
authorization = clientAuthorization,
appLinkReturnUrl = Uri.parse(
"https://merchant-app.example"
)
)
}
Use the exact current API from the SDK version you install.
Step 7: Start Checkout
Keep the payment implementation in a dedicated class:
private fun startPayPalCheckout(
result: MethodChannel.Result
) {
// Start the current PayPal checkout flow.
// Return success or error through `result`.
}
Avoid putting the entire payment implementation into MainActivity.
Step 8: Return the Result to Flutter
Success:
result.success("completed")
Failure:
result.error(
"PAYPAL_ERROR",
"Payment could not be completed",
null
)
Flutter:
try {
final status = await PayPalBridge.startCheckout();
if (status == 'completed') {
debugPrint('Checkout completed');
}
} on PlatformException catch (e) {
debugPrint('PayPal error: ${e.message}');
}
Step 9: Verify Payments on Your Backend
Never treat a mobile callback as the only source of truth for fulfilling an order.
Use:
Flutter
↓
Native PayPal SDK
↓
PayPal / Braintree
↓
Backend
↓
Verify transaction
↓
Fulfill order
Your backend should verify payment state before marking an order as paid.
Step 10: Separate Flutter and Native Code
A clean structure:
lib/
payments/
paypal_bridge.dart
android/
app/
src/main/kotlin/
PayPalManager.kt
MainActivity.kt
PayPalBridge should expose a small Dart API.
PayPalManager should own Android-specific payment logic.
Security Best Practices
Never:
- Hard-code private credentials.
- Trust only a client-side success callback.
- Log payment tokens.
- Store sensitive credentials in ordinary preferences.
- Fulfill orders without server-side verification.
Use:
- Backend-generated client authorization.
- HTTPS.
- Server-side transaction verification.
- Current supported SDK versions.
- Minimal logging of payment information.
Why Platform Channels Matter
Flutter's platform-channel architecture allows Dart to communicate with Android Kotlin/Java APIs. This makes it possible to use native SDKs when a suitable Flutter plugin is unavailable.
Common Problems
Dependency Resolution Failure
Check the Android repository and dependency version required by the current Braintree documentation.
Checkout Does Not Return to the App
Check your App Link / return URL configuration.
Payment Appears Successful but Order Is Not Updated
Do not rely exclusively on the mobile callback. Verify the transaction on your backend.
SDK Version Problems
Payment SDKs change over time. Check the current Braintree migration and certificate guidance before releasing.
Conclusion
SDK integrations are easiest to maintain when credentials, platform-specific configuration, networking, and UI responsibilities are separated. Start with the smallest working flow, verify it on a physical device, and then add production concerns such as authentication, error handling, lifecycle management, and secure credential handling.
Stay tuned for more advanced Flutter SDK integration tutorials!
SEO Keywords
Flutter SDK tutorial, Flutter integration, Flutter mobile development, Dart SDK integration, Flutter Android, Flutter iOS, SDK integration
Tags
flutter dart mobiledevelopment sdk android
Useful Links
Website: www.v-modal.com
SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutter
SDK Android: https://github.com/v-modal/vmodal_sdk_android
Discord: https://discord.gg/K72z28KUx
Top comments (0)