├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── kotlin
│ └── ir
│ └── cafebazaar
│ └── flutter_poolakey
│ ├── FlutterPoolakeyPlugin.kt
│ ├── Mappers.kt
│ └── PaymentActivity.kt
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── sample
│ │ │ │ │ └── android
│ │ │ │ │ └── trivialdrivesample
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── assets
│ ├── buy_gas.png
│ ├── free.png
│ ├── gas0.png
│ ├── gas1.png
│ ├── gas2.png
│ ├── gas3.png
│ ├── gas4.png
│ ├── gas_inf.png
│ ├── get_infinite_gas.png
│ ├── get_infinite_gas_trial.png
│ ├── premium.png
│ ├── title.png
│ └── upgrade_app.png
├── lib
│ ├── data.dart
│ └── main.dart
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── lib
├── flutter_poolakey.dart
├── purchase_info.dart
└── sku_details.dart
├── pubspec.yaml
├── repo_files
└── flutter_poolakey.jpg
└── test
└── foolakey_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.lock
4 | *.log
5 | *.pyc
6 | *.swp
7 | .DS_Store
8 | .atom/
9 | .buildlog/
10 | .history
11 | .svn/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # Visual Studio Code related
20 | .classpath
21 | .project
22 | .settings/
23 | .vscode/
24 |
25 | # Flutter repo-specific
26 | /bin/cache/
27 | /bin/internal/bootstrap.bat
28 | /bin/internal/bootstrap.sh
29 | /bin/mingit/
30 | /dev/benchmarks/mega_gallery/
31 | /dev/bots/.recipe_deps
32 | /dev/bots/android_tools/
33 | /dev/devicelab/ABresults*.json
34 | /dev/docs/doc/
35 | /dev/docs/flutter.docs.zip
36 | /dev/docs/lib/
37 | /dev/docs/pubspec.yaml
38 | /dev/integration_tests/**/xcuserdata
39 | /dev/integration_tests/**/Pods
40 | /packages/flutter/coverage/
41 | version
42 | analysis_benchmark.json
43 |
44 | # packages file containing multi-root paths
45 | .packages.generated
46 |
47 | # Flutter/Dart/Pub related
48 | **/doc/api/
49 | .dart_tool/
50 | .flutter-plugins
51 | .flutter-plugins-dependencies
52 | **/generated_plugin_registrant.dart
53 | .packages
54 | .pub-cache/
55 | .pub/
56 | build/
57 | flutter_*.png
58 | linked_*.ds
59 | unlinked.ds
60 | unlinked_spec.ds
61 |
62 | # Android related
63 | **/android/**/gradle-wrapper.jar
64 | **/android/.gradle
65 | **/android/captures/
66 | **/android/gradlew
67 | **/android/gradlew.bat
68 | **/android/local.properties
69 | **/android/**/GeneratedPluginRegistrant.java
70 | **/android/key.properties
71 | *.jks
72 |
73 | # iOS/XCode related
74 | **/ios/**/*.mode1v3
75 | **/ios/**/*.mode2v3
76 | **/ios/**/*.moved-aside
77 | **/ios/**/*.pbxuser
78 | **/ios/**/*.perspectivev3
79 | **/ios/**/*sync/
80 | **/ios/**/.sconsign.dblite
81 | **/ios/**/.tags*
82 | **/ios/**/.vagrant/
83 | **/ios/**/DerivedData/
84 | **/ios/**/Icon?
85 | **/ios/**/Pods/
86 | **/ios/**/.symlinks/
87 | **/ios/**/profile
88 | **/ios/**/xcuserdata
89 | **/ios/.generated/
90 | **/ios/Flutter/.last_build_id
91 | **/ios/Flutter/App.framework
92 | **/ios/Flutter/Flutter.framework
93 | **/ios/Flutter/Flutter.podspec
94 | **/ios/Flutter/Generated.xcconfig
95 | **/ios/Flutter/ephemeral
96 | **/ios/Flutter/app.flx
97 | **/ios/Flutter/app.zip
98 | **/ios/Flutter/flutter_assets/
99 | **/ios/Flutter/flutter_export_environment.sh
100 | **/ios/ServiceDefinitions.json
101 | **/ios/Runner/GeneratedPluginRegistrant.*
102 |
103 | # macOS
104 | **/macos/Flutter/GeneratedPluginRegistrant.swift
105 |
106 | # Coverage
107 | coverage/
108 |
109 | # Symbols
110 | app.*.symbols
111 |
112 | # Exceptions to above rules.
113 | !**/ios/**/default.mode1v3
114 | !**/ios/**/default.mode2v3
115 | !**/ios/**/default.pbxuser
116 | !**/ios/**/default.perspectivev3
117 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
118 | !/dev/ci/**/Gemfile.lock
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: adc687823a831bbebe28bdccfac1a628ca621513
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 2.2.0-1.0.0-alpha01
2 | * Update gradle version to 7.5 #29
3 | * Fix illegal state exception on connect #23
4 | * Apply new versioning schema
5 |
6 | ## 2.2.0
7 | * Upgrade Poolakey dependency to 2.2.0
8 | * Update README.md
9 |
10 | ## 1.3.0
11 | * Add (trivial) example project.
12 | * Upgrade Poolakey dependency to 1.2.3
13 | * Fix exception on destroy flutter activity
14 | * Support android 12
15 |
16 | ## 1.2.3
17 | * Upgrade Poolakey dependency to 1.2.3
18 | * Fix exception on destroy main-activity
19 |
20 | ## 1.0.0-rc1
21 | * Write documentations in github WIKI and linked in README.md
22 | * reformat code
23 | * ready to go
24 |
25 | ## 0.1.0
26 | * Upgrade poolakey dependency to 1.1.0-beta02
27 | * Add `getInAppSkuDetails` function to retrieve sku details of your purchase products.
28 | * Add `getSubscriptionSkuDetails` function to retrieve sku details of your subscription products.
29 |
30 | ## 0.0.2
31 | * Add query tag flag in AndroidManifest
32 |
33 | ## 0.0.1
34 | Initial Version
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2019 Mohammad Mahdi Nouri
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](https://pub.dartlang.org/packages/flutter_poolakey)
4 |
5 | ### Android In-App Billing *Flutter* SDK for [Cafe Bazaar](https://cafebazaar.ir/?l=en) App Store.
6 |
7 | ## Getting Started
8 |
9 | To start working with Flutter Poolakey, you need to add its dependency in your
10 | project's `pubspec.yaml` file:
11 |
12 | ### Dependency
13 |
14 | ```yaml
15 | dependencies:
16 | flutter_poolakey: ^2.2.0-1.0.0-alpha01
17 | ```
18 |
19 | Then run the below command to retrieve it:
20 |
21 | ```shell
22 | flutter packages get
23 | ```
24 |
25 | And then Go to the allprojects section of your project gradle file and add the JitPack repository to the repositories block:
26 | ```groovy
27 | allprojects {
28 | repositories {
29 | // add JitPack
30 | maven { url 'https://jitpack.io' }
31 | }
32 | }
33 | ```
34 |
35 | ### Import it
36 |
37 | Now in your Dart code, you can use:
38 |
39 | ```dart
40 | import 'package:flutter_poolakey/flutter_poolakey.dart';
41 | ```
42 |
43 | ### How to use
44 |
45 | For more information regarding the usage of flutter Poolakey, please check out
46 | the [wiki](https://github.com/cafebazaar/flutter_poolakey/wiki) page.
47 |
48 | ### Sample
49 |
50 | There is a fully functional sample application that demonstrates the usage of flutter Poolakey, all you have
51 | to do is cloning the project and running
52 | the [app](https://github.com/cafebazaar/flutter_poolakey/tree/master/example) module.
53 |
54 |
55 | #### flutter_poolakey is a wrapper around [Poolakey](https://github.com/cafebazaar/Poolakey) to use in Flutter.
56 |
57 | > [Poolakey](https://github.com/cafebazaar/Poolakey) is an Android In-App Billing SDK
58 | > for [Cafe Bazaar](https://cafebazaar.ir/?l=en) App Store.
59 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # IntelliJ
40 | *.iml
41 | .idea/workspace.xml
42 | .idea/tasks.xml
43 | .idea/gradle.xml
44 | .idea/assetWizardSettings.xml
45 | .idea/dictionaries
46 | .idea/libraries
47 | # Android Studio 3 in .gitignore file.
48 | .idea/caches
49 | .idea/modules.xml
50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
51 | .idea/navEditor.xml
52 |
53 | # Keystore files
54 | # Uncomment the following lines if you do not want to check your keystore files in.
55 | #*.jks
56 | #*.keystore
57 |
58 | # External native build folder generated in Android Studio 2.2 and later
59 | .externalNativeBuild
60 | .cxx/
61 |
62 | # Google Services (e.g. APIs or Firebase)
63 | # google-services.json
64 |
65 | # Freeline
66 | freeline.py
67 | freeline/
68 | freeline_project_description.json
69 |
70 | # fastlane
71 | fastlane/report.xml
72 | fastlane/Preview.html
73 | fastlane/screenshots
74 | fastlane/test_output
75 | fastlane/readme.md
76 |
77 | # Version control
78 | vcs.xml
79 |
80 | # lint
81 | lint/intermediates/
82 | lint/generated/
83 | lint/outputs/
84 | lint/tmp/
85 | # lint/reports/
86 |
87 | # Android Profiling
88 | *.hprof
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = "1.7.10"
3 | ext.poolakey_version = "2.2.0"
4 | repositories {
5 | jcenter()
6 | maven { url "https://jitpack.io" }
7 | google()
8 | maven { url "https://plugins.gradle.org/m2/" }
9 | maven {
10 | url "https://maven.google.com/"
11 | name "Google"
12 | }
13 | }
14 |
15 | dependencies {
16 | classpath 'com.android.tools.build:gradle:7.4.2'
17 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
18 | }
19 | }
20 |
21 | plugins {
22 | id "com.android.library"
23 | id "kotlin-android"
24 | }
25 |
26 | group 'ir.cafebazaar.flutter_poolakey'
27 | version '2.2.0-1.0.0-alpha01'
28 |
29 | allprojects {
30 | repositories {
31 | jcenter()
32 | maven { url "https://jitpack.io" }
33 | google()
34 | maven { url "https://plugins.gradle.org/m2/" }
35 | maven {
36 | url "https://maven.google.com/"
37 | name "Google"
38 | }
39 | }
40 | }
41 |
42 | android {
43 | namespace 'ir.cafebazaar.flutter_poolakey'
44 | compileSdkVersion 34
45 |
46 | compileOptions {
47 | sourceCompatibility JavaVersion.VERSION_1_8
48 | targetCompatibility JavaVersion.VERSION_1_8
49 | }
50 |
51 | buildFeatures {
52 | buildConfig true
53 | }
54 |
55 | kotlinOptions {
56 | jvmTarget = '1.8'
57 | }
58 |
59 | sourceSets {
60 | main.java.srcDirs += 'src/main/kotlin'
61 | }
62 |
63 | defaultConfig {
64 | minSdkVersion 16
65 | versionCode 2002001001
66 | versionName "2.2.0-1.0.0-alpha01"
67 | }
68 |
69 | buildTypes {
70 | debug {
71 | buildConfigField "String", "POOLAKEY_VERSION", '"' + poolakey_version + '"'
72 | }
73 | release {
74 | buildConfigField "String", "POOLAKEY_VERSION", '"' + poolakey_version + '"'
75 | }
76 | }
77 |
78 | dependencies {
79 | implementation "com.github.cafebazaar.Poolakey:poolakey:$poolakey_version"
80 | }
81 | }
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'flutter_poolakey'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/ir/cafebazaar/flutter_poolakey/FlutterPoolakeyPlugin.kt:
--------------------------------------------------------------------------------
1 | package ir.cafebazaar.flutter_poolakey
2 |
3 | import android.app.Activity
4 | import androidx.annotation.NonNull
5 |
6 | import io.flutter.embedding.engine.plugins.FlutterPlugin
7 | import io.flutter.embedding.engine.plugins.activity.ActivityAware
8 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
9 | import io.flutter.plugin.common.MethodCall
10 | import io.flutter.plugin.common.MethodChannel
11 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler
12 | import io.flutter.plugin.common.MethodChannel.Result
13 | import ir.cafebazaar.poolakey.Connection
14 | import ir.cafebazaar.poolakey.ConnectionState
15 | import ir.cafebazaar.poolakey.Payment
16 | import ir.cafebazaar.poolakey.callback.PurchaseCallback
17 | import ir.cafebazaar.poolakey.config.PaymentConfiguration
18 | import ir.cafebazaar.poolakey.config.SecurityCheck
19 |
20 | class FlutterPoolakeyPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
21 | private lateinit var channel: MethodChannel
22 | private var activityBinding: ActivityPluginBinding? = null
23 | private lateinit var flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
24 |
25 | private val requireActivity: Activity
26 | get() = activityBinding!!.activity
27 |
28 | private var purchaseCallback: (PurchaseCallback.() -> Unit)? = null
29 |
30 | private lateinit var paymentConnection: Connection
31 | private lateinit var payment: Payment
32 |
33 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
34 | this.flutterPluginBinding = flutterPluginBinding
35 | }
36 |
37 | override fun onAttachedToActivity(binding: ActivityPluginBinding) {
38 | activityBinding = binding
39 | channel =
40 | MethodChannel(flutterPluginBinding.binaryMessenger, "ir.cafebazaar.flutter_poolakey")
41 | channel.setMethodCallHandler(this)
42 | }
43 |
44 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
45 | when (call.method) {
46 | "version" -> {
47 | getVersion(result);
48 | }
49 |
50 | "connect" -> {
51 | val inAppBillingKey = call.argument("in_app_billing_key")
52 | connect(inAppBillingKey, result)
53 | }
54 |
55 | "disconnect" -> {
56 | disconnect(result)
57 | }
58 |
59 | "purchase" -> {
60 | startActivity(
61 | activity = requireActivity,
62 | command = PaymentActivity.Command.Purchase,
63 | productId = call.argument("product_id")!!,
64 | result = result,
65 | payload = call.argument("payload"),
66 | dynamicPriceToken = call.argument("dynamicPriceToken"),
67 | )
68 | }
69 |
70 | "subscribe" -> {
71 | startActivity(
72 | activity = requireActivity,
73 | command = PaymentActivity.Command.Subscribe,
74 | productId = call.argument("product_id")!!,
75 | result = result,
76 | payload = call.argument("payload"),
77 | dynamicPriceToken = call.argument("dynamicPriceToken"),
78 | )
79 | }
80 |
81 | "consume" -> {
82 | val purchaseToken = call.argument("purchase_token")!!
83 | consume(purchaseToken, result)
84 | }
85 |
86 | "get_all_purchased_products" -> {
87 | getAllPurchasedProducts(result)
88 | }
89 |
90 | "get_all_subscribed_products" -> {
91 | getAllSubscribedProducts(result)
92 | }
93 |
94 | "get_in_app_sku_details" -> {
95 | val skuIds = call.argument>("sku_ids")!!
96 | getInAppSkuDetails(skuIds, result)
97 | }
98 |
99 | "get_subscription_sku_details" -> {
100 | val skuIds = call.argument>("sku_ids")!!
101 | getSubscriptionSkuDetails(skuIds, result)
102 | }
103 |
104 | "checkTrialSubscription" -> {
105 | checkTrialSubscription(result)
106 | }
107 |
108 | else -> result.notImplemented()
109 | }
110 | }
111 |
112 | private fun getVersion(result: Result) {
113 | result.success(BuildConfig.POOLAKEY_VERSION)
114 | }
115 |
116 | private fun connect(inAppBillingKey: String?, result: Result) {
117 | val securityCheck = if (inAppBillingKey != null) {
118 | SecurityCheck.Enable(rsaPublicKey = inAppBillingKey)
119 | } else {
120 | SecurityCheck.Disable
121 | }
122 | val paymentConfiguration = PaymentConfiguration(localSecurityCheck = securityCheck)
123 |
124 | payment = Payment(context = requireActivity, config = paymentConfiguration)
125 |
126 | paymentConnection = payment.connect {
127 | connectionSucceed {
128 | channel.invokeMethod("connectionSucceed", null)
129 | }
130 | connectionFailed {
131 | channel.invokeMethod("connectionFailed", it.toString(), null)
132 | }
133 | disconnected {
134 | channel.invokeMethod("disconnected", null)
135 | }
136 | }
137 | }
138 |
139 | private fun disconnect(result: Result) {
140 | paymentConnection.disconnect()
141 | result.success(null)
142 | }
143 |
144 | fun startActivity(
145 | activity: Activity,
146 | command: PaymentActivity.Command,
147 | productId: String,
148 | result: Result,
149 | payload: String
150 | ?,
151 | dynamicPriceToken: String
152 | ?
153 | ) {
154 | if (paymentConnection.getState() != ConnectionState.Connected) {
155 | result.error("PURCHASE_FAILED", "In order to purchasing, connect to Poolakey!", null)
156 | return
157 | }
158 |
159 | PaymentActivity.start(
160 | activity,
161 | command,
162 | productId,
163 | payment,
164 | result,
165 | payload,
166 | dynamicPriceToken
167 | )
168 | }
169 |
170 | private fun consume(purchaseToken: String, result: Result) {
171 | if (paymentConnection.getState() != ConnectionState.Connected) {
172 | result.error(
173 | "PAYMENT_CONNECTION_IS_NOT_CONNECTED",
174 | "PaymentConnection is not connected (state: ${paymentConnection.getState()})",
175 | null
176 | )
177 | return
178 | }
179 | payment.consumeProduct(purchaseToken) {
180 | consumeSucceed {
181 | result.success(true)
182 | }
183 | consumeFailed {
184 | result.error("CONSUME_FAILED", it.toString(), null)
185 | }
186 | }
187 | }
188 |
189 | private fun checkTrialSubscription(result: Result) {
190 | if (paymentConnection.getState() != ConnectionState.Connected) {
191 | result.error(
192 | "PAYMENT_CONNECTION_IS_NOT_CONNECTED",
193 | "PaymentConnection is not connected (state: ${paymentConnection.getState()})",
194 | null
195 | )
196 | return
197 | }
198 | payment.checkTrialSubscription {
199 | checkTrialSubscriptionSucceed { trialSubscriptionInfo ->
200 | result.success(
201 | hashMapOf(
202 | "isAvailable" to trialSubscriptionInfo.isAvailable,
203 | "trialPeriodDays" to trialSubscriptionInfo.trialPeriodDays
204 | )
205 | )
206 | }
207 |
208 | checkTrialSubscriptionFailed {
209 | result.error("CHECK_TRIAL_FAILED", it.toString(), null)
210 |
211 | }
212 | }
213 | }
214 |
215 | private fun getAllPurchasedProducts(result: Result) {
216 | if (paymentConnection.getState() != ConnectionState.Connected) {
217 | result.error(
218 | "PAYMENT_CONNECTION_IS_NOT_CONNECTED",
219 | "PaymentConnection is not connected (state: ${paymentConnection.getState()})",
220 | null
221 | )
222 | return
223 | }
224 | payment.getPurchasedProducts {
225 | querySucceed { purchasedItems ->
226 | result.success(purchasedItems.map { it.toMap() })
227 | }
228 | queryFailed {
229 | result.error("QUERY_PURCHASED_PRODUCT_FAILED", it.toString(), null)
230 | }
231 | }
232 | }
233 |
234 | private fun getAllSubscribedProducts(result: Result) {
235 | if (paymentConnection.getState() != ConnectionState.Connected) {
236 | result.error(
237 | "PAYMENT_CONNECTION_IS_NOT_CONNECTED",
238 | "PaymentConnection is not connected (state: ${paymentConnection.getState()})",
239 | null
240 | )
241 | return
242 | }
243 | payment.getSubscribedProducts {
244 | querySucceed { purchasedItems ->
245 | result.success(purchasedItems.map { it.toMap() })
246 | }
247 | queryFailed {
248 | result.error("QUERY_SUBSCRIBED_PRODUCT_FAILED", it.toString(), null)
249 | }
250 | }
251 | }
252 |
253 | private fun getInAppSkuDetails(skuIds: List, result: Result) {
254 | if (paymentConnection.getState() != ConnectionState.Connected) {
255 | result.error(
256 | "PAYMENT_CONNECTION_IS_NOT_CONNECTED",
257 | "PaymentConnection is not connected (state: ${paymentConnection.getState()})",
258 | null
259 | )
260 | return
261 | }
262 |
263 | payment.getInAppSkuDetails(skuIds = skuIds) {
264 | getSkuDetailsSucceed {
265 | result.success(it.map { skuDetails -> skuDetails.toMap() })
266 | }
267 | getSkuDetailsFailed {
268 | result.error("QUERY_GET_IN_APP_SKU_DETAILS_FAILED", it.toString(), null)
269 | }
270 | }
271 | }
272 |
273 | private fun getSubscriptionSkuDetails(skuIds: List, result: Result) {
274 | if (paymentConnection.getState() != ConnectionState.Connected) {
275 | result.error(
276 | "PAYMENT_CONNECTION_IS_NOT_CONNECTED",
277 | "PaymentConnection is not connected (state: ${paymentConnection.getState()})",
278 | null
279 | )
280 | return
281 | }
282 |
283 | payment.getSubscriptionSkuDetails(skuIds = skuIds) {
284 | getSkuDetailsSucceed {
285 | result.success(it.map { skuDetails -> skuDetails.toMap() })
286 | }
287 | getSkuDetailsFailed {
288 | result.error("QUERY_GET_SUBSCRIPTION_SKU_DETAILS_FAILED", it.toString(), null)
289 | }
290 | }
291 | }
292 |
293 | override fun onDetachedFromActivityForConfigChanges() {
294 | // No op
295 | }
296 |
297 | override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
298 | // No op
299 | }
300 |
301 | override fun onDetachedFromActivity() {
302 | activityBinding = null
303 | purchaseCallback = null
304 | channel.setMethodCallHandler(null)
305 | if (::paymentConnection.isInitialized) {
306 | paymentConnection.disconnect()
307 | }
308 | }
309 |
310 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/ir/cafebazaar/flutter_poolakey/Mappers.kt:
--------------------------------------------------------------------------------
1 | package ir.cafebazaar.flutter_poolakey
2 |
3 | import ir.cafebazaar.poolakey.entity.PurchaseInfo
4 | import ir.cafebazaar.poolakey.entity.SkuDetails
5 |
6 | internal fun PurchaseInfo.toMap() = hashMapOf(
7 | "orderId" to orderId,
8 | "purchaseToken" to purchaseToken,
9 | "payload" to payload,
10 | "packageName" to packageName,
11 | "purchaseState" to purchaseState.toString(),
12 | "purchaseTime" to purchaseTime,
13 | "productId" to productId,
14 | "originalJson" to originalJson,
15 | "dataSignature" to dataSignature
16 | )
17 |
18 | internal fun SkuDetails.toMap() = hashMapOf(
19 | "sku" to sku,
20 | "type" to type,
21 | "price" to price,
22 | "title" to title,
23 | "description" to description
24 | )
25 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/ir/cafebazaar/flutter_poolakey/PaymentActivity.kt:
--------------------------------------------------------------------------------
1 | package ir.cafebazaar.flutter_poolakey
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import androidx.activity.ComponentActivity
7 | import io.flutter.plugin.common.MethodChannel
8 | import ir.cafebazaar.poolakey.Payment
9 | import ir.cafebazaar.poolakey.callback.PurchaseCallback
10 | import ir.cafebazaar.poolakey.request.PurchaseRequest
11 | import java.security.InvalidParameterException
12 |
13 | class PaymentActivity : ComponentActivity() {
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 |
17 | val callback: PurchaseCallback.() -> Unit = {
18 | purchaseSucceed {
19 | result.success(it.toMap())
20 | finish()
21 | }
22 | purchaseCanceled {
23 | result.error("PURCHASE_CANCELLED", "Purchase flow has been canceled", null)
24 | finish()
25 | }
26 | purchaseFailed {
27 | result.error("PURCHASE_FAILED", "Purchase flow has been failed", null)
28 | finish()
29 | }
30 | purchaseFlowBegan {
31 | // Nothing
32 | }
33 | failedToBeginFlow {
34 | result.error("FAILED_TO_BEGIN_FLOW", it.toString(), null)
35 | finish()
36 | }
37 | }
38 | when (command) {
39 | Command.Purchase -> purchaseProduct(callback)
40 | Command.Subscribe -> subscribeProduct(callback)
41 | else -> throw InvalidParameterException("Undefined command: $command")
42 | }
43 | }
44 |
45 |
46 | private fun purchaseProduct(callback: PurchaseCallback.() -> Unit) {
47 | payment.purchaseProduct(
48 | activityResultRegistry,
49 | PurchaseRequest(productId, payload, dynamicPriceToken),
50 | callback
51 | )
52 | }
53 |
54 | private fun subscribeProduct(callback: PurchaseCallback.() -> Unit) {
55 | payment.subscribeProduct(
56 | activityResultRegistry,
57 | PurchaseRequest(productId, payload, dynamicPriceToken),
58 | callback
59 | )
60 | }
61 |
62 | companion object {
63 |
64 | private lateinit var command: PaymentActivity.Command
65 | private lateinit var productId: String
66 | private lateinit var payment: Payment
67 | private lateinit var result: MethodChannel.Result
68 | private var payload: String? = null
69 | private var dynamicPriceToken: String? = null
70 |
71 | @JvmStatic
72 | fun start(
73 | activity: Activity,
74 | command: Command,
75 | productId: String,
76 | payment: Payment,
77 | result: MethodChannel.Result,
78 | payload: String?,
79 | dynamicPriceToken: String?
80 | ) {
81 | val intent = Intent(activity, PaymentActivity::class.java)
82 | PaymentActivity.command = command
83 | PaymentActivity.productId = productId
84 | PaymentActivity.payment = payment
85 | PaymentActivity.result = result
86 | PaymentActivity.payload = payload
87 | PaymentActivity.dynamicPriceToken = dynamicPriceToken
88 | activity.startActivity(intent)
89 | }
90 | }
91 |
92 | enum class Command {
93 | Purchase,
94 | Subscribe
95 | }
96 | }
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: adc687823a831bbebe28bdccfac1a628ca621513
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_poolakey_example
2 |
3 | Demonstrates how to use the flutter_poolakey plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | id "dev.flutter.flutter-gradle-plugin"
5 | }
6 |
7 | android {
8 | namespace = "com.sample.android.trivialdrivesample"
9 | compileSdkVersion = 34
10 | sourceSets {
11 | main.java.srcDirs += 'src/main/kotlin'
12 | }
13 | compileOptions {
14 | sourceCompatibility = JavaVersion.VERSION_1_8
15 | targetCompatibility = JavaVersion.VERSION_1_8
16 | }
17 |
18 | kotlinOptions {
19 | jvmTarget = JavaVersion.VERSION_1_8
20 | }
21 |
22 | defaultConfig {
23 | applicationId "ir.cafebazaar.poolakeysample"
24 | // You can update the following values to match your application needs.
25 | // For more information, see: https://flutter.dev/to/review-gradle-config.
26 | minSdkVersion = flutter.minSdkVersion
27 | targetSdkVersion = flutter.targetSdkVersion
28 | versionCode = 1
29 | versionName = "1.0"
30 | }
31 |
32 | buildTypes {
33 | release {
34 | // TODO: Add your own signing config for the release build.
35 | // Signing with the debug keys for now, so `flutter run --release` works.
36 | signingConfig = signingConfigs.debug
37 | }
38 | }
39 | }
40 |
41 | flutter {
42 | source = "../.."
43 | }
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
14 |
18 |
21 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
36 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/com/sample/android/trivialdrivesample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.sample.android.trivialdrivesample
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity()
6 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | jcenter()
4 | maven { url "https://jitpack.io" }
5 | google()
6 | mavenCentral()
7 | }
8 | }
9 |
10 | rootProject.buildDir = "../build"
11 | subprojects {
12 | project.buildDir = "${rootProject.buildDir}/${project.name}"
13 | }
14 | subprojects {
15 | project.evaluationDependsOn(":app")
16 | }
17 |
18 | tasks.register("clean", Delete) {
19 | delete rootProject.buildDir
20 | }
21 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
6 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }()
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | maven { url "https://jitpack.io" }
17 | }
18 | }
19 |
20 | plugins {
21 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
22 | id "com.android.application" version "7.4.0" apply false
23 | id "org.jetbrains.kotlin.android" version "1.7.10" apply false
24 | }
25 |
26 | rootProject.name = 'flutter_poolakey'
27 | include ":app"
--------------------------------------------------------------------------------
/example/assets/buy_gas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/buy_gas.png
--------------------------------------------------------------------------------
/example/assets/free.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/free.png
--------------------------------------------------------------------------------
/example/assets/gas0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/gas0.png
--------------------------------------------------------------------------------
/example/assets/gas1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/gas1.png
--------------------------------------------------------------------------------
/example/assets/gas2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/gas2.png
--------------------------------------------------------------------------------
/example/assets/gas3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/gas3.png
--------------------------------------------------------------------------------
/example/assets/gas4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/gas4.png
--------------------------------------------------------------------------------
/example/assets/gas_inf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/gas_inf.png
--------------------------------------------------------------------------------
/example/assets/get_infinite_gas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/get_infinite_gas.png
--------------------------------------------------------------------------------
/example/assets/get_infinite_gas_trial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/get_infinite_gas_trial.png
--------------------------------------------------------------------------------
/example/assets/premium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/premium.png
--------------------------------------------------------------------------------
/example/assets/title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/title.png
--------------------------------------------------------------------------------
/example/assets/upgrade_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cafebazaar/flutter_poolakey/8f462b8a965fbcce0a9f5acfc0bd16ea52f7db9c/example/assets/upgrade_app.png
--------------------------------------------------------------------------------
/example/lib/data.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_poolakey/flutter_poolakey.dart';
2 |
3 | class ProductItem {
4 | final String id;
5 | final String icon;
6 | final bool consumable;
7 | SkuDetails? skuDetails;
8 | PurchaseInfo? purchaseInfo;
9 |
10 | ProductItem(this.id, this.icon, this.consumable);
11 | }
12 |
13 | class TrialSubscription extends SkuDetails {
14 | bool isAvailable = false;
15 | int trialPeriodDays = 0;
16 | TrialSubscription(
17 | String sku, String type, String price, String title, String description)
18 | : super(sku, type, price, title, description);
19 |
20 | static TrialSubscription fromSkuDetails(SkuDetails skuDetails) {
21 | return TrialSubscription(skuDetails.sku, skuDetails.type, skuDetails.price,
22 | skuDetails.title, skuDetails.description);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_poolakey/flutter_poolakey.dart';
3 |
4 | void main() {
5 | runApp(const MyApp());
6 | }
7 |
8 | class MyApp extends StatelessWidget {
9 | const MyApp({Key? key}) : super(key: key);
10 |
11 | // This widget is the root of your application.
12 | @override
13 | Widget build(BuildContext context) {
14 | return MaterialApp(
15 | title: 'Trivial Example for Flutter-Poolakey',
16 | theme: ThemeData(
17 | // This is the theme of your application.
18 | //
19 | // Try running your application with "flutter run". You'll see the
20 | // application has a blue toolbar. Then, without quitting the app, try
21 | // changing the primarySwatch below to Colors.green and then invoke
22 | // "hot reload" (press "r" in the console where you ran "flutter run",
23 | // or simply save your changes to "hot reload" in a Flutter IDE).
24 | // Notice that the counter didn't reset back to zero; the application
25 | // is not restarted.
26 | primarySwatch: Colors.green,
27 | brightness: Brightness.dark,
28 | ),
29 | home: const MyHomePage(title: 'Trivial Example for Flutter-Poolakey'),
30 | );
31 | }
32 | }
33 |
34 | class MyHomePage extends StatefulWidget {
35 | const MyHomePage({Key? key, required this.title}) : super(key: key);
36 |
37 | // This widget is the home page of your application. It is stateful, meaning
38 | // that it has a State object (defined below) that contains fields that affect
39 | // how it looks.
40 |
41 | // This class is the configuration for the state. It holds the values (in this
42 | // case the title) provided by the parent (in this case the App widget) and
43 | // used by the build method of the State. Fields in a Widget subclass are
44 | // always marked "final".
45 |
46 | final String title;
47 |
48 | @override
49 | State createState() => _MyHomePageState();
50 | }
51 |
52 | class _MyHomePageState extends State {
53 | final dynamicPriceTokenController = TextEditingController();
54 | final productIdController = TextEditingController();
55 | bool connected = false;
56 | String status = "";
57 | bool consume = true;
58 |
59 | @override
60 | void initState() {
61 | _initShop();
62 | super.initState();
63 | }
64 |
65 | Future _initShop() async {
66 | var rsaKey =
67 | "MIHNMA0GCSqGSIb3DQEBAQUAA4G7ADCBtwKBrwDbkRScfggn+JSs+DzcZK20ZbxKPKv060aekC4dxqapamlgf9PncC5/4sqhUU4SdeKE770H1s7dJhmV5QEnzLawJTgiTzD3RFcadl2H4dduro/KxVyAe5nNKE/Xg+uRalLU/Hw9Or44m2xDyWESWj8sqweaGDUnsoHWJFsyVwwIj15fx3cDX6kjObC0gYns1o79x+COWCqyIlDwE2Pf7Xum55FASKFH8lqlYpEzR38CAwEAAQ== ";
68 | try {
69 | await FlutterPoolakey.connect(
70 | rsaKey,
71 | onSucceed: () {
72 | connected = true;
73 | _updateState("Service: Connected");
74 | },
75 | onFailed: () {
76 | connected = false;
77 | _updateState("Service: Not Connected");
78 | },
79 | onDisconnected: () {
80 | connected = false;
81 | _updateState("Service: Not Connected");
82 | },
83 | );
84 | } on Exception catch (e) {
85 | showSnackBar(e.toString());
86 | _updateState("Service: Failed to Connect");
87 | }
88 | }
89 |
90 | void _updateState(String message) {
91 | setState(() {
92 | status = message;
93 | });
94 | showSnackBar(message);
95 | }
96 |
97 | @override
98 | Widget build(BuildContext context) {
99 | return Scaffold(
100 | body: Center(
101 | child: Padding(
102 | padding: const EdgeInsets.symmetric(horizontal: 16.0),
103 | child: Column(
104 | mainAxisSize: MainAxisSize.min,
105 | children: [
106 | Text(status),
107 | const SizedBox(height: 8),
108 | TextField(
109 | controller: productIdController,
110 | decoration: InputDecoration(
111 | border: OutlineInputBorder(),
112 | hintText: 'Product id',
113 | ),
114 | ),
115 | const SizedBox(height: 8),
116 | TextField(
117 | controller: dynamicPriceTokenController,
118 | decoration: InputDecoration(
119 | border: OutlineInputBorder(),
120 | hintText: 'Dynamic price token',
121 | ),
122 | ),
123 | Row(
124 | children: [
125 | Text('Consume Purchase'),
126 | Spacer(),
127 | Switch(
128 | value: consume,
129 | onChanged: (checked) {
130 | setState(() {
131 | consume = checked;
132 | });
133 | }),
134 | ],
135 | ),
136 | FilledButton(
137 | onPressed: () {
138 | purchaseProduct(
139 | productIdController.text,
140 | "purchasePayload",
141 | dynamicPriceTokenController.text,
142 | );
143 | },
144 | child: Text('Purchase')),
145 | FilledButton(
146 | onPressed: () {
147 | subscribeProduct(
148 | productIdController.text,
149 | "subscribePayload",
150 | dynamicPriceTokenController.text,
151 | );
152 | },
153 | child: Text('Subscribe')),
154 | FilledButton(
155 | onPressed: checkUserPurchasedItem,
156 | child: Text('Check if user purchased this item')),
157 | FilledButton(
158 | onPressed: checkUserSubscribedItem,
159 | child: Text('Check if user subscribed this item')),
160 | FilledButton(
161 | onPressed: () {
162 | getSkuDetailOfInAppItem(productIdController.text);
163 | },
164 | child: Text('Get Sku detail of in-app item')),
165 | FilledButton(
166 | onPressed: () {
167 | getSkuDetailOfSubscriptionItem(productIdController.text);
168 | },
169 | child: Text('Get Sku detail of subscription item')),
170 | FilledButton(
171 | onPressed: checkTrialSubscription,
172 | child: Text('Check Trial subscription'))
173 | ],
174 | ),
175 | ),
176 | ),
177 | );
178 | }
179 |
180 | Future subscribeProduct(
181 | String productId,
182 | String payload,
183 | String? dynamicPriceToken,
184 | ) async {
185 | if (!connected) {
186 | showSnackBar('Service: Not Connected');
187 | return;
188 | }
189 |
190 | try {
191 | PurchaseInfo? response = await FlutterPoolakey.subscribe(productId,
192 | payload: payload, dynamicPriceToken: dynamicPriceToken ?? "");
193 | } catch (e) {
194 | showSnackBar("subscribeProduct ${e.toString()}");
195 | return;
196 | }
197 | }
198 |
199 | Future purchaseProduct(
200 | String productId,
201 | String payload,
202 | String? dynamicPriceToken,
203 | ) async {
204 | if (!connected) {
205 | showSnackBar('Service: Not Connected');
206 | return;
207 | }
208 | try {
209 | PurchaseInfo? response = await FlutterPoolakey.purchase(productId,
210 | payload: payload, dynamicPriceToken: dynamicPriceToken ?? "");
211 | if (consume) {
212 | consumePurchasedItem(response.purchaseToken);
213 | }
214 | } catch (e) {
215 | showSnackBar("purchaseProduct ${e.toString()}");
216 | return;
217 | }
218 | }
219 |
220 | Future checkUserSubscribedItem() async {
221 | if (!connected) {
222 | showSnackBar('Service: Not Connected');
223 | return;
224 | }
225 |
226 | try {
227 | List? response =
228 | await FlutterPoolakey.getAllSubscribedProducts();
229 | bool result = response
230 | .any((element) => element.productId == productIdController.text);
231 | if (result) {
232 | showSnackBar("User has bought this item");
233 | } else {
234 | showSnackBar("User has not bought this item");
235 | }
236 | } catch (e) {
237 | showSnackBar("checkUserSubscribedItem ${e.toString()}");
238 | return;
239 | }
240 | }
241 |
242 | Future checkUserPurchasedItem() async {
243 | if (!connected) {
244 | showSnackBar('Service: Not Connected');
245 | return;
246 | }
247 |
248 | try {
249 | List? response =
250 | await FlutterPoolakey.getAllPurchasedProducts();
251 | bool result = response
252 | .any((element) => element.productId == productIdController.text);
253 | if (result) {
254 | showSnackBar("User has bought this item");
255 | } else {
256 | showSnackBar("User has not bought this item");
257 | }
258 | } catch (e) {
259 | showSnackBar("checkUserPurchasedItem ${e.toString()}");
260 | return;
261 | }
262 | }
263 |
264 | Future getSkuDetailOfSubscriptionItem(String skuValueInput) async {
265 | if (!connected) {
266 | showSnackBar('Service: Not Connected');
267 | return;
268 | }
269 |
270 | try {
271 | List? response =
272 | await FlutterPoolakey.getSubscriptionSkuDetails([skuValueInput]);
273 | showSnackBar("Detail Of Subscription Item ${response.toString()}");
274 | } catch (e) {
275 | showSnackBar("getSkuDetailOfSubscriptionItem ${e.toString()}");
276 | return;
277 | }
278 | }
279 |
280 | Future getSkuDetailOfInAppItem(String skuValueInput) async {
281 | if (!connected) {
282 | showSnackBar('Service: Not Connected');
283 | return;
284 | }
285 |
286 | try {
287 | List? response =
288 | await FlutterPoolakey.getInAppSkuDetails([skuValueInput]);
289 | showSnackBar("Detail Of InApp Item ${response.toString()}");
290 | } catch (e) {
291 | showSnackBar("getSkuDetailOfInAppItem ${e.toString()}");
292 | return;
293 | }
294 | }
295 |
296 | Future checkTrialSubscription() async {
297 | if (!connected) {
298 | showSnackBar('Service: Not Connected');
299 | return;
300 | }
301 |
302 | try {
303 | Map response = await FlutterPoolakey.checkTrialSubscription();
304 | showSnackBar("isAvailable ${response["isAvailable"].toString()}");
305 | } catch (e) {
306 | showSnackBar("checkTrialSubscription ${e.toString()}");
307 | return;
308 | }
309 | }
310 |
311 | Future consumePurchasedItem(String purchaseToken) async {
312 | if (!connected) {
313 | showSnackBar('Service: Not Connected');
314 | return;
315 | }
316 |
317 | try {
318 | bool? response = await FlutterPoolakey.consume(purchaseToken);
319 | showSnackBar("consumePurchasedItem success $response");
320 | } catch (e) {
321 | showSnackBar(e.toString());
322 | return;
323 | }
324 | }
325 |
326 | void showSnackBar(String message) {
327 | ScaffoldMessenger.of(context)
328 | .showSnackBar(SnackBar(content: Text(message)));
329 | }
330 |
331 | @override
332 | void dispose() {
333 | dynamicPriceTokenController.dispose();
334 | productIdController.dispose();
335 | super.dispose();
336 | }
337 | }
338 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_poolakey_example
2 | description: Demonstrates how to use the flutter_poolakey plugin.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | # publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | environment:
9 | sdk: ">=2.12.0 <3.0.0"
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 |
15 | flutter_poolakey:
16 | # When depending on this package from a real application you should use:
17 | # flutter_poolakey: ^x.y.z
18 | # See https://dart.dev/tools/pub/dependencies#version-constraints
19 | # The example app is bundled with the plugin so we use a path dependency on
20 | # the parent directory to use the current plugin's version.
21 | path: ../
22 |
23 | # The following adds the Cupertino Icons font to your application.
24 | # Use with the CupertinoIcons class for iOS style icons.
25 | cupertino_icons: ^1.0.2
26 |
27 | dev_dependencies:
28 | flutter_test:
29 | sdk: flutter
30 |
31 | # For information on the generic Dart part of this file, see the
32 | # following page: https://dart.dev/tools/pub/pubspec
33 |
34 | # The following section is specific to Flutter.
35 | flutter:
36 |
37 | # The following line ensures that the Material Icons font is
38 | # included with your application, so that you can use the icons in
39 | # the material Icons class.
40 | uses-material-design: true
41 |
42 | # To add assets to your application, add an assets section, like this:
43 | # assets:
44 | # - images/a_dot_burr.jpeg
45 | # - images/a_dot_ham.jpeg
46 |
47 | # An image asset can refer to one or more resolution-specific "variants", see
48 | # https://flutter.dev/assets-and-images/#resolution-aware.
49 |
50 | # For details regarding adding assets from package dependencies, see
51 | # https://flutter.dev/assets-and-images/#from-packages
52 |
53 | # To add custom fonts to your application, add a fonts section here,
54 | # in this "flutter" section. Each entry in this list should have a
55 | # "family" key with the font family name, and a "fonts" key with a
56 | # list giving the asset and other descriptors for the font. For
57 | # example:
58 | # fonts:
59 | # - family: Schyler
60 | # fonts:
61 | # - asset: fonts/Schyler-Regular.ttf
62 | # - asset: fonts/Schyler-Italic.ttf
63 | # style: italic
64 | # - family: Trajan Pro
65 | # fonts:
66 | # - asset: fonts/TrajanPro.ttf
67 | # - asset: fonts/TrajanPro_Bold.ttf
68 | # weight: 700
69 | #
70 | assets:
71 | - assets/
72 | # For details regarding fonts from package dependencies,
73 | # see https://flutter.dev/custom-fonts/#from-packages
74 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter_test/flutter_test.dart';
9 |
10 | void main() {
11 | testWidgets('Verify Platform version', (WidgetTester tester) async {});
12 | }
13 |
--------------------------------------------------------------------------------
/lib/flutter_poolakey.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/services.dart';
4 |
5 | import 'purchase_info.dart';
6 | import 'sku_details.dart';
7 |
8 | export 'purchase_info.dart';
9 | export 'sku_details.dart';
10 |
11 | /// [FlutterPoolakey] is a Flutter In-App Billing SDK for Cafe Bazaar App Store.
12 | ///
13 | /// It only works in the Android platform (Because [Cafebazaar](https://cafebazaar.ir/?l=en) only supports Android)
14 | /// It uses [Poolakey](https://github.com/cafebazaar/Poolakey) SDK under the hood.
15 | class FlutterPoolakey {
16 | static const MethodChannel _channel =
17 | const MethodChannel('ir.cafebazaar.flutter_poolakey');
18 |
19 | static Future getVersion() async {
20 | return await _channel.invokeMethod('version', {});
21 | }
22 |
23 | /// Initializes the connection between your app and the bazaar app
24 | ///
25 | /// You must call this method before any other methods of this library.
26 | /// [inAppBillingKey] is the RSA key which you can find it in your (pishkhan panel)[https://pishkhan.cafebazaar.ir].
27 | /// You can also disable the local security check (only if you are using Bazaar's REST API)
28 | /// by passing null as [inAppBillingKey].
29 | ///
30 | /// You should listen to [onSucceed] callback to make sure FlutterPoolakey is connected successfully.
31 | ///
32 | /// You should listen to [onFailed] callback to handle connect error.
33 | ///
34 | /// You should listen to [onDisconnected] callback and call [FlutterPoolakey.connect] to reconnect again.
35 | ///
36 | /// This function may return an error, you should handle the error and check the stacktrace to resolve it.
37 | static Future connect(String? inAppBillingKey,
38 | {VoidCallback? onSucceed,
39 | VoidCallback? onFailed,
40 | VoidCallback? onDisconnected}) async {
41 | _registerConnectCallBack(onSucceed, onFailed, onDisconnected);
42 | await _channel
43 | .invokeMethod('connect', {'in_app_billing_key': inAppBillingKey});
44 | }
45 |
46 | static void _registerConnectCallBack(VoidCallback? onConnectionSucceed,
47 | VoidCallback? onConnectionFailed, VoidCallback? onDisconnected) {
48 | if (onConnectionSucceed == null &&
49 | onConnectionFailed == null &&
50 | onDisconnected == null) {
51 | return;
52 | }
53 | _channel.setMethodCallHandler((call) {
54 | if (call.method == 'disconnected') {
55 | onDisconnected?.call();
56 | return Future.value(true);
57 | } else if (call.method == 'connectionSucceed') {
58 | onConnectionSucceed?.call();
59 | return Future.value(true);
60 | } else if (call.method == 'connectionFailed') {
61 | onConnectionFailed?.call();
62 | return Future.value(true);
63 | }
64 | throw StateError('method ${call.method} is not supported');
65 | });
66 | }
67 |
68 | ///To avoid problems such as Memory Leak,
69 | ///you must disconnect from the market in the dispose method of your page widget
70 | /// or when you no longer have anything to do with it:
71 | static Future disconnect() async {
72 | return _channel.invokeMethod('disconnect');
73 | }
74 |
75 | /// Initiates the purchase flow
76 | ///
77 | /// [productId] is your product's identifier (also known as SKU).
78 | /// [payload] is an optional parameter that you can send, bazaar saves it in the [PurchaseInfo].
79 | /// You can access it through your back-end API.
80 | /// [dynamicPriceToken] This is a token that the developer can apply to any user to change the price of the product.
81 | /// for more info about this please read the site documentation.
82 | ///
83 | /// If any error happened (like when user cancels the flow) it throws a [PlatformException] with a stacktrace.
84 | /// You must handle the error with your logic.
85 | ///
86 | /// If it succeeded, it returns the [PurchaseInfo] of the purchased product.
87 | static Future purchase(
88 | String productId, {
89 | String payload = "",
90 | String dynamicPriceToken = "",
91 | }) async {
92 | final map = await _channel.invokeMethod('purchase', {
93 | 'product_id': productId,
94 | 'payload': payload,
95 | 'dynamicPriceToken': dynamicPriceToken
96 | });
97 | return PurchaseInfo.fromMap(map);
98 | }
99 |
100 | /// Initializes the subscription flow
101 | ///
102 | /// [productId] is your product's identifier (also known as SKU).
103 | /// [payload] is an optional parameter that you can send, bazaar saves it in the [PurchaseInfo].
104 | /// You can access it through your back-end API.
105 | /// [dynamicPriceToken] This is a token that the developer can apply to any user to change the price of the product.
106 | /// for more info about this please read the site documentation.
107 | ///
108 | /// If any error happened (like when user cancels the flow) it throws a [PlatformException] with a stacktrace.
109 | /// You must handle the error with your logic.
110 | ///
111 | /// If it succeeded, it returns the [PurchaseInfo] of the purchased product.
112 | static Future subscribe(
113 | String productId, {
114 | String payload = "",
115 | String dynamicPriceToken = "",
116 | }) async {
117 | final map = await _channel.invokeMethod('subscribe', {
118 | 'product_id': productId,
119 | 'payload': payload,
120 | 'dynamicPriceToken': dynamicPriceToken
121 | });
122 | return PurchaseInfo.fromMap(map);
123 | }
124 |
125 | /// Consumes a consumable product
126 | ///
127 | /// It consumes a product which you defined consumable in your business logic.
128 | /// If you consume a product, user can buy that product again.
129 | /// Otherwise user can only buy a purchasable product once.
130 | ///
131 | /// If any error happened it throws a [PlatformException] with a stacktrace.
132 | /// You must handle the error with your logic.
133 | ///
134 | /// [purchaseToken] is the purchase identifier, you can find it in [PurchaseInfo.purchaseToken]
135 | ///
136 | /// It returns true if the process is successful
137 | static Future consume(String purchaseToken) async {
138 | return await _channel
139 | .invokeMethod('consume', {'purchase_token': purchaseToken});
140 | }
141 |
142 | /// Returns all purchases list
143 | ///
144 | /// Retrieves list of [PurchaseInfo] which contains all purchased products in user's inventory.
145 | static Future> getAllPurchasedProducts() async {
146 | final List list = await _channel.invokeMethod("get_all_purchased_products");
147 | return list.map((map) => PurchaseInfo.fromMap(map)).toList();
148 | }
149 |
150 | /// Check Trial-Subscription existanse
151 | ///
152 | /// It availbles if not subscribe any item.
153 | static Future