open_payment 4.0.7
open_payment: ^4.0.7 copied to clipboard
Flutter plugin which lets you seamlessly integrate OPEN Payment Gateway with your Flutter app and start collecting payments from your customers.
example/lib/main.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:open_payment/open_payment.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final TextEditingController _accessTokenController = TextEditingController();
final TextEditingController _paymentTokenController = TextEditingController();
final TextEditingController _mainColorController = TextEditingController();
final TextEditingController _errorColorController = TextEditingController();
final TextEditingController _logoUrlController = TextEditingController();
PaymentEnvironment environment = PaymentEnvironment.preprod;
String? result;
bool isLoading = false;
bool isGetDataButtonEnabled = true; // Control Get Data button state
@override
void initState() {
super.initState();
print('isGetDataButtonEnabled: $isGetDataButtonEnabled'); // Debug print
}
@override
void dispose() {
_accessTokenController.dispose();
_paymentTokenController.dispose();
_mainColorController.dispose();
_errorColorController.dispose();
_logoUrlController.dispose();
super.dispose();
}
// API call to fetch data
Future<void> fetchDataFromAPI() async {
setState(() {
isLoading = true;
result = null;
});
try {
final response = await http.get(
Uri.parse('https://571252e6-bbb9-49ad-a77e-7a1fa2d722e4.mock.pstmn.io/initApp'),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
setState(() {
_accessTokenController.text = data['accessToken'] ?? '';
_paymentTokenController.text = data['paymentToken'] ?? '';
_mainColorController.text = data['mainColor'] ?? '';
_errorColorController.text = data['errorColor'] ?? '';
_logoUrlController.text = data['logo'] ?? '';
result = 'Data loaded successfully from API!';
});
} else {
setState(() {
result = 'Error: Failed to load data. Status: ${response.statusCode}';
});
}
} catch (e) {
setState(() {
result = 'Error: Failed to connect to API. $e';
});
} finally {
setState(() {
isLoading = false;
});
}
}
// Clear all input fields
void clearData() {
setState(() {
_accessTokenController.clear();
_paymentTokenController.clear();
_mainColorController.clear();
_errorColorController.clear();
_logoUrlController.clear();
result = null;
});
}
// Validation method for hex color codes
bool isValidHexColor(String color) {
if (color.isEmpty) return false;
// Remove # if present
String hexColor = color.startsWith('#') ? color.substring(1) : color;
// Check if it's 3 or 6 characters and contains only valid hex characters
return RegExp(r'^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$').hasMatch(hexColor);
}
// Validation method for URL
bool isValidUrl(String url) {
if (url.isEmpty) return true; // URL is optional
try {
Uri.parse(url);
return true;
} catch (e) {
return false;
}
}
// Get validation error message for color fields
String? getColorValidationError(String color, String fieldName) {
if (color.isEmpty) {
return null; // Color is optional
}
if (!isValidHexColor(color)) {
return '$fieldName must be a valid hex color (e.g., #FF0000 or #F00)';
}
return null;
}
// Get validation error message for URL field
String? getUrlValidationError(String url) {
if (url.isEmpty) {
return null; // URL is optional
}
if (!isValidUrl(url)) {
return 'Logo URL must be a valid URL';
}
return null;
}
Future<void> onPaymentClick() async {
final accessToken = _accessTokenController.text.trim();
final paymentToken = _paymentTokenController.text.trim();
final mainColor = _mainColorController.text.trim();
final errorColor = _errorColorController.text.trim();
final logoUrl = _logoUrlController.text.trim();
// Validate required fields
if (accessToken.isEmpty) {
setState(() {
result = 'Error: Access Token is required';
});
return;
}
if (paymentToken.isEmpty) {
setState(() {
result = 'Error: Payment Token is required';
});
return;
}
// Validate optional fields only if they have values
if (mainColor.isNotEmpty && !isValidHexColor(mainColor)) {
setState(() {
result = 'Error: Main Color must be a valid hex color';
});
return;
}
if (errorColor.isNotEmpty && !isValidHexColor(errorColor)) {
setState(() {
result = 'Error: Error Color must be a valid hex color';
});
return;
}
if (logoUrl.isNotEmpty && !isValidUrl(logoUrl)) {
setState(() {
result = 'Error: Logo URL must be a valid URL';
});
return;
}
OpenPaymentPayload openPaymentPayload = OpenPaymentPayload(
accessToken: accessToken,
paymentToken: paymentToken,
environment: environment,
mainColor: mainColor.isNotEmpty ? mainColor : null,
errorColor: errorColor.isNotEmpty ? errorColor : null,
logoUrl: logoUrl.isNotEmpty ? logoUrl : null,
);
await OpenPayment.initiatePayment(
openPaymentPayload: openPaymentPayload,
onTransactionComplete: (TransactionDetails transactionDetails) {
setState(() {
result = jsonEncode(transactionDetails.toJson());
});
},
onError: (String error) {
setState(() {
result = error;
});
},
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Open Payment Demo'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Environment selector dropdown
DropdownButtonFormField<PaymentEnvironment>(
value: environment,
decoration: const InputDecoration(
labelText: 'Environment',
border: OutlineInputBorder(),
),
items: const [
DropdownMenuItem(
value: PaymentEnvironment.preprod,
child: Text('Preprod'),
),
DropdownMenuItem(
value: PaymentEnvironment.live,
child: Text('Live'),
),
],
onChanged: (env) {
if (env != null) {
setState(() {
environment = env;
});
}
},
),
const SizedBox(height: 16),
TextField(
controller: _accessTokenController,
decoration: const InputDecoration(
labelText: 'Access Token',
border: OutlineInputBorder(),
hintText: 'Enter your access token',
hintStyle: TextStyle(color: Colors.grey),
),
),
const SizedBox(height: 16),
TextField(
controller: _paymentTokenController,
decoration: const InputDecoration(
labelText: 'Payment Token',
border: OutlineInputBorder(),
hintText: 'Enter your payment token',
hintStyle: TextStyle(color: Colors.grey),
),
),
const SizedBox(height: 16),
// Main Color and Error Color in horizontal layout
Row(
children: [
Expanded(
child: TextField(
controller: _mainColorController,
decoration: InputDecoration(
labelText: 'Main Color (#83025c)',
border: const OutlineInputBorder(),
hintText: '#83025c',
hintStyle: const TextStyle(color: Colors.grey),
errorText: getColorValidationError(_mainColorController.text, 'Main Color'),
),
onChanged: (value) {
setState(() {
// Trigger validation on change
});
},
),
),
const SizedBox(width: 16),
Expanded(
child: TextField(
controller: _errorColorController,
decoration: InputDecoration(
labelText: 'Error Color (#ff0000)',
border: const OutlineInputBorder(),
hintText: '#ff0000',
hintStyle: const TextStyle(color: Colors.grey),
errorText: getColorValidationError(_errorColorController.text, 'Error Color'),
),
onChanged: (value) {
setState(() {
// Trigger validation on change
});
},
),
),
],
),
const SizedBox(height: 16),
TextField(
controller: _logoUrlController,
decoration: InputDecoration(
labelText: 'Logo URL',
border: const OutlineInputBorder(),
hintText: 'https://example.com/logo.png',
hintStyle: const TextStyle(color: Colors.grey),
errorText: getUrlValidationError(_logoUrlController.text),
),
onChanged: (value) {
setState(() {
// Trigger validation on change
});
},
),
const SizedBox(height: 24),
// Get Data and Clear Data buttons in horizontal layout
Row(
children: [
Expanded(
child: SizedBox(
height: 48, // Standard Material Design button height
child: ElevatedButton(
onPressed: isGetDataButtonEnabled ? fetchDataFromAPI : null,
style: ElevatedButton.styleFrom(
backgroundColor: isGetDataButtonEnabled ? Colors.blue : Colors.grey,
foregroundColor: Colors.white,
disabledBackgroundColor: Colors.grey,
disabledForegroundColor: Colors.white70,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
child: const Text('Get Data'),
),
),
),
const SizedBox(width: 16),
Expanded(
child: SizedBox(
height: 48, // Standard Material Design button height
child: ElevatedButton(
onPressed: clearData,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
child: const Text('Clear Data'),
),
),
),
],
),
const SizedBox(height: 16),
SizedBox(
height: 48, // Standard Material Design button height
width: double.infinity, // Full width
child: ElevatedButton(
onPressed: onPaymentClick,
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
child: const Text('Initiate Payment'),
),
),
const SizedBox(height: 24),
if (result != null)
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Result: $result',
style: const TextStyle(fontSize: 14),
),
),
const SizedBox(height: 24),
],
),
),
),
);
}
}