fl_openpay 0.0.1
fl_openpay: ^0.0.1 copied to clipboard
Flutter plugin for Openpay payment gateway. Supports card tokenization, device fingerprinting, and 3D Secure for iOS and Android.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:fl_openpay/fl_openpay.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Openpay Example',
theme: ThemeData(
colorSchemeSeed: Colors.blue,
useMaterial3: true,
),
home: const OpenpayDemoPage(),
);
}
}
class OpenpayDemoPage extends StatefulWidget {
const OpenpayDemoPage({super.key});
@override
State<OpenpayDemoPage> createState() => _OpenpayDemoPageState();
}
class _OpenpayDemoPageState extends State<OpenpayDemoPage> {
final _openpay = FlOpenpay();
final _formKey = GlobalKey<FormState>();
final _merchantIdController = TextEditingController();
final _apiKeyController = TextEditingController();
final _holderNameController = TextEditingController();
final _cardNumberController = TextEditingController();
final _monthController = TextEditingController();
final _yearController = TextEditingController();
final _cvvController = TextEditingController();
bool _productionMode = false;
OpenpayCountry _country = OpenpayCountry.mexico;
String _status = '';
bool _loading = false;
@override
void dispose() {
_merchantIdController.dispose();
_apiKeyController.dispose();
_holderNameController.dispose();
_cardNumberController.dispose();
_monthController.dispose();
_yearController.dispose();
_cvvController.dispose();
super.dispose();
}
Future<void> _initialize() async {
if (_merchantIdController.text.isEmpty || _apiKeyController.text.isEmpty) {
_setStatus('Enter Merchant ID and API Key first.');
return;
}
setState(() => _loading = true);
try {
await _openpay.initialize(
merchantId: _merchantIdController.text.trim(),
publicApiKey: _apiKeyController.text.trim(),
productionMode: _productionMode,
country: _country,
);
_setStatus('SDK initialized successfully.');
} on OpenpayException catch (e) {
_setStatus('Init error: ${e.message}');
} catch (e) {
_setStatus('Init error: $e');
} finally {
setState(() => _loading = false);
}
}
Future<void> _tokenize() async {
if (!_formKey.currentState!.validate()) return;
if (!_openpay.isInitialized) {
_setStatus('Initialize SDK first.');
return;
}
setState(() => _loading = true);
try {
final token = await _openpay.tokenizeCard(
OpenpayCard(
holderName: _holderNameController.text.trim(),
cardNumber: _cardNumberController.text.trim(),
expirationMonth: _monthController.text.trim(),
expirationYear: _yearController.text.trim(),
cvv2: _cvvController.text.trim(),
),
);
_setStatus('Token: ${token.id}\nBrand: ${token.brand}\nCard: ${token.cardNumberMasked}');
} on OpenpayGatewayException catch (e) {
_setStatus('Bank declined [${e.code}]: ${e.message}');
} on OpenpayRequestException catch (e) {
_setStatus('Request error [${e.code}]: ${e.message}');
} on OpenpayException catch (e) {
_setStatus('Error [${e.code}]: ${e.message}');
} catch (e) {
_setStatus('Error: $e');
} finally {
setState(() => _loading = false);
}
}
Future<void> _getSessionId() async {
if (!_openpay.isInitialized) {
_setStatus('Initialize SDK first.');
return;
}
setState(() => _loading = true);
try {
final sessionId = await _openpay.createDeviceSessionId();
_setStatus('Device Session ID:\n$sessionId');
} on OpenpayException catch (e) {
_setStatus('Session error: ${e.message}');
} catch (e) {
_setStatus('Error: $e');
} finally {
setState(() => _loading = false);
}
}
void _setStatus(String message) {
setState(() => _status = message);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Openpay Demo')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// -- Configuration --
Text('Configuration', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
TextFormField(
controller: _merchantIdController,
decoration: const InputDecoration(
labelText: 'Merchant ID',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
TextFormField(
controller: _apiKeyController,
decoration: const InputDecoration(
labelText: 'Public API Key',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: DropdownButtonFormField<OpenpayCountry>(
initialValue: _country,
decoration: const InputDecoration(
labelText: 'Country',
border: OutlineInputBorder(),
),
items: OpenpayCountry.values
.map((c) => DropdownMenuItem(value: c, child: Text(c.name)))
.toList(),
onChanged: (v) => setState(() => _country = v!),
),
),
const SizedBox(width: 12),
Column(
children: [
const Text('Production'),
Switch(
value: _productionMode,
onChanged: (v) => setState(() => _productionMode = v),
),
],
),
],
),
const SizedBox(height: 8),
FilledButton(
onPressed: _loading ? null : _initialize,
child: const Text('Initialize SDK'),
),
const Divider(height: 32),
// -- Card Data --
Text('Card Data', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
TextFormField(
controller: _holderNameController,
decoration: const InputDecoration(
labelText: 'Holder Name',
border: OutlineInputBorder(),
),
validator: (v) => (v == null || v.isEmpty) ? 'Required' : null,
),
const SizedBox(height: 8),
TextFormField(
controller: _cardNumberController,
decoration: const InputDecoration(
labelText: 'Card Number',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
validator: (v) => (v == null || v.length < 15) ? 'Invalid card number' : null,
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: TextFormField(
controller: _monthController,
decoration: const InputDecoration(
labelText: 'Month (MM)',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
validator: (v) => (v == null || v.isEmpty) ? 'Required' : null,
),
),
const SizedBox(width: 8),
Expanded(
child: TextFormField(
controller: _yearController,
decoration: const InputDecoration(
labelText: 'Year (YY)',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
validator: (v) => (v == null || v.isEmpty) ? 'Required' : null,
),
),
const SizedBox(width: 8),
Expanded(
child: TextFormField(
controller: _cvvController,
decoration: const InputDecoration(
labelText: 'CVV',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
obscureText: true,
validator: (v) => (v == null || v.length < 3) ? 'Invalid' : null,
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: FilledButton(
onPressed: _loading ? null : _tokenize,
child: const Text('Tokenize Card'),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton(
onPressed: _loading ? null : _getSessionId,
child: const Text('Get Session ID'),
),
),
],
),
const Divider(height: 32),
// -- Status --
if (_loading) const Center(child: CircularProgressIndicator()),
if (_status.isNotEmpty)
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
),
child: SelectableText(
_status,
style: Theme.of(context).textTheme.bodyMedium,
),
),
],
),
),
),
);
}
}