This guide walks you through integrating PayPal checkout into your Flutter Android app using platform channels. This approach lets you call native Android PayPal SDK code from your Flutter code, which is necessary because PayPal does not offer an official Flutter SDK. You’ll set up the native side, create a platform channel, and handle the payment flow from Flutter.
What You’ll Need
- Flutter SDK installed and configured
- Android Studio (or another IDE) with the Android SDK
- A PayPal Developer account to get sandbox credentials (Client ID and Secret)
- Basic knowledge of Kotlin/Java and Flutter/Dart
Step-by-Step
-
Set up your PayPal Developer account and app.
Log in to the PayPal Developer portal, create a new app under My Apps & Credentials, and note your sandbox Client ID. You will need this later.
-
Create a new Flutter project.
Run
flutter create paypal_flutter_exampleto create a new project, or open an existing one. -
Add the PayPal Android SDK dependency.
Open
android/app/build.gradleand add the following to thedependenciesblock:implementation 'com.paypal.android:paypal-checkout:1.3.0'
Make sure your
minSdkVersionis at least 21. -
Configure the AndroidManifest.xml.
Add the following permissions and the PayPal service declaration inside the
<application>tag:<uses-permission android:name="android.permission.INTERNET" /> ... <service android:name="com.paypal.android.checkout.PayPalService" android:exported="false" /> -
Create a Kotlin (or Java) class for the platform channel.
In
android/app/src/main/kotlin/.../MainActivity.kt, add a class that handles the channel. For simplicity, you can put it inMainActivity.ktitself.Example Kotlin code:
class MainActivity : FlutterActivity() { private val CHANNEL = "paypal_checkout" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) .setMethodCallHandler { call, result -> when (call.method) { "startCheckout" -> { val amount = call.argument<String>("amount") val clientId = call.argument<String>("clientId") startPayPalCheckout(amount, clientId, result) } else -> result.notImplemented() } } } private fun startPayPalCheckout(amount: String?, clientId: String?, result: MethodChannel.Result) { // Implementation will go here } } -
Implement the PayPal checkout logic in Kotlin.
Inside
startPayPalCheckout, use the PayPal SDK to create a checkout session. Here’s a simplified example:val config = CheckoutConfig( clientId = clientId!!, environment = Environment.SANDBOX, returnUrl = "com.example.paypalflutter://paypalpay" ) val payPalCheckout = PayPalCheckout(config) payPalCheckout.startCheckout(activity = this) { result -> when (result) { is PayPalCheckoutResult.Success -> { result.success { approval -> // Send approval data back to Flutter result.success(mapOf( "status" to "success", "orderId" to approval.orderId )) } } is PayPalCheckoutResult.Canceled -> { result.success(mapOf("status" to "canceled")) } is PayPalCheckoutResult.Error -> { result.error("PAYPAL_ERROR", result.exception.message, null) } } }Note: The exact API may vary depending on the SDK version. Check the official PayPal Android SDK documentation for the correct usage.
-
Create the Flutter side of the platform channel.
Create a Dart file, e.g.,
paypal_service.dart, with a method that calls the native channel:import 'package:flutter/services.dart'; class PayPalService { static const MethodChannel _channel = MethodChannel('paypal_checkout'); static Future<Map<String, dynamic>> startCheckout({ required String amount, required String clientId, }) async { try { final result = await _channel.invokeMethod('startCheckout', { 'amount': amount, 'clientId': clientId, }); return Map<String, dynamic>.from(result); } on PlatformException catch (e) { return {'status': 'error', 'message': e.message}; } } } -
Call the checkout from your UI.
In your Flutter widget, use a button to trigger the payment:
ElevatedButton( onPressed: () async { final result = await PayPalService.startCheckout( amount: '10.00', clientId: 'YOUR_CLIENT_ID', ); if (result['status'] == 'success') { // Handle success, e.g., show order ID } else { // Handle error or cancellation } }, child: Text('Pay with PayPal'), ) -
Run and test.
Run your app on an Android emulator or device. Make sure you are using sandbox credentials. You should see the PayPal login screen and be able to complete a test payment.
Common Mistakes
- Using the wrong Client ID: Make sure you use the sandbox Client ID for testing, not the live one.
- Forgetting to add the PayPal service to the manifest: This causes a runtime crash.
- Not setting the return URL correctly: The return URL in the config must match the scheme you define in your AndroidManifest (if you use deep linking). For the basic flow, you can use a dummy URL.
- Calling the channel before the native side is ready: Ensure the Flutter engine is fully attached before invoking the method.
- Mixing up the result types: The native code must return a Map that Flutter can decode; avoid sending non-serializable objects.
Tips
- Always test with sandbox credentials first to avoid real charges.
- Keep your Client ID in a secure place; do not hardcode it in production. Use environment variables or secure storage.
- Handle the result asynchronously and show a loading indicator while the PayPal UI is open.
- Consider adding a timeout or error handling for network issues.
- Check the official PayPal Android SDK documentation for the latest API changes.
Troubleshooting
Problem: The PayPal screen does not open.
Check that you added the PayPal service to the manifest and that the Client ID is correct. Also, ensure the environment is set to SANDBOX.
Problem: The app crashes when starting checkout.
Look at the Logcat for the exact exception. Common causes include missing internet permission, wrong SDK version, or a missing dependency.
Problem: The result is always ‘canceled’ even after payment.
This usually means the return URL is not being handled properly. In the PayPal SDK, you need to set up a deep link to capture the result. Alternatively, use the newer PayPal Checkout SDK that uses a different callback mechanism.
Problem: Platform channel returns ‘null’ result.
Make sure you call result.success() exactly once in the native code. If you call it multiple times, Flutter will throw an error.
Frequently Asked Questions
Do I need a server for PayPal checkout?
For the basic client-side flow, you only need the Client ID. However, for production, you should verify the payment on your server to prevent fraud.
Can I use this for both Android and iOS?
No, this guide is Android-only. For iOS, you would need to create a separate platform channel implementation using the PayPal iOS SDK.
What is the difference between PayPal Checkout and PayPal Payments Pro?
PayPal Checkout is a simpler, hosted solution. Payments Pro allows more customization but requires a merchant account and more setup.
How do I get a live Client ID?
In the PayPal Developer portal, switch from Sandbox to Live and create an app. You will need to provide business information and get approval.
Can I test without a real PayPal account?
Yes, the sandbox environment provides test buyer accounts that you can use to simulate payments.
Is it possible to use PayPal without platform channels?
There are third-party Flutter plugins, but they may not be maintained or may have limitations. Platform channels give you full control and access to the latest native SDK features.
Related
General steps for current mainstream versions; menus can differ by device and version. See the disclaimer or report an error.
More in this bed
- Twitter X Data: Collect It via Authorized API Access
- Microsoft Teredo Adapter Not Working? Fix It
- Microsoft Laptop vs MacBook: Key Differences
- BioShock Co-Op: Supported Modes & PC Settings
- Windows Notifications: Cut Pop-Ups, Keep Alerts
- Fix Windows 10 Troubleshooter Not Working