Flutter (pub.dev)
SDK oficial de Venepagos para Flutter. Integra fácilmente pagos en tus aplicaciones Flutter mediante una ventana emergente que maneja todo el flujo de pago de forma segura.
Características
✅ Fácil integración - Solo unas líneas de código ✅ Ventana emergente - WebView modal nativo ✅ Detección automática - Callbacks de éxito/error automáticos ✅ API completa - Crear payment links programáticamente ✅ Tipo seguro - Modelos Dart con tipado fuerte ✅ Responsive - Funciona en móvil y tablet
Instalación
Agrega esta dependencia a tu archivo pubspec.yaml
:
dependencies:
venepagos: ^1.0.1
Luego ejecuta:
flutter pub get
Configuración
1. Obtén tu API Key
Visita tu dashboard de Venepagos para generar tu API key.
2. Configura el SDK
import 'package:venepagos/venepagos.dart';
void main() {
// Configurar Venepagos antes de usar
Venepagos.instance.configure(
apiKey: 'vp_tu_api_key_aqui',
sandboxMode: true, // Solo para desarrollo
);
runApp(MyApp());
}
Uso Básico
Opción 1: Flujo Completo (Recomendado)
Crea el payment link y abre el pago en una sola llamada:
class MyPaymentButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => _iniciarPago(context),
child: Text('Pagar \$29.99'),
);
}
Future<void> _iniciarPago(BuildContext context) async {
try {
final resultado = await Venepagos.instance.createAndOpenPayment(
context: context,
title: 'Suscripción Premium',
description: 'Plan mensual premium con todas las funciones',
amount: 29.99,
currency: 'USD',
metadata: {
'user_id': '12345',
'plan': 'premium',
'source': 'mobile_app',
},
onSuccess: (referencia) {
print('¡Pago exitoso! Referencia: $referencia');
// Aquí puedes actualizar la UI, navegar, etc.
},
onError: (error) {
print('Error en el pago: $error');
// Mostrar mensaje de error al usuario
},
onCancelled: () {
print('Pago cancelado por el usuario');
},
);
// El resultado también está disponible aquí
if (resultado?.isSuccess == true) {
// Pago exitoso
Navigator.pushNamed(context, '/success');
}
} catch (e) {
print('Error creando payment link: $e');
}
}
}
Opción 2: Dos Pasos
Para mayor control, puedes separar la creación del payment link de la apertura del pago:
Future<void> _pagoEnDosPasos(BuildContext context) async {
try {
// Paso 1: Crear payment link
final paymentLink = await Venepagos.instance.createPaymentLink(
title: 'Mi Producto',
amount: 50.0,
currency: 'USD',
expiresAt: DateTime.now().add(Duration(hours: 24)),
metadata: {'order_id': 'ORD-001'},
);
// Paso 2: Abrir pago
final resultado = await Venepagos.instance.openPayment(
context: context,
paymentLink: paymentLink,
onSuccess: (referencia) {
// Lógica de éxito
},
);
} catch (e) {
print('Error: $e');
}
}
Opción 3: Payment Link Existente
Si ya tienes un payment link URL, puedes abrirlo directamente:
Future<void> _abrirPaymentLinkExistente(BuildContext context) async {
final resultado = await Venepagos.instance.openPaymentFromUrl(
context: context,
paymentUrl: 'https://venepagos.com.ve/pagar/pl_abc123',
onSuccess: (referencia) {
print('Pago completado: $referencia');
},
);
}
Ejemplos Avanzados
Manejo de Estados
class PaymentScreen extends StatefulWidget {
@override
_PaymentScreenState createState() => _PaymentScreenState();
}
class _PaymentScreenState extends State<PaymentScreen> {
bool _isLoading = false;
String? _error;
String? _successReference;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Checkout')),
body: Column(
children: [
if (_error != null)
Container(
padding: EdgeInsets.all(16),
color: Colors.red[100],
child: Text('Error: $_error'),
),
if (_successReference != null)
Container(
padding: EdgeInsets.all(16),
color: Colors.green[100],
child: Text('¡Pago exitoso! Ref: $_successReference'),
),
ElevatedButton(
onPressed: _isLoading ? null : () => _procesarPago(),
child: _isLoading
? CircularProgressIndicator()
: Text('Procesar Pago'),
),
],
),
);
}
Future<void> _procesarPago() async {
setState(() {
_isLoading = true;
_error = null;
_successReference = null;
});
try {
await Venepagos.instance.createAndOpenPayment(
context: context,
title: 'Compra en App',
amount: 99.99,
currency: 'USD',
onSuccess: (referencia) {
setState(() {
_successReference = referencia;
_isLoading = false;
});
_enviarWebhookConfirmacion(referencia);
},
onError: (error) {
setState(() {
_error = error;
_isLoading = false;
});
},
onCancelled: () {
setState(() {
_isLoading = false;
});
},
);
} catch (e) {
setState(() {
_error = e.toString();
_isLoading = false;
});
}
}
void _enviarWebhookConfirmacion(String referencia) {
// Aquí puedes enviar la confirmación a tu backend
print('Enviando confirmación para: $referencia');
}
}
Validación de API Key
class ConfigScreen extends StatefulWidget {
@override
_ConfigScreenState createState() => _ConfigScreenState();
}
class _ConfigScreenState extends State<ConfigScreen> {
final _apiKeyController = TextEditingController();
bool _isValidating = false;
bool? _isValid;
Future<void> _validarApiKey() async {
final apiKey = _apiKeyController.text.trim();
if (apiKey.isEmpty) return;
setState(() {
_isValidating = true;
_isValid = null;
});
try {
Venepagos.instance.configure(apiKey: apiKey);
final isValid = await Venepagos.instance.testApiKey();
setState(() {
_isValid = isValid;
_isValidating = false;
});
if (isValid) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('API Key válida ✅')),
);
}
} catch (e) {
setState(() {
_isValid = false;
_isValidating = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Configuración')),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
TextField(
controller: _apiKeyController,
decoration: InputDecoration(
labelText: 'API Key de Venepagos',
hintText: 'vp_...',
suffixIcon: _isValid == null
? null
: Icon(
_isValid! ? Icons.check_circle : Icons.error,
color: _isValid! ? Colors.green : Colors.red,
),
),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _isValidating ? null : _validarApiKey,
child: _isValidating
? CircularProgressIndicator()
: Text('Validar API Key'),
),
],
),
),
);
}
}
Metadata y Webhooks
Future<void> _pagoConMetadata(BuildContext context) async {
final usuario = getCurrentUser(); // Tu función para obtener el usuario
final resultado = await Venepagos.instance.createAndOpenPayment(
context: context,
title: 'Suscripción Pro',
amount: 49.99,
currency: 'USD',
metadata: {
// Información del usuario
'user_id': usuario.id,
'user_email': usuario.email,
'user_tier': 'premium',
// Información del pedido
'order_id': 'ORD-${DateTime.now().millisecondsSinceEpoch}',
'plan_type': 'pro_monthly',
'billing_cycle': 'monthly',
// Información de tracking
'source': 'mobile_app',
'platform': Platform.isIOS ? 'ios' : 'android',
'app_version': '1.2.3',
// Datos personalizados
'campaign_id': 'summer_2024',
'referral_code': usuario.referralCode,
},
onSuccess: (referencia) {
// Los webhooks incluirán automáticamente toda la metadata
_actualizarSuscripcionUsuario(usuario.id, 'pro');
},
);
}
API Reference
Venepagos.instance
configure()
void configure({
required String apiKey,
bool sandboxMode = false,
String? baseUrl,
})
createPaymentLink()
Future<PaymentLink> createPaymentLink({
required String title,
String? description,
double? amount,
String currency = 'USD',
DateTime? expiresAt,
Map<String, dynamic>? metadata,
})
openPayment()
Future<PaymentResult?> openPayment({
required BuildContext context,
required PaymentLink paymentLink,
Function(String reference)? onSuccess,
Function(String error)? onError,
VoidCallback? onCancelled,
})
createAndOpenPayment()
Future<PaymentResult?> createAndOpenPayment({
required BuildContext context,
required String title,
// ... mismos parámetros que createPaymentLink + callbacks
})
testApiKey()
Future<bool> testApiKey()
Modelos
PaymentLink
class PaymentLink {
final String id;
final String title;
final String? description;
final double? amount;
final String currency;
final String url;
final bool isActive;
final DateTime? expiresAt;
final DateTime createdAt;
final Map<String, dynamic>? metadata;
}
PaymentResult
class PaymentResult {
final PaymentResultType type; // success, error, cancelled
final String? reference;
final String? errorMessage;
final Map<String, dynamic>? data;
bool get isSuccess;
bool get isError;
bool get isCancelled;
}
Configuración para Producción
iOS (ios/Runner/Info.plist)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>venepagos.com.ve</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<false/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
</dict>
</dict>
</dict>
Android (android/app/src/main/AndroidManifest.xml)
<uses-permission android:name="android.permission.INTERNET" />
Solución de Problemas
Error: "API key no válida"
- Verifica que tu API key comience con
vp_
- Asegúrate de estar usando la API key correcta (producción vs sandbox)
- Verifica que la API key no haya expirado
WebView no carga
- Verifica tu conexión a internet
- Asegúrate de que los permisos de red estén configurados
- En iOS, verifica la configuración de App Transport Security
Callbacks no se ejecutan
- Verifica que estés usando la URL correcta de Venepagos
- El SDK detecta automáticamente las páginas de confirmación y error