diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96486fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..93fddf2 --- /dev/null +++ b/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 85684f9300908116a78138ea4c6036c35c9a1236 + channel: stable + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 85684f9300908116a78138ea4c6036c35c9a1236 + base_revision: 85684f9300908116a78138ea4c6036c35c9a1236 + - platform: android + create_revision: 85684f9300908116a78138ea4c6036c35c9a1236 + base_revision: 85684f9300908116a78138ea4c6036c35c9a1236 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..27f4cb9 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "easy_upi_payment", + "request": "launch", + "type": "dart" + }, + { + "name": "easy_upi_payment (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "easy_upi_payment (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + }, + { + "name": "example", + "cwd": "example", + "request": "launch", + "type": "dart" + }, + { + "name": "example (profile mode)", + "cwd": "example", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "example (release mode)", + "cwd": "example", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ceddbb7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "mandir" + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..41cc7d8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ea1f79 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# easy_upi_payment + +A new flutter plugin to make upi payment (only on Android). + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..161bdcd --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..88ea3de --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,51 @@ +group 'com.pcu.easy_upi_payment' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 31 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + minSdkVersion 19 + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "dev.shreyaspatil.EasyUpiPayment:EasyUpiPayment:3.0.3" +} diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..0f631b3 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'easy_upi_payment' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9faaae7 --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/android/src/main/kotlin/com/pcu/easy_upi_payment/EasyUpiPaymentPlugin.kt b/android/src/main/kotlin/com/pcu/easy_upi_payment/EasyUpiPaymentPlugin.kt new file mode 100644 index 0000000..98aeeb5 --- /dev/null +++ b/android/src/main/kotlin/com/pcu/easy_upi_payment/EasyUpiPaymentPlugin.kt @@ -0,0 +1,103 @@ +package com.pcu.easy_upi_payment + +import android.app.Activity +import androidx.annotation.NonNull +import dev.shreyaspatil.easyupipayment.EasyUpiPayment +import dev.shreyaspatil.easyupipayment.listener.PaymentStatusListener +import dev.shreyaspatil.easyupipayment.model.TransactionDetails +import dev.shreyaspatil.easyupipayment.model.TransactionStatus + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.activity.ActivityAware +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result + +/** EasyUpiPaymentPlugin */ +class EasyUpiPaymentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, PaymentStatusListener { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel : MethodChannel + private var activity: Activity? = null + private lateinit var result: Result + private var easyUpiPayment: EasyUpiPayment? = null + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "easy_upi_payment") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + if (call.method == "startPayment" && activity != null) { + this.result = result + easyUpiPayment = EasyUpiPayment(activity!!) { + this.payeeVpa = call.argument("payeeVpa") + this.payeeName = call.argument("payeeName") + this.payeeMerchantCode = call.argument("payeeMerchantCode") + this.transactionId = call.argument("transactionId") + this.transactionRefId = call.argument("transactionRefId") + this.description = call.argument("description") + this.amount = call.argument("amount") + } + + easyUpiPayment?.setPaymentStatusListener(this); + easyUpiPayment?.startPayment() + + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } + + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + activity = binding.activity; + } + + override fun onDetachedFromActivityForConfigChanges() { + activity = null; + } + + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + activity = binding.activity; + } + + override fun onDetachedFromActivity() { + activity = null; + } + + override fun onTransactionCancelled() { + result.error("Cancelled", "Transaction cancelled by user", "Transaction was cancelled by the user"); + } + + override fun onTransactionCompleted(transactionDetails: TransactionDetails) { + when(transactionDetails.transactionStatus) { + TransactionStatus.FAILURE -> result.error( + "Failure", + "Transaction is failed.", + "Transaction is failed." + ) + TransactionStatus.SUCCESS -> result.success( + hashMapOf( + "transactionId" to transactionDetails.transactionId, + "responseCode" to transactionDetails.responseCode, + "approvalRefNo" to transactionDetails.approvalRefNo, + "transactionRefId" to transactionDetails.transactionRefId, + "amount" to transactionDetails.amount, + ), + ) + TransactionStatus.SUBMITTED -> result.error( + "Submitted", + "Transaction is in PENDING state. Money might get deducted from user’s account but not yet deposited in payee’s account.", + "Transaction is in PENDING state. Money might get deducted from user’s account but not yet deposited in payee’s account." + ) + } + easyUpiPayment?.removePaymentStatusListener(); + } +} diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..a8e938c --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,47 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..c7e9833 --- /dev/null +++ b/example/README.md @@ -0,0 +1,16 @@ +# easy_upi_payment_example + +Demonstrates how to use the easy_upi_payment plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle new file mode 100644 index 0000000..a301de5 --- /dev/null +++ b/example/android/app/build.gradle @@ -0,0 +1,72 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.pcu.easy_upi_payment_example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion 19 + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "dev.shreyaspatil.EasyUpiPayment:EasyUpiPayment:3.0.3" +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..6e85fab --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a228c71 --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/kotlin/com/pcu/easy_upi_payment_example/MainActivity.kt b/example/android/app/src/main/kotlin/com/pcu/easy_upi_payment_example/MainActivity.kt new file mode 100644 index 0000000..b4e97b1 --- /dev/null +++ b/example/android/app/src/main/kotlin/com/pcu/easy_upi_payment_example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.pcu.easy_upi_payment_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..6e85fab --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle new file mode 100644 index 0000000..83ae220 --- /dev/null +++ b/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..cc5527d --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 0000000..d7c10f5 --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,67 @@ +import 'dart:developer'; + +import 'package:easy_upi_payment/easy_upi_payment.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: _isLoading + ? const CircularProgressIndicator() + : ElevatedButton( + onPressed: startTransaction, + child: const Text("Pay Now"), + ), + ), + ), + ); + } + + bool _isLoading = false; + + Future startTransaction() async { + setState(() { + _isLoading = true; + }); + + try { + final transactionDetail = await EasyUpiPaymentPlatform.instance.startPayment( + EasyUpiPaymentModel( + // TODO: add payeeVpa + payeeVpa: 'gaurav.jajoo@upi', + // TODO: add payeeName + payeeName: 'Gaurav Jajoo', + // TODO: add amount + amount: '1.0', + description: 'donate to Ram Mandir', + ), + ); + + print(transactionDetail.toString()); + } on PlatformException catch (e) { + log('exception is $e'); + } finally { + setState(() { + _isLoading = false; + }); + } + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..c747883 --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,182 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.2" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + easy_upi_payment: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.9" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" +sdks: + dart: ">=2.17.5 <3.0.0" + flutter: ">=2.5.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..83d04e9 --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,84 @@ +name: easy_upi_payment_example +description: Demonstrates how to use the easy_upi_payment plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ">=2.17.5 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + easy_upi_payment: + # When depending on this package from a real application you should use: + # easy_upi_payment: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 0000000..0185b28 --- /dev/null +++ b/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:easy_upi_payment_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => widget is Text && + widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/lib/easy_upi_payment.dart b/lib/easy_upi_payment.dart new file mode 100644 index 0000000..0e031de --- /dev/null +++ b/lib/easy_upi_payment.dart @@ -0,0 +1,3 @@ +export 'src/easy_upi_payment_platform_interface.dart'; +export 'src/model/easy_upi_payment_model.dart'; +export 'src/model/transaction_detail_model.dart'; diff --git a/lib/src/easy_upi_payment_method_channel.dart b/lib/src/easy_upi_payment_method_channel.dart new file mode 100644 index 0000000..50b65fc --- /dev/null +++ b/lib/src/easy_upi_payment_method_channel.dart @@ -0,0 +1,28 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'easy_upi_payment_platform_interface.dart'; +import 'model/easy_upi_payment_model.dart'; +import 'model/transaction_detail_model.dart'; + +/// An implementation of [EasyUpiPaymentPlatform] that uses method channels. +class MethodChannelEasyUpiPayment extends EasyUpiPaymentPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('easy_upi_payment'); + + @override + Future startPayment( + EasyUpiPaymentModel easyUpiPaymentModel, + ) async { + final data = await methodChannel.invokeMethod( + 'startPayment', + easyUpiPaymentModel.toMap(), + ); + if (data != null) { + return TransactionDetailModel.fromMap(data); + } else { + throw PlatformException(code: 'No response'); + } + } +} diff --git a/lib/src/easy_upi_payment_platform_interface.dart b/lib/src/easy_upi_payment_platform_interface.dart new file mode 100644 index 0000000..70d00d9 --- /dev/null +++ b/lib/src/easy_upi_payment_platform_interface.dart @@ -0,0 +1,33 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'easy_upi_payment_method_channel.dart'; +import 'model/easy_upi_payment_model.dart'; +import 'model/transaction_detail_model.dart'; + +abstract class EasyUpiPaymentPlatform extends PlatformInterface { + /// Constructs a EasyUpiPaymentPlatform. + EasyUpiPaymentPlatform() : super(token: _token); + + static final Object _token = Object(); + + static EasyUpiPaymentPlatform _instance = MethodChannelEasyUpiPayment(); + + /// The default instance of [EasyUpiPaymentPlatform] to use. + /// + /// Defaults to [MethodChannelEasyUpiPayment]. + static EasyUpiPaymentPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [EasyUpiPaymentPlatform] when + /// they register themselves. + static set instance(EasyUpiPaymentPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future startPayment( + EasyUpiPaymentModel easyUpiPaymentModel, + ) { + throw UnimplementedError('startPayment() has not been implemented.'); + } +} diff --git a/lib/src/model/easy_upi_payment_model.dart b/lib/src/model/easy_upi_payment_model.dart new file mode 100644 index 0000000..75d5bfb --- /dev/null +++ b/lib/src/model/easy_upi_payment_model.dart @@ -0,0 +1,48 @@ +class EasyUpiPaymentModel { + EasyUpiPaymentModel({ + required this.payeeVpa, + required this.payeeName, + required this.amount, + required this.description, + this.payeeMerchantCode, + this.transactionId, + this.transactionRefId, + }); + + /// It takes name of the payee like Gaurav Jajoo + final String payeeVpa; + + /// It takes VPA address of payee for e.g. gaurav.jajoo@upi + final String payeeName; + + /// Payee Merchant code. This should be valid. + final String? payeeMerchantCode; + + /// This field is used in Merchant Payments generated by PSPs. + /// if provided null then it will use [DateTime.now().microsecondsSinceEpoch] + final String? transactionId; + + /// Transaction reference ID. This could be order number, subscription number, Bill ID, booking ID, insurance renewal reference, etc. Needed for merchant transactions and dynamic URL generation. This is mandatory because absence of this field generated error in apps like PhonePe + /// if provided null then it will use [DateTime.now().millisecondsSinceEpoch] + final String? transactionRefId; + + /// To provide a valid small note or description about payment. + /// for e.g. for donating to Ram-mandir + final String? description; + + /// It takes the amount in String decimal format (xx.xx) to be paid. + /// For e.g. 90.88 will pay Rs. 90.88. + final String amount; + + Map toMap() { + return { + 'payeeVpa': payeeVpa, + 'payeeName': payeeName, + 'payeeMerchantCode': payeeMerchantCode ?? '', + 'transactionId': transactionId ?? DateTime.now().microsecondsSinceEpoch.toString(), + 'transactionRefId': transactionRefId ?? DateTime.now().millisecondsSinceEpoch.toString(), + 'description': description, + 'amount': amount, + }; + } +} diff --git a/lib/src/model/transaction_detail_model.dart b/lib/src/model/transaction_detail_model.dart new file mode 100644 index 0000000..acbb30a --- /dev/null +++ b/lib/src/model/transaction_detail_model.dart @@ -0,0 +1,61 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +class TransactionDetailModel { + const TransactionDetailModel({ + required this.transactionId, + required this.responseCode, + required this.approvalRefNo, + required this.transactionRefId, + required this.amount, + }); + + factory TransactionDetailModel.fromMap(Map data) { + return TransactionDetailModel( + transactionId: data['transactionId'] as String, + responseCode: data['responseCode'] as String, + approvalRefNo: data['approvalRefNo'] as String, + transactionRefId: data['transactionRefId'] as String, + amount: data['amount'] as String, + ); + } + + /// Returns Transaction ID + final String transactionId; + + /// Returns UPI Response Code + final String responseCode; + + /// Returns UPI Approval Reference Number (beneficiary) + final String approvalRefNo; + + /// Returns Transaction reference ID passed in input + final String transactionRefId; + + /// Returns Transaction amount + final String amount; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is TransactionDetailModel && + other.transactionId == transactionId && + other.responseCode == responseCode && + other.approvalRefNo == approvalRefNo && + other.transactionRefId == transactionRefId && + other.amount == amount; + } + + @override + int get hashCode { + return transactionId.hashCode ^ + responseCode.hashCode ^ + approvalRefNo.hashCode ^ + transactionRefId.hashCode ^ + amount.hashCode; + } + + @override + String toString() { + return 'TransactionDetailModel(transactionId: $transactionId, responseCode: $responseCode, approvalRefNo: $approvalRefNo, transactionRefId: $transactionRefId, amount: $amount)'; + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..bded38b --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,70 @@ +name: easy_upi_payment +description: A new flutter plugin to make upi payment (only on Android). +version: 0.0.1 +# homepage: + +environment: + sdk: ">=2.17.5 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: com.pcu.easy_upi_payment + pluginClass: EasyUpiPaymentPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/test/easy_upi_payment_method_channel_test.dart b/test/easy_upi_payment_method_channel_test.dart new file mode 100644 index 0000000..c4053a8 --- /dev/null +++ b/test/easy_upi_payment_method_channel_test.dart @@ -0,0 +1,20 @@ +void main() { + // MethodChannelEasyUpiPayment platform = MethodChannelEasyUpiPayment(); + // const MethodChannel channel = MethodChannel('easy_upi_payment'); + // + // TestWidgetsFlutterBinding.ensureInitialized(); + // + // setUp(() { + // channel.setMockMethodCallHandler((MethodCall methodCall) async { + // return '42'; + // }); + // }); + // + // tearDown(() { + // channel.setMockMethodCallHandler(null); + // }); + // + // test('getPlatformVersion', () async { + // expect(await platform.getPlatformVersion(), '42'); + // }); +} diff --git a/test/easy_upi_payment_test.dart b/test/easy_upi_payment_test.dart new file mode 100644 index 0000000..6da1337 --- /dev/null +++ b/test/easy_upi_payment_test.dart @@ -0,0 +1,29 @@ +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:easy_upi_payment/easy_upi_payment.dart'; +// import 'package:easy_upi_payment/easy_upi_payment_platform_interface.dart'; +// import 'package:easy_upi_payment/easy_upi_payment_method_channel.dart'; +// import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +// +// class MockEasyUpiPaymentPlatform +// with MockPlatformInterfaceMixin +// implements EasyUpiPaymentPlatform { +// +// @override +// Future getPlatformVersion() => Future.value('42'); +// } +// +// void main() { +// final EasyUpiPaymentPlatform initialPlatform = EasyUpiPaymentPlatform.instance; +// +// test('$MethodChannelEasyUpiPayment is the default instance', () { +// expect(initialPlatform, isInstanceOf()); +// }); +// +// test('getPlatformVersion', () async { +// EasyUpiPayment easyUpiPaymentPlugin = EasyUpiPayment(); +// MockEasyUpiPaymentPlatform fakePlatform = MockEasyUpiPaymentPlatform(); +// EasyUpiPaymentPlatform.instance = fakePlatform; +// +// expect(await easyUpiPaymentPlugin.getPlatformVersion(), '42'); +// }); +// }