amwal_pay_sdk 1.0.70 copy "amwal_pay_sdk: ^1.0.70" to clipboard
amwal_pay_sdk: ^1.0.70 copied to clipboard

amwal pay sdk

example/lib/main.dart

import 'dart:async';

import 'package:amwal_pay_sdk/amwal_pay_sdk.dart';
import 'package:amwal_pay_sdk/amwal_sdk_settings/amwal_sdk_settings.dart';
import 'package:amwal_pay_sdk/core/enums/transaction_type.dart';
import 'package:amwal_pay_sdk/core/networking/constants.dart';
import 'package:amwal_pay_sdk/core/networking/dio_client.dart';
import 'package:amwal_pay_sdk/core/networking/secure_hash_interceptor.dart';
import 'package:amwal_pay_sdk/localization/locale_utils.dart';
// import 'package:chucker_flutter/chucker_flutter.dart';
import 'package:dio/dio.dart';
import 'package:example/currency_model.dart';
import 'package:example/drop_down_form.dart';
import 'package:example/text_form.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MediaQuery(
      data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling),
      child: MaterialApp(
        title: 'Amwal pay Demo',
        navigatorObservers: [
          AmwalSdkNavigator.amwalNavigatorObserver,
          // ChuckerFlutter.navigatorObserver
        ],
        theme: ThemeData(
          useMaterial3: false,
          primarySwatch: Colors.blue,
        ),
        home: const DemoScreen(),
      ),
    );
  }
}

class DemoScreen extends StatefulWidget {
  const DemoScreen({Key? key}) : super(key: key);

  @override
  State<DemoScreen> createState() => _DemoScreenState();
}

class _DemoScreenState extends State<DemoScreen> {
  late TextEditingController _currencyController;
  late TextEditingController _amountController;
  late TextEditingController _merchantIdController;
  late TextEditingController _terminalController;
  late TextEditingController _secureHashController;
  late TextEditingController _languageController;
  late TextEditingController _transactionTypeController;

  late GlobalKey<FormState> _formKey;
  late Environment sdkEnv;

  Timer? _timer;
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _formKey = GlobalKey<FormState>();

    /// card terminal => 6942344
    /// wallet terminal => 68341808775

    _amountController = TextEditingController(text: '1');
    _currencyController = TextEditingController(text: 'OMR');
    _languageController = TextEditingController(text: 'en');
    _transactionTypeController = TextEditingController(text: 'CARD || Wallet');
    _merchantIdController = TextEditingController(text: '84131');
    _terminalController = TextEditingController(text: '811018');
    _secureHashController = TextEditingController(
      text: '8570CEED656C8818E4A7CE04F22206358F272DAD5F0227D322B654675ABF8F83',
    );
    sdkEnv = Environment.UAT;
  }

  Future<String?> getSDKSessionToken({
    required String merchantId,
    required String secureHashValue,
    String? customerId,
  }) async {
    var webhookUrl = '';

    if (sdkEnv == Environment.SIT) {
      webhookUrl = 'https://test.amwalpg.com:24443/';
    } else if (sdkEnv == Environment.UAT) {
      webhookUrl = 'https://test.amwalpg.com:14443/';
    } else if (sdkEnv == Environment.PROD) {
      webhookUrl = 'https://webhook.amwalpg.com/';
    }

    try {
      final dio = Dio(
        BaseOptions(
          baseUrl: webhookUrl,
          headers: {
            'authority': 'localhost',
            'accept': 'text/plain',
            'accept-language': 'en-US,en;q=0.9',
            'content-type': 'application/json',
          },
        ),
      );

      // dio.interceptors.add(ChuckerDioInterceptor());
      // DioClient.dio?.interceptors.add(ChuckerDioInterceptor());

      var sec = SecureHashInterceptor.clearSecureHash(secureHashValue, {
        'merchantId': merchantId,
        'customerId': customerId,
      });
      debugPrint('Request [POST] => URL: ${webhookUrl+SDKNetworkConstants.getSDKSessionToken}');
      debugPrint('Request Headers: ${dio.options.headers}');
      debugPrint('Request Data: {merchantId: $merchantId, secureHashValue: $sec, customerId: $customerId}');
      final response = await dio.post(
        SDKNetworkConstants.getSDKSessionToken,
        data: {
          'merchantId': merchantId,
          'secureHashValue': sec,
          'customerId': customerId,
        },
      );

      debugPrint('Response [${response.statusCode}] => URL: ${SDKNetworkConstants.getSDKSessionToken}');
      debugPrint('Response Data: ${response.data}');

      if (response.data['success']) {
        return response.data['data']['sessionToken'];
      }
    } on DioException catch (e) {
      debugPrint('Full API Error: ${e.response}');

      final errorList = e.response?.data['errorList'];
      final errorMessage =
          (errorList != null) ? errorList.join(',') : 'Unknown error';
      await _showErrorDialog(errorMessage);
      return null;
    } catch (e) {
      await _showErrorDialog('Something Went Wrong');
      return null;
    }
    return null;
  }

  Future<void> _showErrorDialog(String message) async {
    await showDialog(
      context: context,
      builder: (_) {
        return AlertDialog(
          title: Text('failed'.translate(context)),
          content: Text(message),
        );
      },
    );
  }

  void _onCustomerId(String? customerId) async {
    final instance = await SharedPreferences.getInstance();
    if (customerId != null) {
      await instance.setString('customer_id', customerId);
    }
  }

  Future<String?> _getCustomerId() async {
    final instance = await SharedPreferences.getInstance();
    return instance.getString('customer_id');
  }

  void _onResponse(String? response) {
    debugPrint(response);
  }

  Future<void> initPayment() async {
    try {
      final valid = _formKey.currentState!.validate();
      if (!valid) return;

      var customerId = await _getCustomerId();

      if (customerId == null || customerId.isEmpty || customerId == "null") {
        customerId = null;
      }
      final sessionToken = await getSDKSessionToken(
        merchantId: _merchantIdController.text,
        secureHashValue: _secureHashController.text,
        customerId: customerId,
      );

      if (sessionToken == null) return;

      await AmwalPaySdk.instance.initSdk(
        settings: AmwalSdkSettings(
          environment: sdkEnv,
          sessionToken: sessionToken ?? '',
          currency: _currencyController.text,
          amount: _amountController.text,
          transactionId: const Uuid().v1(),
          merchantId: _merchantIdController.text,
          terminalId: _terminalController.text,
          locale: Locale(_languageController.text),
          isMocked: false,
          transactionType: _getTransactionType(),
          customerCallback: _onCustomerId,
          customerId: customerId,
          onResponse: _onResponse,
        ),
      );
    }  catch (e, stackTrace) {
      debugPrint('Error during payment: $e');
      debugPrint('Stack trace: $stackTrace');
      await _showErrorDialog('Something went wrong during payment');
    }  finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  TransactionType _getTransactionType() {
    switch (_transactionTypeController.text) {
      case 'NFC':
        return TransactionType.nfc;
      case 'APPLE_PAY':
      case 'GOOGLE_PAY':
        return TransactionType.appleOrGooglePay;
      case 'CARD || Wallet':
      default:
        return TransactionType.cardWallet;
    }
  }

  @override
  void dispose() {
    _languageController.dispose();
    _terminalController.dispose();
    _merchantIdController.dispose();
    _secureHashController.dispose();
    _amountController.dispose();
    _currencyController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Amwal Pay Demo'),
        actions: [
          IconButton(
            icon: const Icon(Icons.delete),
            onPressed: () {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('Customer ID removed')),
              );
              // make toast here

              SharedPreferences.getInstance().then((instance) {
                instance.remove('customer_id');
              });
              // Add your onPressed code here!
            },
          ),
        ],
      ),
      body: SizedBox(
        width: size.width,
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              const SizedBox(height: 8),
              Expanded(
                child: Form(
                  key: _formKey,
                  child: SingleChildScrollView(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        TextForm(
                          title: "Merchant Id",
                          controller: _merchantIdController,
                          isNumeric: true,
                          maxLength: 10,
                        ),
                        TextForm(
                          title: "Terminal Id",
                          controller: _terminalController,
                          isNumeric: true,
                          maxLength: 10,
                        ),
                        TextForm(
                          title: "Amount",
                          controller: _amountController,
                          isNumeric: true,
                          maxLength: 6,
                          validator: (value) {
                            if (double.parse(value!) < 0.001) {
                              return 'Invalid Amount';
                            } else {
                              return null;
                            }
                          },
                        ),
                        DropdownForm<CurrencyModel>(
                          title: 'Currency',
                          options: const [
                            CurrencyModel(name: 'OMR', id: '512'),
                          ],
                          valueMapper: (currency) => currency.name,
                          nameMapper: (currency) => currency.name,
                          initialValue:
                              const CurrencyModel(name: 'OMR', id: '512'),
                          onChanged: (currencyId) {
                            _currencyController.text = currencyId ?? '';
                          },
                        ),
                        DropdownForm<String>(
                          title: 'Language',
                          options: const [
                            'ar',
                            'en',
                          ],
                          valueMapper: (lang) => lang,
                          nameMapper: (lang) => lang,
                          initialValue: 'en',
                          onChanged: (currencyId) {
                            _languageController.text = currencyId ?? '';
                          },
                        ),
                        DropdownForm<String>(
                          title: 'Transaction Type',
                          options: [
                            'NFC',
                            'CARD || Wallet',
                            if (Theme.of(context).platform ==
                                TargetPlatform.iOS)
                              'APPLE_PAY',
                            if (Theme.of(context).platform ==
                                TargetPlatform.android)
                              'GOOGLE_PAY',
                          ],
                          valueMapper: (lang) => lang,
                          nameMapper: (lang) => lang,
                          initialValue: 'CARD || Wallet',
                          onChanged: (type) {
                            _transactionTypeController.text = type ?? '';
                          },
                        ),
                        TextForm(
                          title: "Secret Key",
                          controller: _secureHashController,
                        ),
                        const SizedBox(height: 8),
                        // const Text("Select Environment"),
                        const SizedBox(height: 8),
                        DropdownForm<Environment>(
                            title: 'Select Environment',
                            options: Environment.values,
                            valueMapper: (env) => env.index.toString(),
                            nameMapper: (env) => env.name,
                            initialValue: sdkEnv,
                            onChanged: (type) {
                              if (type == null) return;
                              sdkEnv = Environment.values[int.parse(type)];
                            }),
                        // Container(
                        //   decoration: BoxDecoration(
                        //     border: Border.all(
                        //       color: Colors.grey, // Specify the border color
                        //       width: 2.0, // Specify the border width
                        //     ),
                        //     borderRadius: BorderRadius.circular(
                        //       10,
                        //     ), // Optional: Add rounded corners
                        //   ),
                        //   child: Padding(
                        //     padding: const EdgeInsets.all(4.0),
                        //     child: DropdownButton<String>(
                        //       isExpanded: true,
                        //       value: dropdownValue,
                        //       onChanged: (String? newValue) {
                        //         setState(() {
                        //           dropdownValue = newValue!;
                        //           switch (dropdownValue) {
                        //             case 'SIT':
                        //               sdkEnv = Environment.SIT;
                        //               break;
                        //             case 'UAT':
                        //               sdkEnv = Environment.UAT;
                        //               break;
                        //             case 'PROD':
                        //               sdkEnv = Environment.PROD;
                        //               break;
                        //             default:
                        //               sdkEnv = Environment.PROD;
                        //               break;
                        //           }
                        //         });
                        //       },
                        //       items: Environment.values
                        //           .map<DropdownMenuItem<String>>(
                        //               (Environment env) {
                        //         return DropdownMenuItem<String>(
                        //           value: env.name,
                        //           child: Text(env.name),
                        //         );
                        //       }).toList(),
                        //     ),
                        //   ),
                        // ),
                      ],
                    ),
                  ),
                ),
              ),
              ElevatedButton(
                onPressed: _isLoading
                    ? null
                    : () async {
                        setState(() {
                          _isLoading = true;
                        });
                        if (_timer?.isActive ?? false) _timer?.cancel();
                        _timer = Timer(const Duration(milliseconds: 300),
                            () async => await initPayment());
                      },
                style: ElevatedButton.styleFrom(
                  minimumSize: const Size(double.infinity, 56),
                  padding: const EdgeInsets.symmetric(vertical: 16),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(12),
                  ),
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    if (_isLoading)
                      const SizedBox(
                        width: 20,
                        height: 20,
                        child: CircularProgressIndicator(
                          strokeWidth: 2,
                          valueColor:
                              AlwaysStoppedAnimation<Color>(Colors.white),
                        ),
                      )
                    else
                      const Text(
                        'Initiate Payment Demo',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.w600,
                        ),
                      ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}