bancard_vpos 0.1.0
bancard_vpos: ^0.1.0 copied to clipboard
Flutter package for Bancard VPOS 2.0 payments (Paraguay). Supports single buy and recurring payments with native card management. No backend required.
example/lib/main.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:bancard_vpos/bancard_vpos.dart';
/// ⚠️ SANDBOX CREDENTIALS — DO NOT USE IN PRODUCTION ⚠️
/// Replace with your own Bancard keys before deploying.
const _sandboxConfig = BancardConfig(
publicKey: 'your_sandbox_public_key',
privateKey: 'your_sandbox_private_key',
environment: BancardEnvironment.sandbox,
);
/// Sandbox test user ID
const _testUserId = 42;
/// Sandbox test data:
/// - Card: 4111 1111 1111 1111
/// - Expiration: any future date
/// - CVV: 123
/// - Cédula: 9661000
/// - OTP Zimple: 1234
void main() {
runApp(const BancardExampleApp());
}
class BancardExampleApp extends StatelessWidget {
const BancardExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bancard VPOS Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.blue,
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
BancardPaymentResult? _lastResult;
/// Generates a unique shop process ID based on timestamp.
int _generateShopProcessId() {
return DateTime.now().millisecondsSinceEpoch ~/ 1000;
}
Future<void> _startSingleBuyCheckout() async {
final result = await Navigator.push<BancardPaymentResult>(
context,
MaterialPageRoute(
builder: (_) => BancardCheckoutPage(
config: _sandboxConfig,
shopProcessId: _generateShopProcessId(),
amount: 150000,
description: 'Test Order #${Random().nextInt(9999)}',
currency: 'PYG',
userId: _testUserId,
title: 'Checkout',
),
),
);
if (result != null && mounted) {
setState(() => _lastResult = result);
}
}
Future<void> _openCardManagement() async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const BancardCardManagementPage(
config: _sandboxConfig,
userId: _testUserId,
userEmail: 'test@example.com',
userPhone: '0981000000',
),
),
);
}
Future<void> _startSimplePayment() async {
// This demonstrates payment WITHOUT saved cards (no userId)
final result = await Navigator.push<BancardPaymentResult>(
context,
MaterialPageRoute(
builder: (_) => BancardCheckoutPage(
config: _sandboxConfig,
shopProcessId: _generateShopProcessId(),
amount: 50000,
description: 'Quick Payment Test',
currency: 'PYG',
// No userId = skip card selection, go directly to WebView
title: 'Quick Payment',
),
),
);
if (result != null && mounted) {
setState(() => _lastResult = result);
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('Bancard VPOS Example'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header
Icon(Icons.payment, size: 64, color: theme.colorScheme.primary),
const SizedBox(height: 16),
Text(
'Bancard VPOS 2.0',
style: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Flutter integration example',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.textTheme.bodySmall?.color,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
// Sandbox info
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.amber.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.amber.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.info_outline, color: Colors.amber.shade800),
const SizedBox(width: 8),
Text(
'Sandbox Test Data',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 8),
const Text('Card: 4111 1111 1111 1111'),
const Text('Exp: any future date'),
const Text('CVV: 123'),
const Text('Cédula: 9661000'),
const Text('OTP Zimple: 1234'),
],
),
),
const SizedBox(height: 24),
// Action buttons
ElevatedButton.icon(
onPressed: _startSingleBuyCheckout,
icon: const Icon(Icons.shopping_cart),
label: const Text('Full Checkout (with saved cards)'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
const SizedBox(height: 12),
OutlinedButton.icon(
onPressed: _startSimplePayment,
icon: const Icon(Icons.flash_on),
label: const Text('Quick Payment (new card only)'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
const SizedBox(height: 12),
OutlinedButton.icon(
onPressed: _openCardManagement,
icon: const Icon(Icons.credit_card),
label: const Text('Manage Cards'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
// Last result
if (_lastResult != null) ...[
const SizedBox(height: 32),
const Divider(),
const SizedBox(height: 16),
Text(
'Last Result',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: _lastResult!.isSuccess
? Colors.green.shade50
: _lastResult!.isCancelled
? Colors.grey.shade100
: Colors.red.shade50,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
_lastResult!.isSuccess
? Icons.check_circle
: _lastResult!.isCancelled
? Icons.cancel
: Icons.error,
color: _lastResult!.isSuccess
? Colors.green
: _lastResult!.isCancelled
? Colors.grey
: Colors.red,
),
const SizedBox(width: 8),
Text(
_lastResult!.status.name.toUpperCase(),
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
if (_lastResult!.processId != null) ...[
const SizedBox(height: 4),
Text('Process ID: ${_lastResult!.processId}'),
],
if (_lastResult!.authorizationNumber != null) ...[
const SizedBox(height: 4),
Text('Auth: ${_lastResult!.authorizationNumber}'),
],
if (_lastResult!.responseDescription != null) ...[
const SizedBox(height: 4),
Text('${_lastResult!.responseDescription}'),
],
],
),
),
],
],
),
),
);
}
}