payu_ppi_flutter 1.2.0-alpha.4 copy "payu_ppi_flutter: ^1.2.0-alpha.4" to clipboard
payu_ppi_flutter: ^1.2.0-alpha.4 copied to clipboard

The PPI for Flutter provides a complete Mobile solution for the Flutter App and allows you to integrate Virtual CARD DISPLAY POWERED BY PayU for iOS and Android.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:payu_ppi_flutter_example/HashService.dart';
import 'package:payu_ppi_flutter/payu_ppi_flutter.dart';
import 'package:payu_ppi_flutter/PayUConstantKeys.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> implements PayUPPIProtocol {
  late PayUPPIFlutter _ppi;

  // Default values aligned with iOS OnePayUJSSampleApp ViewController.
  static const _defaultMerchantKey = 'YourKey';
  static const _defaultSalt = 'YourSalt';
  static const _defaultPhone = 'YourNumber';
  static const _defaultEnvironment = 'Test';
  static const _defaultWalletUrn = '1003097';
  static const _defaultWalletIdentifier = 'OLW';
  static const _defaultCustomerName = 'Amrendra';
  static const _defaultPrimaryTheme = '#3394B4';
  static const _defaultSecondaryTheme = '#AD42f5';
  static const _kycModeIdHint = 'digioKyc:10, adharKyc:11';
  static const _defaultKycUrl = 'https://test.payu.in/kyc-demo-url';
  static const _defaultRedirectionUrl = 'https://citruspay-api.pc.enstage-sas.com/login';
  static const _defaultMerchantName = 'Flutter Merchant';

  final _keyController = TextEditingController(text: _defaultMerchantKey);
  final _saltController = TextEditingController(text: _defaultSalt);
  final _phoneController = TextEditingController(text: _defaultPhone);
  final _environmentController = TextEditingController(text: _defaultEnvironment);
  final _requestIdController = TextEditingController();
  final _walletUrnController = TextEditingController(text: _defaultWalletUrn);
  final _walletIdentifierController =
      TextEditingController(text: _defaultWalletIdentifier);
  final _customerNameController =
      TextEditingController(text: _defaultCustomerName);
  final _videoKycModeIdController = TextEditingController();
  final _primaryThemeController = TextEditingController();
  final _secondaryThemeController = TextEditingController();
  final _kycUrlController = TextEditingController(text: _defaultKycUrl);
  final _redirectionUrlController =
      TextEditingController(text: _defaultRedirectionUrl);
  final _merchantNameController =
      TextEditingController(text: _defaultMerchantName);

  bool _kycConsent = true;

  @override
  void initState() {
    super.initState();
    _ppi = PayUPPIFlutter(this);
    _refreshRequestId();
  }

  @override
  void dispose() {
    _keyController.dispose();
    _saltController.dispose();
    _phoneController.dispose();
    _environmentController.dispose();
    _requestIdController.dispose();
    _walletUrnController.dispose();
    _walletIdentifierController.dispose();
    _customerNameController.dispose();
    _videoKycModeIdController.dispose();
    _primaryThemeController.dispose();
    _secondaryThemeController.dispose();
    _kycUrlController.dispose();
    _redirectionUrlController.dispose();
    _merchantNameController.dispose();
    super.dispose();
  }

  /// Generates a fresh request/reference id for each SDK launch.
  void _refreshRequestId() {
    _requestIdController.text = 'payu${DateTime.now().millisecondsSinceEpoch}';
  }

  bool get _isProductionEnvironment {
    return _environmentController.text.trim().toLowerCase() == 'prod';
  }

  /// Builds wallet params used by showCards and initiateCKYC.
  Map<String, dynamic> _buildPayUPPIParams() {
    return {
      PayUPPIParamKey.merchantKey: _keyController.text.trim(),
      PayUPPIParamKey.referenceId: _requestIdController.text.trim(),
      // OnePayUJSEnvironment: production = 0, test = 1
      PayUPPIParamKey.environment: _isProductionEnvironment ? '0' : '1',
      PayUPPIParamKey.mobileNumber: _phoneController.text.trim(),
      PayUPPIParamKey.walletUrn: _walletUrnController.text.trim(),
      PayUPPIParamKey.walletIdentifier: _walletIdentifierController.text.trim(),
    };
  }

  /// Builds standard KYC params; phone field drives customerMobile.
  Map<String, dynamic> _buildKYCParams() {
    return {
      PayUKYCParamKey.clientTxnId: 'TXN${DateTime.now().millisecondsSinceEpoch}',
      PayUKYCParamKey.customerMobile: _phoneController.text.trim(),
      PayUKYCParamKey.consent:  _kycConsent ? '1' : '0',
      PayUKYCParamKey.clientId: '2070',
      PayUKYCParamKey.entityId: '503',
      PayUKYCParamKey.bankId: '6050',
      PayUKYCParamKey.modeId: _videoKycModeIdController.text.trim(),
      PayUKYCParamKey.redirectionUrl: _redirectionUrlController.text.trim(),
      PayUKYCParamKey.secureCode: 'NcRfUjXn2r5u8x',
      PayUKYCParamKey.environment: _isProductionEnvironment
          ? PayUEnvironment.production
          : PayUEnvironment.test,
    };
  }

  /// Resolves videoKycModeId — optional; allowed values: empty (nil), 10, or 11.
  String? _resolveVideoKycModeId() {
    final modeId = _videoKycModeIdController.text.trim();
    if (modeId.isEmpty) {
      return null;
    }
    return modeId;
  }

  /// Builds CKYC params from CKYC section fields.
  Map<String, dynamic> _buildCKYCParams() {
    final primaryTheme = _primaryThemeController.text.trim().isEmpty
        ? _defaultPrimaryTheme
        : _primaryThemeController.text.trim();
    final secondaryTheme = _secondaryThemeController.text.trim().isEmpty
        ? _defaultSecondaryTheme
        : _secondaryThemeController.text.trim();
    final videoKycModeId = _resolveVideoKycModeId();

    final ckycParams = <String, dynamic>{
     PayUPPIParamKey.merchantKey: _keyController.text.trim(),
      PayUPPIParamKey.referenceId: _requestIdController.text.trim(),
     PayUPPIParamKey.walletIdentifier: _walletIdentifierController.text.trim(),
      PayUPPIParamKey.walletUrn: _walletUrnController.text.trim(),
      PayUCKYCParamKey.customerMobile: _phoneController.text.trim(),
      PayUCKYCParamKey.customerName: _customerNameController.text.trim(),
      PayUCKYCParamKey.kycConsent: _kycConsent ? '1' : '0',
      PayUCKYCParamKey.primaryTheme: primaryTheme,
      PayUCKYCParamKey.secondaryTheme: secondaryTheme,
      PayUCKYCParamKey.redirectionUrl: _redirectionUrlController.text.trim(),
      PayUCKYCParamKey.merchantName: _merchantNameController.text.trim(),
      PayUCKYCParamKey.environment: _isProductionEnvironment
          ? PayUEnvironment.production
          : PayUEnvironment.test,
    };

    // videoKycModeId is optional — nil, 10 (digioKyc), or 11 (adharKyc).
    if (videoKycModeId != null) {
      ckycParams[PayUCKYCParamKey.videoKycModeId] = videoKycModeId;
    }

    return ckycParams;
  }

  /// Builds KYC-with-URL params.
  Map<String, dynamic> _buildKYCUrlParams() {
    return {
      PayUKYCUrlParamKey.redirectionUrl: _redirectionUrlController.text.trim(),
      PayUKYCUrlParamKey.kycUrl: _kycUrlController.text.trim(),
    };
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        // Keep action buttons pinned at bottom when keyboard is visible.
        resizeToAvoidBottomInset: false,
        appBar: AppBar(
          title: const Text('PayU PPI SDK'),
        ),
        body: GestureDetector(
          onTap: () => FocusScope.of(context).unfocus(),
          behavior: HitTestBehavior.translucent,
          child: Column(
            children: [
              Expanded(
                child: SingleChildScrollView(
                  keyboardDismissBehavior:
                      ScrollViewKeyboardDismissBehavior.onDrag,
                  child: Column(
                    children: [
                      _buildFieldRow('Key', _keyController),
                      _buildFieldRow('Salt', _saltController),
                      _buildFieldRow('Phone', _phoneController),
                      _buildFieldRow('Environment', _environmentController,
                          hint: 'Test or Prod'),
                      _buildFieldRow('Request ID', _requestIdController),
                      _buildFieldRow('WalletUrn', _walletUrnController),
                      _buildFieldRow(
                          'WalletIdentifier', _walletIdentifierController),
                      const Divider(height: 24),
                      _buildFieldRow(
                          'customer name', _customerNameController),
                      _buildFieldRow(
                          'Merchant Name', _merchantNameController),
                      _buildFieldRow(
                        'Kyc Mode Id',
                        _videoKycModeIdController,
                        hint: _kycModeIdHint,
                        keyboardType: TextInputType.number,
                      ),
                      _buildSwitchRow('kyc Consent', _kycConsent, (value) {
                        setState(() => _kycConsent = value);
                      }),
                      _buildFieldRow(
                        'Color (for ckyc)',
                        _primaryThemeController,
                        hint: _defaultPrimaryTheme,
                      ),
                      _buildFieldRow(
                        'secondaryTheme',
                        _secondaryThemeController,
                        hint: _defaultSecondaryTheme,
                      ),
                      const Divider(height: 24),
                      _buildFieldRow('KYC URL', _kycUrlController),
                      _buildFieldRow(
                          'Redirection URL', _redirectionUrlController),
                      const SizedBox(height: 16),
                    ],
                  ),
                ),
              ),
              SafeArea(
                top: false,
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    _buildActionButton(
                      label: 'INITIATE KYC WITH URL',
                      onPressed: () {
                        debugPrint('Launching initiateKYCWithUrl');
                        _ppi.initiateKYCWithUrl(
                            kycUrlParams: _buildKYCUrlParams());
                      },
                    ),
                    _buildActionButton(
                      label: 'initiate CKYC',
                      onPressed: () {
                        debugPrint('Launching initiateCKYC');
                        _ppi.initiateCKYC(ckycParams: _buildCKYCParams());
                      },
                    ),
                    _buildActionButton(
                      label: 'INITIATE KYC',
                      onPressed: () {
                        debugPrint('Launching initiateKYC');
                        _ppi.initiateKYC(kycParams: _buildKYCParams());
                      },
                    ),
                    _buildActionButton(
                      label: 'CARDS',
                      onPressed: () {
                        debugPrint('Launching showCards');
                        _ppi.showCards(payUPPIParams: _buildPayUPPIParams());
                      },
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  /// Label + bordered text field row for editable inputs.
  Widget _buildFieldRow(
    String label,
    TextEditingController controller, {
    String? hint,
    TextInputType keyboardType = TextInputType.text,
  }) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          SizedBox(
            width: 130,
            child: Text(
              label,
              style: const TextStyle(fontSize: 15),
            ),
          ),
          Expanded(
            child: TextField(
              controller: controller,
              keyboardType: keyboardType,
              decoration: InputDecoration(
                hintText: hint,
                hintStyle: TextStyle(
                  fontSize: 14,
                  color: Colors.grey.shade500,
                ),
                filled: true,
                fillColor: Colors.white,
                contentPadding: const EdgeInsets.symmetric(
                  horizontal: 12,
                  vertical: 10,
                ),
                enabledBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(6),
                  borderSide: BorderSide(color: Colors.grey.shade400),
                ),
                focusedBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(6),
                  borderSide: const BorderSide(color: Colors.blue, width: 1.5),
                ),
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(6),
                  borderSide: BorderSide(color: Colors.grey.shade400),
                ),
                isDense: true,
              ),
              style: const TextStyle(fontSize: 15),
              textInputAction: TextInputAction.done,
              onSubmitted: (_) => FocusScope.of(context).unfocus(),
            ),
          ),
        ],
      ),
    );
  }

  /// Toggle row for CKYC consent switch.
  Widget _buildSwitchRow(
    String label,
    bool value,
    ValueChanged<bool> onChanged,
  ) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
      child: Row(
        children: [
          SizedBox(
            width: 130,
            child: Text(
              label,
              style: const TextStyle(fontSize: 15),
            ),
          ),
          Switch(
            value: value,
            onChanged: onChanged,
            activeColor: Colors.green,
          ),
        ],
      ),
    );
  }

  /// Full-width outlined action button using default Flutter style.
  Widget _buildActionButton({
    required String label,
    required VoidCallback onPressed,
  }) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
      child: SizedBox(
        width: double.infinity,
        height: 48,
        child: OutlinedButton(
          onPressed: onPressed,
          child: Text(
            label,
            style: const TextStyle(
              fontSize: 16,
              fontWeight: FontWeight.w600,
            ),
          ),
        ),
      ),
    );
  }

  void _showAlertDialog(String title, String content) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text(title),
          content: SingleChildScrollView(
            child: Text(content),
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('OK'),
            ),
          ],
        );
      },
    );
  }

  @override
  generateHash(Map response) {
    // Pass response param to your backend server; sample uses salt from form.
    final hashResponse = HashService.generateHash(
      response,
      merchantSalt: _saltController.text.trim(),
    );
    _ppi.hashGenerated(hash: hashResponse);
  }

  @override
  onCancel() {
    _refreshRequestId();
    _showAlertDialog('onCancel', 'Cancel By user');
  }

  @override
  onSuccess() {
    _refreshRequestId();
    _showAlertDialog('onSuccess', 'Flow completed successfully');
  }

  @override
  onError(Map? response) {
    _refreshRequestId();
    _showAlertDialog('onError', response.toString());
  }
}
2
likes
125
points
210
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

The PPI for Flutter provides a complete Mobile solution for the Flutter App and allows you to integrate Virtual CARD DISPLAY POWERED BY PayU for iOS and Android.

Homepage

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on payu_ppi_flutter

Packages that implement payu_ppi_flutter