pine_payment_sdk 1.0.0 copy "pine_payment_sdk: ^1.0.0" to clipboard
pine_payment_sdk: ^1.0.0 copied to clipboard

Flutter plugin for fullscreen payment WebView flow with UPI intent handling and callback-based results.

example/lib/main.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:pine_payment_sdk/pine_payment_sdk.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Pine Payment SDK Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF005C4B)),
        useMaterial3: true,
      ),
      home: const PaymentDemoPage(),
    );
  }
}

class PaymentDemoPage extends StatefulWidget {
  const PaymentDemoPage({super.key});

  @override
  State<PaymentDemoPage> createState() => _PaymentDemoPageState();
}

class _PaymentDemoPageState extends State<PaymentDemoPage> {
  static const String _defaultCheckoutRedirectUrl =
      'https://pluraluat.v2.pinepg.in/api/v3/checkout-bff/redirect/checkout?token=V3_VhNtSoX8fgDVJl4J9JXC%2FmNUkgV0YzvzJD56Kk1v9MExAc5yhXJzGyt2hdntTMK1';

  PaymentResult? _lastResult;
  String? _lastResultJson;
  String? _error;
  bool _isProcessing = false;
  late final TextEditingController _customPaymentUrlController;

  @override
  void initState() {
    super.initState();
    _customPaymentUrlController = TextEditingController(
      text: _defaultCheckoutRedirectUrl,
    );
  }

  @override
  void dispose() {
    _customPaymentUrlController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Pine Payment SDK Example')),
      body: SafeArea(
        child: ListView(
          padding: const EdgeInsets.all(16),
          children: <Widget>[
            const Text(
              'Merchant simulation: paste the checkout redirect URL from your backend, '
              'tap Start Payment, and the SDK handles WebView + UPI intent switching.',
            ),
            const SizedBox(height: 16),
            Card(
              child: Padding(
                padding: const EdgeInsets.all(12),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    const Text(
                      'Checkout Redirect URL',
                      style: TextStyle(fontWeight: FontWeight.w700),
                    ),
                    const SizedBox(height: 8),
                    TextField(
                      controller: _customPaymentUrlController,
                      enabled: !_isProcessing,
                      maxLines: 3,
                      decoration: const InputDecoration(
                        labelText: 'Redirect URL from merchant backend',
                        border: OutlineInputBorder(),
                      ),
                    ),
                    const SizedBox(height: 8),
                    FilledButton(
                      onPressed: _isProcessing
                          ? null
                          : () {
                              _startCustomPaymentFromInput();
                            },
                      child: const Text('Start Payment'),
                    ),
                  ],
                ),
              ),
            ),
            if (_isProcessing)
              const Padding(
                padding: EdgeInsets.only(top: 16),
                child: Center(child: CircularProgressIndicator()),
              ),
            const SizedBox(height: 24),
            _buildResultCard(),
            if (_error != null) ...<Widget>[
              const SizedBox(height: 16),
              Text(
                _error!,
                style: TextStyle(
                  color: Theme.of(context).colorScheme.error,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ],
          ],
        ),
      ),
    );
  }

  Future<void> _startCustomPaymentFromInput() async {
    final String redirectUrl = _customPaymentUrlController.text.trim();
    if (redirectUrl.isEmpty) {
      setState(() {
        _error = 'Please enter a checkout redirect URL.';
      });
      return;
    }

    setState(() {
      _isProcessing = true;
      _error = null;
    });

    try {
      final PaymentResult result = await PinePaymentSdk.startPaymentFromRedirectUrl(
        redirectUrl: redirectUrl,
        appBarTitle: 'Complete Payment',
      );

      if (!mounted) {
        return;
      }

      setState(() {
        _lastResult = result;
        _lastResultJson = const JsonEncoder.withIndent('  ').convert(result.toJson());
      });

      debugPrint('PinePaymentSdk result => ${result.toJson()}');
    } catch (error) {
      if (!mounted) {
        return;
      }

      setState(() {
        _error = 'Unable to start custom URL payment flow: $error';
      });

      debugPrint('PinePaymentSdk error => $error');
    } finally {
      if (mounted) {
        setState(() {
          _isProcessing = false;
        });
      }
    }
  }

  Widget _buildResultCard() {
    if (_lastResult == null) {
      return const Card(
        child: Padding(
          padding: EdgeInsets.all(16),
          child: Text('No payment result yet.'),
        ),
      );
    }

    final PaymentResult result = _lastResult!;
    final Color statusColor = result.success ? Colors.green : Colors.red;

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Row(
              children: <Widget>[
                Icon(
                  result.success ? Icons.check_circle : Icons.cancel,
                  color: statusColor,
                ),
                const SizedBox(width: 8),
                Text(
                  'Status: ${result.status}',
                  style: TextStyle(
                    color: statusColor,
                    fontWeight: FontWeight.w700,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 12),
            Text('Success: ${result.success}'),
            Text('Final URL: ${result.finalUrl}'),
            Text('Transaction ID: ${result.transactionId ?? '-'}'),
            Text('Message: ${result.message ?? '-'}'),
            if (_lastResultJson != null) ...<Widget>[
              const SizedBox(height: 12),
              const Text(
                'Raw PaymentResult',
                style: TextStyle(fontWeight: FontWeight.w700),
              ),
              const SizedBox(height: 6),
              SelectableText(_lastResultJson!),
            ],
          ],
        ),
      ),
    );
  }
}
0
likes
130
points
19
downloads

Documentation

API reference

Publisher

verified publisherpinelabsonline.com

Weekly Downloads

Flutter plugin for fullscreen payment WebView flow with UPI intent handling and callback-based results.

Homepage

License

MIT (license)

Dependencies

flutter, url_launcher, webview_flutter, webview_flutter_android, webview_flutter_wkwebview

More

Packages that depend on pine_payment_sdk

Packages that implement pine_payment_sdk