I am working with stripe terminal android SDK https://docs.stripe.com/terminal/quickstart?reader=wp3 in flutter because there is no SDK available for Flutter. I am trying for many days. Stripe terminal got initialized but it is not detecting Wisepad 3 reader.
Flutter code =
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class BluetoothConfigurationScreen extends StatefulWidget {
const BluetoothConfigurationScreen({super.key});
@override
State<BluetoothConfigurationScreen> createState() =>
_BluetoothConfigurationScreenState();
}
class _BluetoothConfigurationScreenState
extends State<BluetoothConfigurationScreen> {
static const platform = MethodChannel('com.example.case/stripe');
TextEditingController textEditingController = TextEditingController();
String resultText = '';
String _batteryLevel = 'Unknown battery level.';
String _cardInfo = 'No card scanned';
String _connectedDeviceInfo = 'No device connected';
bool isTerminalInitialized = false;
bool isReaderConnected = false;
// Method to initialize Stripe terminal
Future<void> _initializeStripe() async {
try {
// Initialize the Stripe terminal by invoking the method on the native side
final result = await platform.invokeMethod('initializeStripe');
setState(() {
isTerminalInitialized = true; // Mark terminal as initialized
});
print(result);
} catch (e) {
setState(() {
isTerminalInitialized = false; // Handle initialization failure
});
print("Error initializing terminal: $e");
}
}
// Method to get battery level
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final result = await platform.invokeMethod<int>('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
// Method to start card scan
Future<void> _startCardScan() async {
if (!isTerminalInitialized || !isReaderConnected) {
setState(() {
_cardInfo = 'Terminal or Reader is not connected. Please initialize and connect first.';
});
return;
}
try {
final cardData = await platform.invokeMethod('scanCard');
setState(() {
_cardInfo = cardData;
});
} catch (e) {
setState(() {
_cardInfo = 'Error scanning card: $e';
});
}
}
// Method to discover Bluetooth devices and fetch connected device info
Future<void> _discoverReaders() async {
if (!isTerminalInitialized) {
setState(() {
_cardInfo = 'Terminal is not initialized yet. Please initialize first.';
});
return;
}
try {
final result = await platform.invokeMethod('discoverReaders');
setState(() {
_cardInfo = result;
isReaderConnected = true; // Assuming reader is connected after discovery
});
// Fetch connected device info
final connectedDevice = await platform.invokeMethod('getConnectedDeviceInfo');
setState(() {
_connectedDeviceInfo = connectedDevice ?? 'No connected device information available';
});
} catch (e) {
setState(() {
_cardInfo = 'Error discovering readers: $e';
isReaderConnected = false;
_connectedDeviceInfo = 'Error fetching connected device info';
});
}
}
// Method to make a 0.1 cent payment
Future<void> _makePayment() async {
if (!isTerminalInitialized || !isReaderConnected) {
setState(() {
_cardInfo = 'Terminal or Reader is not connected. Please initialize and connect first.';
});
return;
}
try {
final paymentResult = await platform.invokeMethod('makePayment');
setState(() {
_cardInfo = paymentResult;
});
} catch (e) {
setState(() {
_cardInfo = 'Error processing payment: $e';
});
}
}
@override
void dispose() {
textEditingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () async {
await _initializeStripe(); // Initialize Stripe Terminal
if (isTerminalInitialized) {
setState(() {
_cardInfo = "Terminal Initialized. You can now scan cards.";
});
} else {
setState(() {
_cardInfo = "Initialization failed. Try again.";
});
}
},
child: const Text("Initialize Stripe Terminal"),
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: _getBatteryLevel,
child: const Text('Get Battery Level'),
),
Text(_batteryLevel),
const SizedBox(height: 30),
ElevatedButton(
onPressed: _startCardScan, // Trigger card scan
child: const Text('Scan Card'),
),
Text(_cardInfo), // Display scanned card information
const SizedBox(height: 30),
ElevatedButton(
onPressed: _discoverReaders, // Discover Bluetooth readers
child: const Text('Discover Bluetooth Readers'),
),
Text(_cardInfo), // Display reader information
Text('Connected Device Info: $_connectedDeviceInfo'), // Display connected device info
const SizedBox(height: 30),
ElevatedButton(
onPressed: _makePayment, // Trigger payment of 0.1 cent
child: const Text('Make 0.1 Cent Payment'),
),
Text(_cardInfo), // Display payment result
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: TextField(
controller: textEditingController,
decoration: const InputDecoration(
labelText: 'Enter UserName',
),
),
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () async {
final userName = textEditingController.text;
await callNativeCode(userName);
setState(() {});
},
child: const Text('Send Data'),
),
Text(resultText),
],
),
),
);
}
// Method to send user data to the native code
Future<void> callNativeCode(String userName) async {
try {
resultText =
await platform.invokeMethod('userName', {'username': userName});
setState(() {});
} catch (e) {
print("Failed to send user data: $e");
}
}
}
Android code =
package com.example.np_casse
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.BatteryManager
import androidx.annotation.NonNull
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import com.stripe.stripeterminal.Terminal
import com.stripe.stripeterminal.external.callable.*
import com.stripe.stripeterminal.external.models.*
import com.stripe.stripeterminal.log.LogLevel
import android.util.Log
import com.stripe.stripeterminal.external.models.Reader
class MainActivity : FlutterActivity() {
private val CHANNEL = "com.example.case/stripe"
private var terminalInitialized = false
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"initializeStripe" -> initializeTerminal(result)
"discoverReaders" -> {
if (terminalInitialized) {
discoverReaders(result)
} else {
result.error("TERMINAL_NOT_INITIALIZED", "Terminal must be initialized first", null)
}
}
"scanCard" -> {
if (terminalInitialized) {
startCardScan(result)
} else {
result.error("TERMINAL_NOT_INITIALIZED", "Terminal must be initialized first", null)
}
}
"makePayment" -> {
if (terminalInitialized) {
makePayment(result)
} else {
result.error("TERMINAL_NOT_INITIALIZED", "Terminal must be initialized first", null)
}
}
"getBatteryLevel" -> getBatteryLevel(result)
"getConnectedDeviceInfo" -> getConnectedDeviceInfo(result) // Fetch device info
else -> result.notImplemented()
}
}
}
private fun initializeTerminal(result: MethodChannel.Result) {
if (!Terminal.isInitialized()) {
try {
Terminal.initTerminal(applicationContext, LogLevel.VERBOSE, TokenProvider(), TerminalEventListener())
terminalInitialized = true
result.success("Stripe Initialized")
} catch (e: TerminalException) {
terminalInitialized = false
result.error("INITIALIZATION_ERROR", "Error initializing Terminal: ${e.message}", null)
}
} else {
terminalInitialized = true
result.success("Stripe Already Initialized")
}
}
private fun discoverReaders(result: MethodChannel.Result) {
if (checkLocationPermissions() && checkBluetoothPermissions()) {
val discoveryConfig = DiscoveryConfiguration.BluetoothDiscoveryConfiguration(isSimulated = false)
val discoveryCallback = object : Callback {
override fun onSuccess() {
result.success("Reader discovery started")
}
override fun onFailure(e: TerminalException) {
result.error("DISCOVERY_ERROR", e.message, null)
}
}
val discoveryListener = object : DiscoveryListener {
override fun onUpdateDiscoveredReaders(readers: List<Reader>) {
if (readers.isEmpty()) {
result.success("No readers detected.")
} else {
for (reader in readers) {
result.success("Reader found: ${reader.label}")
}
}
}
}
Terminal.getInstance().discoverReaders(discoveryConfig, discoveryListener, discoveryCallback)
} else {
requestPermissions()
}
}
// New method to get connected device info
private fun getConnectedDeviceInfo(result: MethodChannel.Result) {
val reader: Reader? = Terminal.getInstance().connectedReader
if (reader != null) {
val deviceInfo = "Label: ${reader.label}, Serial Number: ${reader.serialNumber}"
result.success(deviceInfo)
} else {
result.success("No reader connected")
}
}
private fun startCardScan(result: MethodChannel.Result) {
val reader = Terminal.getInstance().connectedReader
if (reader != null) {
val paymentIntentParams = PaymentIntentParameters.Builder(listOf(PaymentMethodType.CARD_PRESENT))
.setAmount(1)
.setCurrency("usd")
.build()
Terminal.getInstance().createPaymentIntent(paymentIntentParams, object : PaymentIntentCallback {
override fun onSuccess(paymentIntent: PaymentIntent) {
Terminal.getInstance().collectPaymentMethod(paymentIntent, object : PaymentIntentCallback {
override fun onSuccess(paymentIntent: PaymentIntent) {
Terminal.getInstance().confirmPaymentIntent(paymentIntent, object : PaymentIntentCallback {
override fun onSuccess(paymentIntent: PaymentIntent) {
result.success("Payment Successful")
}
override fun onFailure(e: TerminalException) {
result.error("PAYMENT_CONFIRM_ERROR", "Error confirming payment: ${e.message}", null)
}
})
}
override fun onFailure(e: TerminalException) {
result.error("PAYMENT_METHOD_ERROR", "Error collecting payment method: ${e.message}", null)
}
})
}
override fun onFailure(e: TerminalException) {
result.error("PAYMENT_INTENT_ERROR", "Error creating payment intent: ${e.message}", null)
}
})
} else {
result.error("NO_READER", "No reader connected", null)
}
}
private fun makePayment(result: MethodChannel.Result) {
val reader = Terminal.getInstance().connectedReader
if (reader != null) {
val paymentIntentParams = PaymentIntentParameters.Builder(listOf(PaymentMethodType.CARD_PRESENT))
.setAmount(1)
.setCurrency("usd")
.build()
Terminal.getInstance().createPaymentIntent(paymentIntentParams, object : PaymentIntentCallback {
override fun onSuccess(paymentIntent: PaymentIntent) {
Terminal.getInstance().collectPaymentMethod(paymentIntent, object : PaymentIntentCallback {
override fun onSuccess(paymentIntent: PaymentIntent) {
Terminal.getInstance().confirmPaymentIntent(paymentIntent, object : PaymentIntentCallback {
override fun onSuccess(paymentIntent: PaymentIntent) {
result.success("Payment Successful")
}
override fun onFailure(e: TerminalException) {
result.error("PAYMENT_CONFIRM_ERROR", "Error confirming payment: ${e.message}", null)
}
})
}
override fun onFailure(e: TerminalException) {
result.error("PAYMENT_METHOD_ERROR", "Error collecting payment method: ${e.message}", null)
}
})
}
override fun onFailure(e: TerminalException) {
result.error("PAYMENT_INTENT_ERROR", "Error creating payment intent: ${e.message}", null)
}
})
} else {
result.error("NO_READER", "No reader connected", null)
}
}
private fun getBatteryLevel(result: MethodChannel.Result) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
result.success(batteryLevel)
}
private fun checkLocationPermissions(): Boolean {
return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
private fun checkBluetoothPermissions(): Boolean {
return ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED
}
private fun requestPermissions() {
ActivityCompat.requestPermissions(
this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BLUETOOTH), 1
)
}
}
I tried different things but I am not able to find any solution. If someone had faces same issue and knows how to fix it then please let me know. Thank you
reader.label does not exist. you can check for serialNumber and deviceType. For me it is working like that =
if (checkPermissions()) {
if (BluetoothAdapter.getDefaultAdapter()?.isEnabled == false) {
BluetoothAdapter.getDefaultAdapter().enable()
}
val discoveryConfig = DiscoveryConfiguration.BluetoothDiscoveryConfiguration(isSimulated = false)
val discoveredReaders = mutableListOf<Reader>()
val discoveryListener = object : DiscoveryListener {
override fun onUpdateDiscoveredReaders(readers: List<Reader>) {
if (readers.isNotEmpty()) {
val firstReader = readers.first()
// Sending the first reader's details
val readerInfo = mapOf(
"serialNumber" to (firstReader.serialNumber ?: "Unknown"),
"deviceType" to (firstReader.deviceType.name ?: "Unknown")
)
// Send the first reader to Flutter
result.success(readerInfo)
} else {
result.success(null) // Send null if no readers are found
}
}
}