├── .gitignore ├── .metadata ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── cn │ └── nekocode │ └── h5pay │ └── H5payPlugin.kt ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── cn │ │ │ │ │ └── nekocode │ │ │ │ │ └── h5pay_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── 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 │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ └── settings_aar.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Flutter.podspec │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ └── main.dart └── pubspec.yaml ├── image └── screenshot.gif ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── H5payPlugin.h │ ├── H5payPlugin.m │ └── SwiftH5payPlugin.swift └── h5pay.podspec ├── lib ├── h5pay.dart └── src │ ├── h5pay_channel.dart │ ├── h5pay_dialog.dart │ ├── h5pay_widget.dart │ └── utils.dart ├── pubspec.yaml └── test └── h5pay_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # https://github.com/flutter/plugins/blob/master/.gitignore 2 | .DS_Store 3 | .idea/ 4 | .vscode/ 5 | 6 | .packages 7 | .pub/ 8 | .dart_tool/ 9 | pubspec.lock 10 | flutter_export_environment.sh 11 | 12 | Podfile 13 | Podfile.lock 14 | Pods/ 15 | .symlinks/ 16 | **/Flutter/App.framework/ 17 | **/Flutter/Flutter.framework/ 18 | **/Flutter/Generated.xcconfig 19 | **/Flutter/flutter_assets/ 20 | ServiceDefinitions.json 21 | xcuserdata/ 22 | *.xcworkspace 23 | **/DerivedData/ 24 | 25 | local.properties 26 | keystore.properties 27 | .gradle/ 28 | gradlew 29 | gradlew.bat 30 | gradle-wrapper.jar 31 | *.iml 32 | 33 | GeneratedPluginRegistrant.h 34 | GeneratedPluginRegistrant.m 35 | GeneratedPluginRegistrant.java 36 | build/ 37 | .flutter-plugins 38 | .flutter-plugins-dependencies 39 | 40 | .project 41 | .classpath 42 | .settings -------------------------------------------------------------------------------- /.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: 1946fc4da0f80c522d7e3ae7d4f7309908ed86f2 8 | channel: dev 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # https://medium.com/flutter/test-flutter-apps-on-travis-3fd5142ecd8c 2 | 3 | os: 4 | - linux 5 | sudo: false 6 | addons: 7 | apt: 8 | # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18 9 | sources: 10 | - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version 11 | packages: 12 | - libstdc++6 13 | - fonts-droid-fallback 14 | before_script: 15 | - git clone https://github.com/flutter/flutter.git -b stable 16 | - ./flutter/bin/flutter doctor 17 | script: 18 | - ./flutter/bin/flutter test 19 | cache: 20 | directories: 21 | - $HOME/.pub-cache -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.1.1 2 | 3 | * Fix the issue of unable to jump on Android 4 | 5 | ## 1.1.0 6 | 7 | * Add support for setting http headers 8 | 9 | ## 1.0.1 10 | 11 | * Just bump version 12 | 13 | ## 1.0.0-nullsafety.0 14 | 15 | * Migrate to null safety 16 | 17 | ## 0.5.0 18 | 19 | * Refactor: getPaymentUrl => getPaymentArguments 20 | 21 | ## 0.4.2 22 | 23 | * Just update the README 24 | 25 | ## 0.4.1 26 | 27 | * Revert: use future to get payment url 28 | 29 | ## 0.4.0 30 | 31 | * Replace the 'getH5Url' argument with 'paymentUrl' 32 | * Expose some util methods from platform plugins 33 | 34 | ## 0.2.0 35 | 36 | * Handle unmounted state of widget 37 | 38 | ## 0.1.0 39 | 40 | * Initial release 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # h5pay 2 | [![build status](https://api.travis-ci.com/nekocode/h5pay-flutter.svg)](https://travis-ci.com/nekocode/h5pay-flutter) 3 | [![pub package](https://img.shields.io/pub/v/h5pay.svg)](https://pub.dev/packages/h5pay) 4 | 5 | A H5 payment (such as Alipay, WeChat Pay) plugin for flutter. 6 | 7 | 8 | 9 | ## Usage 10 | 11 | Use the `showH5PayDialog` method to show a loading dialog and jump to payment app. When user switches from payment app back to your app, you can check payment result with your server in the `verifyResult` callback (Optional). 12 | 13 | ```dart 14 | import 'package:h5pay/h5pay.dart'; 15 | 16 | final PaymentStatus status = await showH5PayDialog( 17 | context: context, 18 | // You can get payment url (normally is http or payment app scheme) from server in the getPaymentArguments callback 19 | getPaymentArguments: () async => PaymentArguments( 20 | url: 'https://is.gd/4cLE6j', 21 | redirectSchemes: ['alipay', 'alipays', 'weixin', 'wechat'], 22 | httpHeaders: { 23 | 'referer': 'https://xxx', 24 | }, 25 | ), 26 | verifyResult: () async => true, // check order result with your server 27 | ); 28 | if (status == PaymentStatus.success) { 29 | // Do something 30 | } 31 | ``` 32 | 33 | Values of `PaymentStatus`: 34 | 35 | ```dart 36 | enum PaymentStatus { 37 | idle, 38 | gettingArguments, 39 | getArgumentsFail, 40 | launchingUrl, 41 | cantLaunchUrl, // Maybe target payment app is not installed 42 | launchUrlTimeout, // Maybe redirecting url is fail 43 | jumping, 44 | jumpTimeout, 45 | verifying, 46 | success, 47 | fail, 48 | } 49 | ``` 50 | 51 | ### Notes 52 | 53 | * In iOS, for allowing to jump to the payment app from your app, you must add schemes of the payment apps into the `Info.plist` file. Just like: 54 | 55 | ```xml 56 | LSApplicationQueriesSchemes 57 | 58 | wechat 59 | weixin 60 | alipay 61 | alipays 62 | 63 | ``` 64 | 65 | ### Advanced 66 | 67 | If you have more complex requirements, you can use the `H5PayWidget`. Check the [example](example) for more detail. 68 | 69 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'cn.nekocode.h5pay' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.5.31' 6 | repositories { 7 | google() 8 | jcenter() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:7.0.2' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | apply plugin: 'kotlin-android' 26 | 27 | android { 28 | compileSdkVersion 30 29 | 30 | sourceSets { 31 | main.java.srcDirs += 'src/main/kotlin' 32 | } 33 | defaultConfig { 34 | minSdkVersion 16 35 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 36 | } 37 | lintOptions { 38 | disable 'InvalidPackage' 39 | } 40 | } 41 | 42 | dependencies { 43 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 44 | } 45 | -------------------------------------------------------------------------------- /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-5.6.4-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'h5pay' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/kotlin/cn/nekocode/h5pay/H5payPlugin.kt: -------------------------------------------------------------------------------- 1 | package cn.nekocode.h5pay 2 | 3 | import android.annotation.SuppressLint 4 | import android.annotation.TargetApi 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.content.pm.ApplicationInfo 8 | import android.net.Uri 9 | import android.os.Build 10 | import android.view.View 11 | import android.webkit.WebResourceRequest 12 | import android.webkit.WebView 13 | import android.webkit.WebViewClient 14 | import io.flutter.embedding.engine.plugins.FlutterPlugin 15 | import io.flutter.plugin.common.MethodCall 16 | import io.flutter.plugin.common.MethodChannel 17 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 18 | import io.flutter.plugin.common.MethodChannel.Result 19 | 20 | class H5payPlugin : FlutterPlugin, MethodCallHandler { 21 | private lateinit var channel: MethodChannel 22 | private lateinit var binding: FlutterPlugin.FlutterPluginBinding 23 | private var targetSchemes: Iterable = emptyList() 24 | private var httpHeaders: Map = emptyMap() 25 | private var webView: WebView? = null 26 | private var result: Result? = null 27 | 28 | override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { 29 | this.binding = binding 30 | channel = MethodChannel(binding.binaryMessenger, "h5pay") 31 | channel.setMethodCallHandler(this) 32 | } 33 | 34 | override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { 35 | channel.setMethodCallHandler(null) 36 | } 37 | 38 | override fun onMethodCall(call: MethodCall, result: Result) { 39 | when (call.method) { 40 | "launchRedirectUrl" -> { 41 | launchRedirectUrl(call, result) 42 | } 43 | "launchUrl" -> { 44 | launchUrl(call, result) 45 | } 46 | "canLaunch" -> { 47 | canLaunch(call, result) 48 | } 49 | else -> { 50 | result.notImplemented() 51 | } 52 | } 53 | } 54 | 55 | private fun launchRedirectUrl(call: MethodCall, result: Result) { 56 | val arguments = call.arguments as? HashMap<*, *> 57 | targetSchemes = (arguments?.get("targetSchemes") as? Iterable<*>) 58 | ?.filterIsInstance() 59 | ?: emptyList() 60 | @Suppress("UNCHECKED_CAST") 61 | httpHeaders = (arguments?.get("httpHeaders") as? Map) ?: emptyMap() 62 | 63 | // Try to launch url directly 64 | val url = (arguments?.get("url") as? String) 65 | if (url == null) { 66 | result.success(false) 67 | return 68 | } 69 | if (Utils.hasScheme(url, targetSchemes)) { 70 | result.success(Utils.launchUrl(binding.applicationContext, url)) 71 | return 72 | } 73 | 74 | initWebView() 75 | 76 | this.result = result 77 | webView!!.run { 78 | stopLoading() 79 | loadUrl(url, httpHeaders) 80 | } 81 | } 82 | 83 | private fun launchUrl(call: MethodCall, result: Result) { 84 | val arguments = call.arguments as? HashMap<*, *> 85 | val url = arguments?.get("url") as? String 86 | result.success(Utils.launchUrl(binding.applicationContext, url)) 87 | } 88 | 89 | private fun canLaunch(call: MethodCall, result: Result) { 90 | val arguments = call.arguments as? HashMap<*, *> 91 | val url = arguments?.get("url") as? String 92 | if (url == null) { 93 | result.success(false) 94 | return 95 | } 96 | result.success(Utils.canLaunch(binding.applicationContext, url)) 97 | } 98 | 99 | @SuppressLint("SetJavaScriptEnabled") 100 | private fun initWebView() { 101 | if (webView != null) { 102 | return 103 | } 104 | 105 | val context = binding.applicationContext 106 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 107 | if (0 != (context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE)) { 108 | WebView.setWebContentsDebuggingEnabled(true) 109 | } 110 | } 111 | 112 | val webView = WebView(context) 113 | webView.visibility = View.GONE 114 | webView.settings.javaScriptEnabled = true 115 | webView.settings.domStorageEnabled = true 116 | webView.settings.allowFileAccessFromFileURLs = true 117 | webView.settings.allowUniversalAccessFromFileURLs = true 118 | webView.settings.loadsImagesAutomatically = false 119 | webView.settings.blockNetworkImage = false 120 | webView.webViewClient = Client() 121 | this.webView = webView 122 | } 123 | 124 | inner class Client : WebViewClient() { 125 | override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { 126 | return shouldOverrideUrlLoading(url) 127 | } 128 | 129 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 130 | override fun shouldOverrideUrlLoading( 131 | view: WebView?, 132 | request: WebResourceRequest? 133 | ): Boolean { 134 | val url = request?.url?.toString() 135 | return shouldOverrideUrlLoading(url) 136 | } 137 | 138 | private fun shouldOverrideUrlLoading(url: String?): Boolean { 139 | if (Utils.hasScheme(url, targetSchemes)) { 140 | result?.success(Utils.launchUrl(binding.applicationContext, url)) 141 | return true 142 | } 143 | return false 144 | } 145 | } 146 | } 147 | 148 | object Utils { 149 | private const val FALLBACK_COMPONENT_NAME = 150 | "{com.android.fallback/com.android.fallback.Fallback}" 151 | 152 | fun launchUrl(context: Context, url: String?): Boolean { 153 | return if (!canLaunch(context, url)) { 154 | false 155 | } else { 156 | try { 157 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)); 158 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 159 | context.startActivity(intent) 160 | true 161 | } catch (_: Exception) { 162 | false 163 | } 164 | } 165 | } 166 | 167 | fun canLaunch(context: Context, url: String?): Boolean { 168 | url ?: return false 169 | val launchIntent = Intent(Intent.ACTION_VIEW).apply { 170 | data = Uri.parse(url) 171 | } 172 | val componentName = launchIntent.resolveActivity(context.packageManager) 173 | 174 | return componentName != null && 175 | FALLBACK_COMPONENT_NAME != componentName.toShortString() 176 | } 177 | 178 | fun hasScheme(url: String?, targetSchemes: Iterable): Boolean { 179 | url ?: return false 180 | for (scheme in targetSchemes) { 181 | if (!url.startsWith("$scheme:")) { 182 | continue 183 | } 184 | return true 185 | } 186 | return false 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /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 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Web related 33 | lib/generated_plugin_registrant.dart 34 | 35 | # Exceptions to above rules. 36 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 37 | -------------------------------------------------------------------------------- /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: 1946fc4da0f80c522d7e3ae7d4f7309908ed86f2 8 | channel: dev 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # h5pay_example 2 | 3 | Demonstrates how to use the h5pay 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 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 30 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | applicationId "cn.nekocode.h5pay_example" 41 | minSdkVersion 16 42 | versionCode flutterVersionCode.toInteger() 43 | versionName flutterVersionName 44 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 45 | } 46 | 47 | buildTypes { 48 | release { 49 | // TODO: Add your own signing config for the release build. 50 | // Signing with the debug keys for now, so `flutter run --release` works. 51 | signingConfig signingConfigs.debug 52 | } 53 | } 54 | } 55 | 56 | flutter { 57 | source '../..' 58 | } 59 | 60 | dependencies { 61 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 62 | testImplementation 'junit:junit:4.12' 63 | androidTestImplementation 'androidx.test:runner:1.1.1' 64 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 65 | } 66 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 11 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 | 36 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/cn/nekocode/h5pay_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package cn.nekocode.h5pay_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /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/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/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/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/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/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/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/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/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/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.5.31' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.0.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=true 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Mar 27 17:53:17 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Generated.xcconfig 20 | Flutter/app.flx 21 | Flutter/app.zip 22 | Flutter/flutter_assets/ 23 | Flutter/flutter_export_environment.sh 24 | ServiceDefinitions.json 25 | Runner/GeneratedPluginRegistrant.* 26 | 27 | # Exceptions to above rules. 28 | !default.mode1v3 29 | !default.mode2v3 30 | !default.pbxuser 31 | !default.perspectivev3 32 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE: This podspec is NOT to be published. It is only used as a local source! 3 | # This is a generated file; do not edit or check into version control. 4 | # 5 | 6 | Pod::Spec.new do |s| 7 | s.name = 'Flutter' 8 | s.version = '1.0.0' 9 | s.summary = 'High-performance, high-fidelity mobile apps.' 10 | s.homepage = 'https://flutter.io' 11 | s.license = { :type => 'MIT' } 12 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 13 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 14 | s.ios.deployment_target = '9.0' 15 | # Framework linking is handled by Flutter tooling, not CocoaPods. 16 | # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. 17 | s.vendored_frameworks = 'path/to/nothing' 18 | end 19 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | B23475D7AF1DC6C8B5044C31 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1588BAF74BF9B28F329E0E6B /* Pods_Runner.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 35 | 1588BAF74BF9B28F329E0E6B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 37 | 5CD299C846584808A42E53B6 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 38 | 69509F9C50D8DE03936A48C7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 39 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 40 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 41 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 42 | 870474766FD64077A4632910 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | B23475D7AF1DC6C8B5044C31 /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 9740EEB11CF90186004384FC /* Flutter */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 68 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 69 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 70 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 71 | ); 72 | name = Flutter; 73 | sourceTree = ""; 74 | }; 75 | 97C146E51CF9000F007C117D = { 76 | isa = PBXGroup; 77 | children = ( 78 | 9740EEB11CF90186004384FC /* Flutter */, 79 | 97C146F01CF9000F007C117D /* Runner */, 80 | 97C146EF1CF9000F007C117D /* Products */, 81 | FA11FCB0DD8A86C785225185 /* Pods */, 82 | B22AC23DA418F21FC9618090 /* Frameworks */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | 97C146EF1CF9000F007C117D /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146EE1CF9000F007C117D /* Runner.app */, 90 | ); 91 | name = Products; 92 | sourceTree = ""; 93 | }; 94 | 97C146F01CF9000F007C117D /* Runner */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 98 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 99 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 100 | 97C147021CF9000F007C117D /* Info.plist */, 101 | 97C146F11CF9000F007C117D /* Supporting Files */, 102 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 103 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 104 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 105 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 106 | ); 107 | path = Runner; 108 | sourceTree = ""; 109 | }; 110 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | ); 114 | name = "Supporting Files"; 115 | sourceTree = ""; 116 | }; 117 | B22AC23DA418F21FC9618090 /* Frameworks */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 1588BAF74BF9B28F329E0E6B /* Pods_Runner.framework */, 121 | ); 122 | name = Frameworks; 123 | sourceTree = ""; 124 | }; 125 | FA11FCB0DD8A86C785225185 /* Pods */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 870474766FD64077A4632910 /* Pods-Runner.debug.xcconfig */, 129 | 5CD299C846584808A42E53B6 /* Pods-Runner.release.xcconfig */, 130 | 69509F9C50D8DE03936A48C7 /* Pods-Runner.profile.xcconfig */, 131 | ); 132 | path = Pods; 133 | sourceTree = ""; 134 | }; 135 | /* End PBXGroup section */ 136 | 137 | /* Begin PBXNativeTarget section */ 138 | 97C146ED1CF9000F007C117D /* Runner */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 141 | buildPhases = ( 142 | B623D3E015095CCE157A4C2E /* [CP] Check Pods Manifest.lock */, 143 | 9740EEB61CF901F6004384FC /* Run Script */, 144 | 97C146EA1CF9000F007C117D /* Sources */, 145 | 97C146EB1CF9000F007C117D /* Frameworks */, 146 | 97C146EC1CF9000F007C117D /* Resources */, 147 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 148 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 149 | D1E37E9FEB65B98FE9B1F8DC /* [CP] Embed Pods Frameworks */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = Runner; 156 | productName = Runner; 157 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 158 | productType = "com.apple.product-type.application"; 159 | }; 160 | /* End PBXNativeTarget section */ 161 | 162 | /* Begin PBXProject section */ 163 | 97C146E61CF9000F007C117D /* Project object */ = { 164 | isa = PBXProject; 165 | attributes = { 166 | LastUpgradeCheck = 1020; 167 | ORGANIZATIONNAME = "The Chromium Authors"; 168 | TargetAttributes = { 169 | 97C146ED1CF9000F007C117D = { 170 | CreatedOnToolsVersion = 7.3.1; 171 | LastSwiftMigration = 0910; 172 | }; 173 | }; 174 | }; 175 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 176 | compatibilityVersion = "Xcode 3.2"; 177 | developmentRegion = en; 178 | hasScannedForEncodings = 0; 179 | knownRegions = ( 180 | en, 181 | Base, 182 | ); 183 | mainGroup = 97C146E51CF9000F007C117D; 184 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 185 | projectDirPath = ""; 186 | projectRoot = ""; 187 | targets = ( 188 | 97C146ED1CF9000F007C117D /* Runner */, 189 | ); 190 | }; 191 | /* End PBXProject section */ 192 | 193 | /* Begin PBXResourcesBuildPhase section */ 194 | 97C146EC1CF9000F007C117D /* Resources */ = { 195 | isa = PBXResourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 199 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 200 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 201 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | /* End PBXResourcesBuildPhase section */ 206 | 207 | /* Begin PBXShellScriptBuildPhase section */ 208 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 209 | isa = PBXShellScriptBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | ); 213 | inputPaths = ( 214 | ); 215 | name = "Thin Binary"; 216 | outputPaths = ( 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | shellPath = /bin/sh; 220 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 221 | }; 222 | 9740EEB61CF901F6004384FC /* Run Script */ = { 223 | isa = PBXShellScriptBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | inputPaths = ( 228 | ); 229 | name = "Run Script"; 230 | outputPaths = ( 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | shellPath = /bin/sh; 234 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 235 | }; 236 | B623D3E015095CCE157A4C2E /* [CP] Check Pods Manifest.lock */ = { 237 | isa = PBXShellScriptBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | ); 241 | inputFileListPaths = ( 242 | ); 243 | inputPaths = ( 244 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 245 | "${PODS_ROOT}/Manifest.lock", 246 | ); 247 | name = "[CP] Check Pods Manifest.lock"; 248 | outputFileListPaths = ( 249 | ); 250 | outputPaths = ( 251 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | shellPath = /bin/sh; 255 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 256 | showEnvVarsInLog = 0; 257 | }; 258 | D1E37E9FEB65B98FE9B1F8DC /* [CP] Embed Pods Frameworks */ = { 259 | isa = PBXShellScriptBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | ); 263 | inputPaths = ( 264 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", 265 | "${BUILT_PRODUCTS_DIR}/h5pay/h5pay.framework", 266 | ); 267 | name = "[CP] Embed Pods Frameworks"; 268 | outputPaths = ( 269 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/h5pay.framework", 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | shellPath = /bin/sh; 273 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 274 | showEnvVarsInLog = 0; 275 | }; 276 | /* End PBXShellScriptBuildPhase section */ 277 | 278 | /* Begin PBXSourcesBuildPhase section */ 279 | 97C146EA1CF9000F007C117D /* Sources */ = { 280 | isa = PBXSourcesBuildPhase; 281 | buildActionMask = 2147483647; 282 | files = ( 283 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 284 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXSourcesBuildPhase section */ 289 | 290 | /* Begin PBXVariantGroup section */ 291 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 292 | isa = PBXVariantGroup; 293 | children = ( 294 | 97C146FB1CF9000F007C117D /* Base */, 295 | ); 296 | name = Main.storyboard; 297 | sourceTree = ""; 298 | }; 299 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 300 | isa = PBXVariantGroup; 301 | children = ( 302 | 97C147001CF9000F007C117D /* Base */, 303 | ); 304 | name = LaunchScreen.storyboard; 305 | sourceTree = ""; 306 | }; 307 | /* End PBXVariantGroup section */ 308 | 309 | /* Begin XCBuildConfiguration section */ 310 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_ANALYZER_NONNULL = YES; 315 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 316 | CLANG_CXX_LIBRARY = "libc++"; 317 | CLANG_ENABLE_MODULES = YES; 318 | CLANG_ENABLE_OBJC_ARC = YES; 319 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 320 | CLANG_WARN_BOOL_CONVERSION = YES; 321 | CLANG_WARN_COMMA = YES; 322 | CLANG_WARN_CONSTANT_CONVERSION = YES; 323 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 324 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 325 | CLANG_WARN_EMPTY_BODY = YES; 326 | CLANG_WARN_ENUM_CONVERSION = YES; 327 | CLANG_WARN_INFINITE_RECURSION = YES; 328 | CLANG_WARN_INT_CONVERSION = YES; 329 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 330 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 331 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 334 | CLANG_WARN_STRICT_PROTOTYPES = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 336 | CLANG_WARN_UNREACHABLE_CODE = YES; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 341 | ENABLE_NS_ASSERTIONS = NO; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu99; 344 | GCC_NO_COMMON_BLOCKS = YES; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 352 | MTL_ENABLE_DEBUG_INFO = NO; 353 | SDKROOT = iphoneos; 354 | SUPPORTED_PLATFORMS = iphoneos; 355 | TARGETED_DEVICE_FAMILY = "1,2"; 356 | VALIDATE_PRODUCT = YES; 357 | }; 358 | name = Profile; 359 | }; 360 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 361 | isa = XCBuildConfiguration; 362 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 363 | buildSettings = { 364 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 365 | CLANG_ENABLE_MODULES = YES; 366 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 367 | ENABLE_BITCODE = NO; 368 | FRAMEWORK_SEARCH_PATHS = ( 369 | "$(inherited)", 370 | "$(PROJECT_DIR)/Flutter", 371 | ); 372 | INFOPLIST_FILE = Runner/Info.plist; 373 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 374 | LIBRARY_SEARCH_PATHS = ( 375 | "$(inherited)", 376 | "$(PROJECT_DIR)/Flutter", 377 | ); 378 | PRODUCT_BUNDLE_IDENTIFIER = cn.nekocode.h5payExample; 379 | PRODUCT_NAME = "$(TARGET_NAME)"; 380 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 381 | SWIFT_VERSION = 4.0; 382 | VERSIONING_SYSTEM = "apple-generic"; 383 | }; 384 | name = Profile; 385 | }; 386 | 97C147031CF9000F007C117D /* Debug */ = { 387 | isa = XCBuildConfiguration; 388 | buildSettings = { 389 | ALWAYS_SEARCH_USER_PATHS = NO; 390 | CLANG_ANALYZER_NONNULL = YES; 391 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 392 | CLANG_CXX_LIBRARY = "libc++"; 393 | CLANG_ENABLE_MODULES = YES; 394 | CLANG_ENABLE_OBJC_ARC = YES; 395 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 396 | CLANG_WARN_BOOL_CONVERSION = YES; 397 | CLANG_WARN_COMMA = YES; 398 | CLANG_WARN_CONSTANT_CONVERSION = YES; 399 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 400 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 401 | CLANG_WARN_EMPTY_BODY = YES; 402 | CLANG_WARN_ENUM_CONVERSION = YES; 403 | CLANG_WARN_INFINITE_RECURSION = YES; 404 | CLANG_WARN_INT_CONVERSION = YES; 405 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 406 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 407 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 408 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 409 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 410 | CLANG_WARN_STRICT_PROTOTYPES = YES; 411 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 412 | CLANG_WARN_UNREACHABLE_CODE = YES; 413 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 414 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 415 | COPY_PHASE_STRIP = NO; 416 | DEBUG_INFORMATION_FORMAT = dwarf; 417 | ENABLE_STRICT_OBJC_MSGSEND = YES; 418 | ENABLE_TESTABILITY = YES; 419 | GCC_C_LANGUAGE_STANDARD = gnu99; 420 | GCC_DYNAMIC_NO_PIC = NO; 421 | GCC_NO_COMMON_BLOCKS = YES; 422 | GCC_OPTIMIZATION_LEVEL = 0; 423 | GCC_PREPROCESSOR_DEFINITIONS = ( 424 | "DEBUG=1", 425 | "$(inherited)", 426 | ); 427 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 428 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 429 | GCC_WARN_UNDECLARED_SELECTOR = YES; 430 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 431 | GCC_WARN_UNUSED_FUNCTION = YES; 432 | GCC_WARN_UNUSED_VARIABLE = YES; 433 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 434 | MTL_ENABLE_DEBUG_INFO = YES; 435 | ONLY_ACTIVE_ARCH = YES; 436 | SDKROOT = iphoneos; 437 | TARGETED_DEVICE_FAMILY = "1,2"; 438 | }; 439 | name = Debug; 440 | }; 441 | 97C147041CF9000F007C117D /* Release */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | ALWAYS_SEARCH_USER_PATHS = NO; 445 | CLANG_ANALYZER_NONNULL = YES; 446 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 447 | CLANG_CXX_LIBRARY = "libc++"; 448 | CLANG_ENABLE_MODULES = YES; 449 | CLANG_ENABLE_OBJC_ARC = YES; 450 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 451 | CLANG_WARN_BOOL_CONVERSION = YES; 452 | CLANG_WARN_COMMA = YES; 453 | CLANG_WARN_CONSTANT_CONVERSION = YES; 454 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 455 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 456 | CLANG_WARN_EMPTY_BODY = YES; 457 | CLANG_WARN_ENUM_CONVERSION = YES; 458 | CLANG_WARN_INFINITE_RECURSION = YES; 459 | CLANG_WARN_INT_CONVERSION = YES; 460 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 461 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 462 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 463 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 464 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 465 | CLANG_WARN_STRICT_PROTOTYPES = YES; 466 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 467 | CLANG_WARN_UNREACHABLE_CODE = YES; 468 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 469 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 470 | COPY_PHASE_STRIP = NO; 471 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 472 | ENABLE_NS_ASSERTIONS = NO; 473 | ENABLE_STRICT_OBJC_MSGSEND = YES; 474 | GCC_C_LANGUAGE_STANDARD = gnu99; 475 | GCC_NO_COMMON_BLOCKS = YES; 476 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 477 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 478 | GCC_WARN_UNDECLARED_SELECTOR = YES; 479 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 480 | GCC_WARN_UNUSED_FUNCTION = YES; 481 | GCC_WARN_UNUSED_VARIABLE = YES; 482 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 483 | MTL_ENABLE_DEBUG_INFO = NO; 484 | SDKROOT = iphoneos; 485 | SUPPORTED_PLATFORMS = iphoneos; 486 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 487 | TARGETED_DEVICE_FAMILY = "1,2"; 488 | VALIDATE_PRODUCT = YES; 489 | }; 490 | name = Release; 491 | }; 492 | 97C147061CF9000F007C117D /* Debug */ = { 493 | isa = XCBuildConfiguration; 494 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 495 | buildSettings = { 496 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 497 | CLANG_ENABLE_MODULES = YES; 498 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 499 | ENABLE_BITCODE = NO; 500 | FRAMEWORK_SEARCH_PATHS = ( 501 | "$(inherited)", 502 | "$(PROJECT_DIR)/Flutter", 503 | ); 504 | INFOPLIST_FILE = Runner/Info.plist; 505 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 506 | LIBRARY_SEARCH_PATHS = ( 507 | "$(inherited)", 508 | "$(PROJECT_DIR)/Flutter", 509 | ); 510 | PRODUCT_BUNDLE_IDENTIFIER = cn.nekocode.h5payExample; 511 | PRODUCT_NAME = "$(TARGET_NAME)"; 512 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 513 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 514 | SWIFT_VERSION = 4.0; 515 | VERSIONING_SYSTEM = "apple-generic"; 516 | }; 517 | name = Debug; 518 | }; 519 | 97C147071CF9000F007C117D /* Release */ = { 520 | isa = XCBuildConfiguration; 521 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 522 | buildSettings = { 523 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 524 | CLANG_ENABLE_MODULES = YES; 525 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 526 | ENABLE_BITCODE = NO; 527 | FRAMEWORK_SEARCH_PATHS = ( 528 | "$(inherited)", 529 | "$(PROJECT_DIR)/Flutter", 530 | ); 531 | INFOPLIST_FILE = Runner/Info.plist; 532 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 533 | LIBRARY_SEARCH_PATHS = ( 534 | "$(inherited)", 535 | "$(PROJECT_DIR)/Flutter", 536 | ); 537 | PRODUCT_BUNDLE_IDENTIFIER = cn.nekocode.h5payExample; 538 | PRODUCT_NAME = "$(TARGET_NAME)"; 539 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 540 | SWIFT_VERSION = 4.0; 541 | VERSIONING_SYSTEM = "apple-generic"; 542 | }; 543 | name = Release; 544 | }; 545 | /* End XCBuildConfiguration section */ 546 | 547 | /* Begin XCConfigurationList section */ 548 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 549 | isa = XCConfigurationList; 550 | buildConfigurations = ( 551 | 97C147031CF9000F007C117D /* Debug */, 552 | 97C147041CF9000F007C117D /* Release */, 553 | 249021D3217E4FDB00AE95B9 /* Profile */, 554 | ); 555 | defaultConfigurationIsVisible = 0; 556 | defaultConfigurationName = Release; 557 | }; 558 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 559 | isa = XCConfigurationList; 560 | buildConfigurations = ( 561 | 97C147061CF9000F007C117D /* Debug */, 562 | 97C147071CF9000F007C117D /* Release */, 563 | 249021D4217E4FDB00AE95B9 /* Profile */, 564 | ); 565 | defaultConfigurationIsVisible = 0; 566 | defaultConfigurationName = Release; 567 | }; 568 | /* End XCConfigurationList section */ 569 | }; 570 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 571 | } 572 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | h5pay_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | LSApplicationQueriesSchemes 45 | 46 | wechat 47 | weixin 48 | alipay 49 | alipays 50 | mailto 51 | sms 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:h5pay/h5pay.dart'; 3 | 4 | void main() => runApp(MaterialApp(home: IndexPage())); 5 | 6 | class IndexPage extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text('Example app'), 12 | ), 13 | body: Center( 14 | child: TextButton( 15 | child: Text('Plugin Test'), 16 | onPressed: () { 17 | Navigator.push( 18 | context, 19 | MaterialPageRoute(builder: (context) => MainPage()), 20 | ); 21 | }, 22 | ), 23 | ), 24 | ); 25 | } 26 | } 27 | 28 | class MainPage extends StatelessWidget { 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | appBar: AppBar( 33 | title: const Text('Plugin Test'), 34 | ), 35 | body: MainBody(), 36 | ); 37 | } 38 | } 39 | 40 | class MainBody extends StatelessWidget { 41 | @override 42 | Widget build(BuildContext context) { 43 | return Center( 44 | child: Column( 45 | mainAxisAlignment: MainAxisAlignment.center, 46 | children: [ 47 | H5PayWidget( 48 | getPaymentArguments: () async => PaymentArguments( 49 | url: 'https://is.gd/4cLE6j', 50 | redirectSchemes: ['sms'], 51 | httpHeaders: { 52 | 'referer': 'https://is.gd', 53 | }, 54 | ), 55 | verifyResult: () async => true, 56 | builder: (context, status, controller) => TextButton( 57 | onPressed: status != PaymentStatus.gettingArguments && 58 | status != PaymentStatus.launchingUrl && 59 | status != PaymentStatus.jumping && 60 | status != PaymentStatus.verifying 61 | ? () async { 62 | controller.launch(); 63 | } 64 | : null, 65 | child: Text(status.toString()), 66 | ), 67 | ), 68 | SizedBox(height: 10), 69 | TextButton( 70 | onPressed: () async { 71 | final status = await showH5PayDialog( 72 | context: context, 73 | getPaymentArguments: () async => PaymentArguments( 74 | url: 'https://is.gd/4cLE6j', 75 | redirectSchemes: ['sms'], 76 | httpHeaders: { 77 | 'referer': 'https://is.gd', 78 | }, 79 | ), 80 | verifyResult: () async => true, 81 | ); 82 | ScaffoldMessenger.of(context).showSnackBar(SnackBar( 83 | content: Text(status.toString()), 84 | )); 85 | }, 86 | child: Text('showH5PayDialog'), 87 | ), 88 | ], 89 | ), 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: h5pay_example 2 | description: Demonstrates how to use the h5pay plugin. 3 | publish_to: 'none' 4 | 5 | environment: 6 | sdk: ">=2.12.0 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | 12 | # The following adds the Cupertino Icons font to your application. 13 | # Use with the CupertinoIcons class for iOS style icons. 14 | cupertino_icons: ^1.0.6 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | h5pay: 21 | path: ../ 22 | 23 | # For information on the generic Dart part of this file, see the 24 | # following page: https://dart.dev/tools/pub/pubspec 25 | 26 | # The following section is specific to Flutter. 27 | flutter: 28 | 29 | # The following line ensures that the Material Icons font is 30 | # included with your application, so that you can use the icons in 31 | # the material Icons class. 32 | uses-material-design: true 33 | 34 | # To add assets to your application, add an assets section, like this: 35 | # assets: 36 | # - images/a_dot_burr.jpeg 37 | # - images/a_dot_ham.jpeg 38 | 39 | # An image asset can refer to one or more resolution-specific "variants", see 40 | # https://flutter.dev/assets-and-images/#resolution-aware. 41 | 42 | # For details regarding adding assets from package dependencies, see 43 | # https://flutter.dev/assets-and-images/#from-packages 44 | 45 | # To add custom fonts to your application, add a fonts section here, 46 | # in this "flutter" section. Each entry in this list should have a 47 | # "family" key with the font family name, and a "fonts" key with a 48 | # list giving the asset and other descriptors for the font. For 49 | # example: 50 | # fonts: 51 | # - family: Schyler 52 | # fonts: 53 | # - asset: fonts/Schyler-Regular.ttf 54 | # - asset: fonts/Schyler-Italic.ttf 55 | # style: italic 56 | # - family: Trajan Pro 57 | # fonts: 58 | # - asset: fonts/TrajanPro.ttf 59 | # - asset: fonts/TrajanPro_Bold.ttf 60 | # weight: 700 61 | # 62 | # For details regarding fonts from package dependencies, 63 | # see https://flutter.dev/custom-fonts/#from-packages 64 | -------------------------------------------------------------------------------- /image/screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/image/screenshot.gif -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/h5pay-flutter/0cee3173a0788d30899b045ca438408616ba0f68/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/H5payPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface H5payPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/H5payPlugin.m: -------------------------------------------------------------------------------- 1 | #import "H5payPlugin.h" 2 | #import 3 | 4 | @implementation H5payPlugin 5 | + (void)registerWithRegistrar:(NSObject*)registrar { 6 | [SwiftH5payPlugin registerWithRegistrar:registrar]; 7 | } 8 | @end 9 | -------------------------------------------------------------------------------- /ios/Classes/SwiftH5payPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import WebKit 4 | 5 | public class SwiftH5payPlugin: NSObject, FlutterPlugin { 6 | private var targetSchemes = [String]() 7 | private var httpHeaders = [String:String]() 8 | private var webView: WKWebView? = nil 9 | private var result: FlutterResult? = nil 10 | 11 | public static func register(with registrar: FlutterPluginRegistrar) { 12 | let channel = FlutterMethodChannel(name: "h5pay", binaryMessenger: registrar.messenger()) 13 | let instance = SwiftH5payPlugin() 14 | registrar.addMethodCallDelegate(instance, channel: channel) 15 | } 16 | 17 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 18 | switch call.method { 19 | case "launchRedirectUrl": 20 | launchRedirectUrl(call, result: result) 21 | break 22 | case "launchUrl": 23 | launchUrl(call, result: result) 24 | break 25 | case "canLaunch": 26 | canLaunch(call, result: result) 27 | break 28 | default: 29 | result(FlutterMethodNotImplemented) 30 | break 31 | } 32 | } 33 | 34 | private func launchRedirectUrl(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 35 | guard 36 | let arguments = call.arguments as? [String: Any], 37 | let urlString = arguments["url"] as? String, 38 | let url = URL(string: urlString) 39 | else { 40 | result(false) 41 | return 42 | } 43 | if let targetSchemes = arguments["targetSchemes"] as? [String] { 44 | self.targetSchemes = targetSchemes 45 | } else { 46 | self.targetSchemes = [String]() 47 | } 48 | if let httpHeaders = arguments["httpHeaders"] as? [String:String] { 49 | self.httpHeaders = httpHeaders 50 | } else { 51 | self.httpHeaders = [String:String]() 52 | } 53 | 54 | // Try to launch url directly 55 | if (Utils.hasScheme(url, targetSchemes)) { 56 | result(Utils.launchUrl(url)) 57 | return 58 | } 59 | 60 | initWebView() 61 | validateWebView() 62 | 63 | self.result = result 64 | var request = URLRequest.init(url: url) 65 | for (key, value) in self.httpHeaders { 66 | request.setValue(key, forHTTPHeaderField: value) 67 | } 68 | webView!.load(request) 69 | } 70 | 71 | private func launchUrl(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 72 | guard 73 | let arguments = call.arguments as? [String: Any], 74 | let urlString = arguments["url"] as? String, 75 | let url = URL(string: urlString) 76 | else { 77 | result(false) 78 | return 79 | } 80 | result(Utils.launchUrl(url)) 81 | } 82 | 83 | private func canLaunch(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 84 | guard 85 | let arguments = call.arguments as? [String: Any], 86 | let urlString = arguments["url"] as? String, 87 | let url = URL(string: urlString) 88 | else { 89 | result(false) 90 | return 91 | } 92 | result(Utils.canLaunch(url)) 93 | } 94 | 95 | private func initWebView() { 96 | if (webView != nil) { 97 | return 98 | } 99 | 100 | let config: WKWebViewConfiguration = WKWebViewConfiguration() 101 | if #available(iOS 9.0, *) { 102 | config.allowsPictureInPictureMediaPlayback = false 103 | config.requiresUserActionForMediaPlayback = true 104 | } 105 | if #available(iOS 10.0, *) { 106 | config.mediaTypesRequiringUserActionForPlayback = .all 107 | } 108 | config.allowsInlineMediaPlayback = true 109 | 110 | let preferences = WKPreferences() 111 | preferences.javaScriptEnabled = true 112 | config.preferences = preferences 113 | 114 | let webView = WKWebView(frame: .zero, configuration: config) 115 | webView.isHidden = true 116 | webView.navigationDelegate = self 117 | self.webView = webView 118 | 119 | validateWebView() 120 | } 121 | 122 | private func validateWebView() { 123 | if webView != nil && webView?.superview == nil { 124 | UIApplication.shared.keyWindow?.addSubview(webView!) 125 | } 126 | } 127 | } 128 | 129 | extension SwiftH5payPlugin: WKNavigationDelegate { 130 | public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { 131 | decisionHandler(decidePolicyForRequest(navigationAction.request)) 132 | } 133 | 134 | private func decidePolicyForRequest(_ request: URLRequest) -> WKNavigationActionPolicy { 135 | if let url = request.url { 136 | if (Utils.hasScheme(url, targetSchemes)) { 137 | result?(Utils.launchUrl(url)) 138 | return .cancel 139 | } 140 | } 141 | return .allow 142 | } 143 | } 144 | 145 | class Utils { 146 | public static func launchUrl(_ url: URL) -> Bool { 147 | if !canLaunch(url) { 148 | return false 149 | } else { 150 | let success = UIApplication.shared.openURL(url) 151 | return success 152 | } 153 | } 154 | 155 | public static func canLaunch(_ url: URL) -> Bool { 156 | return UIApplication.shared.canOpenURL(url) 157 | } 158 | 159 | public static func hasScheme(_ url: URL, _ targetSchemes: [String]) -> Bool { 160 | let urlString = url.absoluteString 161 | for scheme in targetSchemes { 162 | if !urlString.hasPrefix(scheme + ":") { 163 | continue 164 | } 165 | return true 166 | } 167 | return false 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /ios/h5pay.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint h5pay.podspec' to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'h5pay' 7 | s.version = '0.0.1' 8 | s.summary = 'A new flutter plugin project.' 9 | s.description = <<-DESC 10 | A new flutter plugin project. 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.public_header_files = 'Classes/**/*.h' 18 | s.dependency 'Flutter' 19 | s.platform = :ios, '8.0' 20 | 21 | # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. 22 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } 23 | end 24 | -------------------------------------------------------------------------------- /lib/h5pay.dart: -------------------------------------------------------------------------------- 1 | export 'src/h5pay_channel.dart'; 2 | export 'src/h5pay_dialog.dart'; 3 | export 'src/h5pay_widget.dart'; 4 | -------------------------------------------------------------------------------- /lib/src/h5pay_channel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | class H5PayChannel { 4 | static const _channel = const MethodChannel('h5pay'); 5 | 6 | static Future launchRedirectUrl( 7 | String url, 8 | List targetSchemes, { 9 | Map? httpHeaders, 10 | }) { 11 | print(httpHeaders); 12 | return _channel.invokeMethod( 13 | 'launchRedirectUrl', 14 | { 15 | 'url': url, 16 | 'targetSchemes': targetSchemes, 17 | 'httpHeaders': httpHeaders, 18 | }, 19 | ); 20 | } 21 | 22 | static Future launchUrl(String url) { 23 | return _channel.invokeMethod( 24 | 'launchUrl', 25 | { 26 | 'url': url, 27 | }, 28 | ); 29 | } 30 | 31 | static Future canLaunch(String url) { 32 | return _channel.invokeMethod( 33 | 'canLaunch', 34 | { 35 | 'url': url, 36 | }, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/h5pay_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | 5 | import 'h5pay_widget.dart'; 6 | 7 | Future showH5PayDialog({ 8 | required BuildContext context, 9 | required GetArgumentsCallback getPaymentArguments, 10 | VerifyResultCallback? verifyResult, 11 | WidgetBuilder? builder, 12 | }) { 13 | return showGeneralDialog( 14 | context: context, 15 | barrierDismissible: false, 16 | barrierColor: Colors.black54, 17 | transitionDuration: const Duration(milliseconds: 150), 18 | pageBuilder: ( 19 | BuildContext buildContext, 20 | Animation animation, 21 | Animation secondaryAnimation, 22 | ) { 23 | return WillPopScope( 24 | onWillPop: () async => false, 25 | child: SafeArea( 26 | child: Builder(builder: (BuildContext context) { 27 | return _H5PayDialog( 28 | getPaymentArguments: getPaymentArguments, 29 | verifyResult: verifyResult, 30 | builder: builder, 31 | ); 32 | }), 33 | ), 34 | ); 35 | }, 36 | ); 37 | } 38 | 39 | class _H5PayDialog extends StatelessWidget { 40 | _H5PayDialog({ 41 | Key? key, 42 | required this.getPaymentArguments, 43 | this.verifyResult, 44 | WidgetBuilder? builder, 45 | }) : this.builder = builder ?? _buildSimpleDialog, 46 | super(key: key); 47 | 48 | final GetArgumentsCallback getPaymentArguments; 49 | final VerifyResultCallback? verifyResult; 50 | final WidgetBuilder builder; 51 | 52 | @override 53 | Widget build(BuildContext context) { 54 | return H5PayWidget( 55 | getPaymentArguments: getPaymentArguments, 56 | verifyResult: verifyResult, 57 | builder: (context, status, controller) { 58 | switch (status) { 59 | case PaymentStatus.idle: 60 | controller.launch(); 61 | break; 62 | case PaymentStatus.gettingArguments: 63 | case PaymentStatus.launchingUrl: 64 | case PaymentStatus.jumping: 65 | case PaymentStatus.verifying: 66 | break; 67 | case PaymentStatus.getArgumentsFail: 68 | case PaymentStatus.cantLaunchUrl: 69 | case PaymentStatus.launchUrlTimeout: 70 | case PaymentStatus.jumpTimeout: 71 | case PaymentStatus.success: 72 | case PaymentStatus.fail: 73 | // Pop dialog with result 74 | Future.microtask(() { 75 | Navigator.pop(context, status); 76 | }); 77 | break; 78 | } 79 | 80 | return builder(context); 81 | }, 82 | ); 83 | } 84 | 85 | static Widget _buildSimpleDialog(BuildContext context) { 86 | return Center( 87 | child: Container( 88 | width: 80, 89 | height: 80, 90 | decoration: BoxDecoration( 91 | color: const Color(0xFFFFFFFF), 92 | borderRadius: BorderRadius.circular(10), 93 | ), 94 | child: const Center(child: CupertinoActivityIndicator()), 95 | ), 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/h5pay_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | import 'h5pay_channel.dart'; 6 | import 'utils.dart'; 7 | 8 | enum PaymentStatus { 9 | idle, 10 | gettingArguments, 11 | getArgumentsFail, 12 | launchingUrl, 13 | cantLaunchUrl, // Maybe target payment app is not installed 14 | launchUrlTimeout, // Maybe redirecting url is fail 15 | jumping, 16 | jumpTimeout, 17 | verifying, 18 | success, 19 | fail, 20 | } 21 | 22 | class PaymentArguments { 23 | // Payment url (can be http or any other protocol) 24 | final String url; 25 | 26 | // If scheme of the 'url' argument is 'http' and this argument is not null, 27 | // then this library will launch the url in a hidden webview, catch the 28 | // redirect url which has the scheme you specified, and finally launch 29 | // the new url by using the system api. 30 | // 31 | // You can leave this argument to null to launch the url by using 32 | // the system api directly. 33 | final List? redirectSchemes; 34 | 35 | // Additional http headers for webview 36 | final Map? httpHeaders; 37 | 38 | // Timeout duration of jumping to payment app (or system browser) 39 | final Duration jumpTimeout; 40 | 41 | // Timeout duration of launching url 42 | final Duration launchUrlTimeout; 43 | 44 | PaymentArguments({ 45 | required this.url, 46 | this.redirectSchemes, 47 | this.httpHeaders, 48 | Duration? jumpTimeout, 49 | Duration? launchUrlTimeout, 50 | }) : assert(url.isNotEmpty), 51 | assert(jumpTimeout == null || !jumpTimeout.isNegative), 52 | assert(launchUrlTimeout == null || !launchUrlTimeout.isNegative), 53 | this.jumpTimeout = jumpTimeout ?? const Duration(seconds: 3), 54 | this.launchUrlTimeout = launchUrlTimeout ?? const Duration(seconds: 5); 55 | } 56 | 57 | typedef Future GetArgumentsCallback(); 58 | typedef Future VerifyResultCallback(); 59 | typedef Widget H5PayWidgetBuilder( 60 | BuildContext context, 61 | PaymentStatus status, 62 | H5PayController controller, 63 | ); 64 | 65 | class H5PayController { 66 | final _launchNotifier = SimpleChangeNotifier(); 67 | 68 | void launch() { 69 | _launchNotifier.notify(); 70 | } 71 | 72 | void _dispose() { 73 | _launchNotifier.dispose(); 74 | } 75 | } 76 | 77 | class H5PayWidget extends StatefulWidget { 78 | const H5PayWidget({ 79 | Key? key, 80 | required this.getPaymentArguments, 81 | required this.builder, 82 | this.verifyResult, 83 | }) : super(key: key); 84 | 85 | final GetArgumentsCallback getPaymentArguments; 86 | final VerifyResultCallback? verifyResult; 87 | final H5PayWidgetBuilder builder; 88 | 89 | @override 90 | _H5PayWidgetState createState() => _H5PayWidgetState(); 91 | } 92 | 93 | class _H5PayWidgetState extends State with WidgetsBindingObserver { 94 | static const _checkJumpPeriod = Duration(milliseconds: 100); 95 | final _controller = H5PayController(); 96 | 97 | PaymentStatus _status = PaymentStatus.idle; 98 | bool _listenLifecycle = false; 99 | bool _jumped = false; 100 | 101 | @override 102 | void initState() { 103 | super.initState(); 104 | WidgetsBinding.instance?.addObserver(this); 105 | _controller._launchNotifier.addListener(() async { 106 | // Start to get payment arguments 107 | _setPaymentStatus(PaymentStatus.gettingArguments); 108 | PaymentArguments args; 109 | try { 110 | args = await widget.getPaymentArguments(); 111 | } catch (_) { 112 | _setPaymentStatus(PaymentStatus.getArgumentsFail); 113 | return; 114 | } 115 | if (!mounted) { 116 | return; 117 | } 118 | 119 | // Start to launch url 120 | _setPaymentStatus(PaymentStatus.launchingUrl); 121 | var failStatus = await _launch(args); 122 | if (!mounted) { 123 | return; 124 | } 125 | if (failStatus != null) { 126 | _setPaymentStatus(failStatus); 127 | return; 128 | } 129 | 130 | // Start to listen app lifecycle 131 | _listenLifecycle = true; 132 | _jumped = false; 133 | _setPaymentStatus(PaymentStatus.jumping); 134 | 135 | // Check if jump is successful 136 | failStatus = await _checkJump(args); 137 | if (!mounted) { 138 | return; 139 | } 140 | if (failStatus != null) { 141 | // Jump failed 142 | _listenLifecycle = false; 143 | _setPaymentStatus(failStatus); 144 | return; 145 | } 146 | }); 147 | } 148 | 149 | @override 150 | void dispose() { 151 | _controller._dispose(); 152 | WidgetsBinding.instance?.removeObserver(this); 153 | super.dispose(); 154 | } 155 | 156 | @override 157 | void didChangeAppLifecycleState(AppLifecycleState state) async { 158 | if (!_listenLifecycle) { 159 | return; 160 | } 161 | 162 | if (state == AppLifecycleState.inactive) { 163 | // Start to jump to payment app 164 | _jumped = true; 165 | } else if (state == AppLifecycleState.resumed) { 166 | // Resume from payment app 167 | _listenLifecycle = false; 168 | 169 | bool success = true; 170 | // Try to verify the payment result 171 | final verifyResult = widget.verifyResult; 172 | if (verifyResult != null) { 173 | _setPaymentStatus(PaymentStatus.verifying); 174 | try { 175 | success = await verifyResult(); 176 | } catch (_) { 177 | success = false; 178 | } 179 | } 180 | 181 | _setPaymentStatus( 182 | success == true ? PaymentStatus.success : PaymentStatus.fail); 183 | } 184 | } 185 | 186 | Future _launch(PaymentArguments args) async { 187 | final completer = Completer(); 188 | void completeOnce(PaymentStatus? status) { 189 | if (!completer.isCompleted) { 190 | completer.complete(status); 191 | } 192 | } 193 | 194 | final redirectSchemes = args.redirectSchemes; 195 | if (redirectSchemes != null) { 196 | H5PayChannel.launchRedirectUrl( 197 | args.url, 198 | redirectSchemes, 199 | httpHeaders: args.httpHeaders, 200 | ).then((success) { 201 | if (success == true) { 202 | completeOnce(null); 203 | } else { 204 | completeOnce(PaymentStatus.cantLaunchUrl); 205 | } 206 | }).catchError((e) { 207 | debugPrint(e.toString()); 208 | completeOnce(PaymentStatus.fail); 209 | }); 210 | } else { 211 | H5PayChannel.launchUrl(args.url).then((success) { 212 | if (success == true) { 213 | completeOnce(null); 214 | } else { 215 | completeOnce(PaymentStatus.cantLaunchUrl); 216 | } 217 | }).catchError((e) { 218 | debugPrint(e.toString()); 219 | completeOnce(PaymentStatus.fail); 220 | }); 221 | } 222 | 223 | Future.delayed(args.launchUrlTimeout, () { 224 | completeOnce(PaymentStatus.launchUrlTimeout); 225 | }); 226 | 227 | return completer.future; 228 | } 229 | 230 | Future _checkJump(PaymentArguments args) async { 231 | final count = 232 | (args.jumpTimeout.inMilliseconds / _checkJumpPeriod.inMilliseconds) 233 | .ceil(); 234 | 235 | // Cycle check 236 | for (int i = 0; i < count; i++) { 237 | if (_jumped || !mounted) { 238 | return null; 239 | } 240 | await Future.delayed(_checkJumpPeriod); 241 | } 242 | 243 | return PaymentStatus.jumpTimeout; 244 | } 245 | 246 | void _setPaymentStatus(PaymentStatus status) { 247 | _status = status; 248 | if (mounted) { 249 | setState(() {}); 250 | } 251 | } 252 | 253 | @override 254 | Widget build(BuildContext context) { 255 | return widget.builder(context, _status, _controller); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | class SimpleChangeNotifier extends ChangeNotifier { 4 | void notify() { 5 | notifyListeners(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: h5pay 2 | description: An H5/HTML5 payment (such as Alipay, WeChat Pay) plugin for flutter. 3 | version: 1.1.1 4 | homepage: https://github.com/nekocode/h5pay-flutter 5 | 6 | flutter: 7 | plugin: 8 | platforms: 9 | android: 10 | package: cn.nekocode.h5pay 11 | pluginClass: H5payPlugin 12 | ios: 13 | pluginClass: H5payPlugin 14 | 15 | environment: 16 | sdk: ">=2.12.0 <3.0.0" 17 | flutter: ">=1.12.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | dev_dependencies: 24 | flutter_test: 25 | sdk: flutter 26 | 27 | -------------------------------------------------------------------------------- /test/h5pay_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | import 'package:h5pay/h5pay.dart'; 5 | 6 | void main() { 7 | const MethodChannel channel = MethodChannel('h5pay'); 8 | 9 | TestWidgetsFlutterBinding.ensureInitialized(); 10 | 11 | setUp(() { 12 | channel.setMockMethodCallHandler((MethodCall methodCall) async { 13 | expect(methodCall.method, 'launchRedirectUrl'); 14 | await Future.delayed(Duration(milliseconds: 500)); 15 | return true; 16 | }); 17 | }); 18 | 19 | tearDown(() { 20 | channel.setMockMethodCallHandler(null); 21 | }); 22 | 23 | testWidgets('H5PayWidget test', (WidgetTester tester) async { 24 | await tester.pumpWidget( 25 | MaterialApp( 26 | home: Scaffold( 27 | body: H5PayWidget( 28 | getPaymentArguments: () async => Future.delayed( 29 | Duration(milliseconds: 500), 30 | () => PaymentArguments( 31 | url: 'https://baidu.com', 32 | redirectSchemes: ['sms'], 33 | ), 34 | ), 35 | verifyResult: () async => 36 | Future.delayed(Duration(milliseconds: 500), () => true), 37 | builder: (context, status, controller) { 38 | print('Current payment status: ' + status.toString()); 39 | return TextButton( 40 | onPressed: controller.launch, 41 | child: Text(status.toString()), 42 | ); 43 | }, 44 | ), 45 | ), 46 | ), 47 | ); 48 | 49 | Finder findText(PaymentStatus status) => find.text(status.toString()); 50 | void verifyStatus(PaymentStatus status) { 51 | expect(findText(status), findsOneWidget); 52 | } 53 | 54 | Future changeAppLifecycleState(AppLifecycleState state) async { 55 | return ServicesBinding.instance?.defaultBinaryMessenger 56 | .handlePlatformMessage( 57 | 'flutter/lifecycle', 58 | const StringCodec().encodeMessage(state.toString()), 59 | (_) {}, 60 | ); 61 | } 62 | 63 | verifyStatus(PaymentStatus.idle); 64 | await tester.tap(findText(PaymentStatus.idle)); 65 | await tester.pump(); 66 | verifyStatus(PaymentStatus.gettingArguments); 67 | await tester.pump(new Duration(milliseconds: 600)); 68 | verifyStatus(PaymentStatus.launchingUrl); 69 | await tester.pump(new Duration(milliseconds: 600)); 70 | verifyStatus(PaymentStatus.jumping); 71 | 72 | await changeAppLifecycleState(AppLifecycleState.inactive); 73 | await tester.pump(); 74 | await changeAppLifecycleState(AppLifecycleState.resumed); 75 | await tester.pump(); 76 | 77 | verifyStatus(PaymentStatus.verifying); 78 | await tester.pump(new Duration(milliseconds: 600)); 79 | verifyStatus(PaymentStatus.success); 80 | await tester.pump(new Duration(milliseconds: 5000)); 81 | }); 82 | } 83 | --------------------------------------------------------------------------------