Commit a5d75a7e authored by 汪林玲's avatar 汪林玲

Initial commit

parents
kind: pipeline
name: default
steps:
- name: prepare
image: v7lin/flutter:1.17.3-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
commands:
- flutter packages get
#- name: build_runner
# image: v7lin/flutter:1.17.3-stable
# volumes:
# - name: pub-cache
# path: /opt/flutter/.pub-cache
# commands:
# - flutter pub run build_runner build --delete-conflicting-outputs
#- name: android-check
# image: v7lin/flutter:1.17.3-stable
# volumes:
# - name: pub-cache
# path: /opt/flutter/.pub-cache
# - name: gradle
# path: /root/.gradle
# commands:
# - cd example/android/
# - ./gradlew :alipay_kit:check
# docker run --rm -it -v ${PWD}:/src v7lin/clang:5.0.2-r0 sh -c "clang-format -style=file -i src/Classes/*.h src/Classes/*.m"
#- name: ios-format
# image: v7lin/clang
# commands:
# - cd ios/
# - clang-format -style=file -i src/Classes/*.h src/Classes/*.m
- name: format
image: v7lin/flutter:1.17.3-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
commands:
- flutter format --dry-run --set-exit-if-changed .
- name: analyze
image: v7lin/flutter:1.17.3-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
commands:
- flutter analyze
- name: test
image: v7lin/flutter:1.17.3-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
commands:
- flutter test --coverage
# - cd example/
# - flutter test
- name: proguard
image: v7lin/flutter:1.17.3-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
- name: gradle
path: /root/.gradle
commands:
- cd example/
- flutter build apk
- name: coverage
image: plugins/codecov:2.0.3
settings:
token:
from_secret: CODECOV_TOKEN
files:
- ./coverage/lcov.info
when:
event:
exclude:
- pull_request
- name: publish-check
image: v7lin/flutter:1.17.3-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
commands:
- flutter packages pub publish --dry-run
volumes:
- name: pub-cache
temp: {}
- name: gradle
temp: {}
---
kind: pipeline
name: publish
steps:
- name: restore-cache
image: alpine:3.9.3
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
commands:
- FLUTTER_HOME=/opt/flutter/.pub-cache
- wget -P $FLUTTER_HOME https://raw.githubusercontent.com/v7lin/pub_credentials/master/credentials.json.enc
- name: restore-cache-openssl
image: v7lin/openssl:1.1.1b
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
environment:
ENC_METHOD:
from_secret: ENC_METHOD
ENC_PASSWORD:
from_secret: ENC_PASSWORD
commands:
- FLUTTER_HOME=/opt/flutter/.pub-cache
- openssl enc -d -$ENC_METHOD -k $ENC_PASSWORD -in $FLUTTER_HOME/credentials.json.enc -out $FLUTTER_HOME/credentials.json
- rm $FLUTTER_HOME/credentials.json.enc
- name: publish
image: v7lin/flutter:1.17.3-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
commands:
- echo "y" | flutter packages pub publish
- name: save-cache-openssl
image: v7lin/openssl:1.1.1b
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
environment:
ENC_METHOD:
from_secret: ENC_METHOD
ENC_PASSWORD:
from_secret: ENC_PASSWORD
commands:
- FLUTTER_HOME=/opt/flutter/.pub-cache
- openssl enc -e -$ENC_METHOD -k $ENC_PASSWORD -in $FLUTTER_HOME/credentials.json -out $FLUTTER_HOME/credentials.json.enc
- rm /opt/flutter/.pub-cache/credentials.json
- name: save-cache
image: docker:git
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
environment:
GIT_USER_EMAIL:
from_secret: GIT_USER_EMAIL
GIT_USER_NAME:
from_secret: GIT_USER_NAME
GIT_USER_PASSWORD:
from_secret: GIT_USER_PASSWORD # 密码含'@',用'%40'替换 -> URLEncoder.encode("@","utf-8");
commands:
- FLUTTER_HOME=/opt/flutter/.pub-cache
- git config --global user.email $GIT_USER_EMAIL
- git config --global user.name $GIT_USER_NAME
- git config --global credential.helper store
- git clone -b master https://$GIT_USER_NAME:$GIT_USER_PASSWORD@github.com/v7lin/pub_credentials.git $FLUTTER_HOME/pub_credentials
- rm $FLUTTER_HOME/pub_credentials/credentials.json.enc
- mv $FLUTTER_HOME/credentials.json.enc $FLUTTER_HOME/pub_credentials/credentials.json.enc
- cd $FLUTTER_HOME/pub_credentials
- git commit -am "update credentials by ci/cd tools"
- git push
volumes:
- name: pub-cache
temp: {}
trigger:
status:
- success
event:
- tag
depends_on:
- default
* linguist-language=Dart
\ No newline at end of file
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://v7lin.github.io/docsify/#/navbar/sponsor # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
.DS_Store
.dart_tool/
.packages
.pub/
build/
# custom
.idea/
*.iml
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: b041144f833e05cf463b8887fa12efdec9493488
channel: stable
project_type: plugin
## 2.0.0
* 升级 Android/iOS SDK
## 2.0.0-Android-Only
* 升级 Android
* 移除 iOS 模块
## 1.1.1
* gradle 依赖兼容 4.x.x
## 1.1.0
* 优化
## 1.0.2
* 优化
## 1.0.1
* 优化 Android 依赖
* 修正 #15
## 1.0.0
* 改名 alipay_kit
* 升级 Android/iOS SDK
## 0.2.3
* 删除RSA2对密钥长度的检查
## 0.2.2
* 简化
## 0.2.1
* 修正线程安全
## 0.2.0
* 优化
* 自动化发布
## 0.1.1
* BUG 修复
## 0.1.0
* 规范 library 代码
## 0.0.1
* android/ios alipay
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
# alipay_kit
[![Build Status](https://cloud.drone.io/api/badges/v7lin/alipay_kit/status.svg)](https://cloud.drone.io/v7lin/alipay_kit)
[![Codecov](https://codecov.io/gh/v7lin/alipay_kit/branch/master/graph/badge.svg)](https://codecov.io/gh/v7lin/alipay_kit)
[![GitHub Tag](https://img.shields.io/github/tag/v7lin/alipay_kit.svg)](https://github.com/v7lin/alipay_kit/releases)
[![Pub Package](https://img.shields.io/pub/v/alipay_kit.svg)](https://pub.dartlang.org/packages/alipay_kit)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/v7lin/alipay_kit/blob/master/LICENSE)
flutter版支付宝SDK
## fake 系列 libraries
* [flutter版微信SDK](https://github.com/v7lin/wechat_kit)
* [flutter版腾讯(QQ)SDK](https://github.com/v7lin/tencent_kit)
* [flutter版新浪微博SDK](https://github.com/v7lin/weibo_kit)
* [flutter版支付宝SDK](https://github.com/v7lin/alipay_kit)
* [flutter版walle渠道打包工具](https://github.com/v7lin/walle_kit)
## dart/flutter 私服
* [simple_pub_server](https://github.com/v7lin/simple_pub_server)
## docs
* [蚂蚁金服开放平台](https://openhome.alipay.com/platform/appManage.htm)
* [支付宝支付](https://docs.open.alipay.com/204/105051/)
* [支付宝登录](https://docs.open.alipay.com/218/105329/)
* [应用签名工具](https://opendocs.alipay.com/open/common/104062)
## android
```
# 不需要做任何额外接入工作
# 混淆已打入 Library,随 Library 引用,自动添加到 apk 打包混淆
```
## ios
```
在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“info”标签栏的“URL type“添加“URL scheme”为你所注册的应用程序id
URL Types
alipay: identifier=alipay schemes=${your app scheme name} # schemes 不能为纯数字,推荐:alipay${appId}
```
```
iOS 9系统策略更新,限制了http协议的访问,此外应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。
<key>LSApplicationQueriesSchemes</key>
<array>
<string>alipay</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
```
## flutter
* snapshot
```
dependencies:
alipay_kit:
git:
url: https://github.com/v7lin/alipay_kit.git
```
* release
```
dependencies:
alipay_kit: ^${latestTag}
```
```
dependencies:
# 请不要进行配置 iOS 相关配置,否则 Apple Store 审核时会拒绝
alipay_kit: ^${latestTag}-Android-Only
```
* example
[示例](./example/lib/main.dart)
## Getting Started
This project is a starting point for a Flutter
[plug-in package](https://flutter.dev/developing-packages/),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
group 'io.github.v7lin.alipay_kit'
version '2.0.0'
buildscript {
repositories {
maven{
allowInsecureProtocol = true
url 'http://127.0.0.1:8081/repository/maven-public/'
}
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.4'
}
}
rootProject.allprojects {
repositories {
maven{
allowInsecureProtocol = true
url 'http://127.0.0.1:8081/repository/maven-public/'
}
google()
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
// library 混淆 -> 随 library 引用,自动添加到 apk 打包混淆
consumerProguardFiles 'consumer-rules.pro'
}
lintOptions {
disable 'InvalidPackage'
}
flavorDimensions 'vendor'
productFlavors {
vendor {
dimension 'vendor'
// library 混淆 -> 随 library 引用,自动添加到 apk 打包混淆
consumerProguardFiles 'consumer-vendor-rules.pro'
}
}
}
dependencies {
implementation 'androidx.annotation:annotation:1.0.0'
implementation 'com.qiaomeng:noutdid.alipaysdk:1.0.0@aar'
// v15.8.01
//vendorImplementation(name: 'alipaysdk-15.8.01.210112203525', ext: 'aar')
}
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
rootProject.name = 'alipay_kit'
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.v7lin.alipay_kit">
</manifest>
package io.github.v7lin.alipay_kit;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.alipay.sdk.app.AuthTask;
import com.alipay.sdk.app.PayTask;
import java.lang.ref.WeakReference;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class AlipayKit implements MethodChannel.MethodCallHandler {
//
private static final String METHOD_ISINSTALLED = "isInstalled";
private static final String METHOD_PAY = "pay";
private static final String METHOD_AUTH = "auth";
private static final String METHOD_ONPAYRESP = "onPayResp";
private static final String METHOD_ONAUTHRESP = "onAuthResp";
private static final String ARGUMENT_KEY_ORDERINFO = "orderInfo";
private static final String ARGUMENT_KEY_AUTHINFO = "authInfo";
private static final String ARGUMENT_KEY_ISSHOWLOADING = "isShowLoading";
//
private Context applicationContext;
private Activity activity;
private MethodChannel channel;
public AlipayKit() {
super();
}
public AlipayKit(Context applicationContext, Activity activity) {
this.applicationContext = applicationContext;
this.activity = activity;
}
//
public void setApplicationContext(@Nullable Context applicationContext) {
this.applicationContext = applicationContext;
}
public void setActivity(@Nullable Activity activity) {
this.activity = activity;
}
public void startListening(@NonNull BinaryMessenger messenger) {
channel = new MethodChannel(messenger, "v7lin.github.io/alipay_kit");
channel.setMethodCallHandler(this);
}
public void stopListening() {
channel.setMethodCallHandler(null);
channel = null;
}
// --- MethodCallHandler
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (METHOD_ISINSTALLED.equals(call.method)) {
boolean isInstalled = false;
try {
final PackageManager packageManager = applicationContext.getPackageManager();
PackageInfo info = packageManager.getPackageInfo("com.eg.android.AlipayGphone", PackageManager.GET_SIGNATURES);
isInstalled = info != null;
} catch (PackageManager.NameNotFoundException e) {
}
result.success(isInstalled);
} else if (METHOD_PAY.equals(call.method)) {
final String orderInfo = call.argument(ARGUMENT_KEY_ORDERINFO);
final boolean isShowLoading = call.argument(ARGUMENT_KEY_ISSHOWLOADING);
final WeakReference<Activity> activityRef = new WeakReference<>(activity);
final WeakReference<MethodChannel> channelRef = new WeakReference<>(channel);
new AsyncTask<String, String, Map<String, String>>() {
@Override
protected Map<String, String> doInBackground(String... params) {
Activity activity = activityRef.get();
if (activity != null && !activity.isFinishing()) {
PayTask task = new PayTask(activity);
return task.payV2(orderInfo, isShowLoading);
}
return null;
}
@Override
protected void onPostExecute(Map<String, String> result) {
if (result != null) {
Activity activity = activityRef.get();
MethodChannel channel = channelRef.get();
if (activity != null && !activity.isFinishing() && channel != null) {
channel.invokeMethod(METHOD_ONPAYRESP, result);
}
}
}
}.execute();
result.success(null);
} else if (METHOD_AUTH.equals(call.method)) {
final String authInfo = call.argument(ARGUMENT_KEY_AUTHINFO);
final boolean isShowLoading = call.argument(ARGUMENT_KEY_ISSHOWLOADING);
final WeakReference<Activity> activityRef = new WeakReference<>(activity);
final WeakReference<MethodChannel> channelRef = new WeakReference<>(channel);
new AsyncTask<String, String, Map<String, String>>(){
@Override
protected Map<String, String> doInBackground(String... strings) {
Activity activity = activityRef.get();
if (activity != null && !activity.isFinishing()) {
AuthTask task = new AuthTask(activity);
return task.authV2(authInfo, isShowLoading);
}
return null;
}
@Override
protected void onPostExecute(Map<String, String> result) {
if (result != null) {
Activity activity = activityRef.get();
MethodChannel channel = channelRef.get();
if (activity != null && !activity.isFinishing() && channel != null) {
channel.invokeMethod(METHOD_ONAUTHRESP, result);
}
}
}
}.execute();
result.success(null);
} else {
result.notImplemented();
}
}
}
package io.github.v7lin.alipay_kit;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/**
* AlipayKitPlugin
*/
public class AlipayKitPlugin implements FlutterPlugin, ActivityAware {
// This static function is optional and equivalent to onAttachedToEngine. It supports the old
// pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
// plugin registration via this function while apps migrate to use the new Android APIs
// post-flutter-1.12 via https://flutter.dev/go/android-project-migration.
//
// It is encouraged to share logic between onAttachedToEngine and registerWith to keep
// them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called
// depending on the user's project. onAttachedToEngine or registerWith must both be defined
// in the same class.
public static void registerWith(Registrar registrar) {
AlipayKit alipayKit = new AlipayKit(registrar.context(), registrar.activity());
alipayKit.startListening(registrar.messenger());
}
// --- FlutterPlugin
private final AlipayKit alipayKit;
public AlipayKitPlugin() {
alipayKit = new AlipayKit();
}
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
alipayKit.setApplicationContext(binding.getApplicationContext());
alipayKit.setActivity(null);
alipayKit.startListening(binding.getBinaryMessenger());
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
alipayKit.stopListening();
alipayKit.setActivity(null);
alipayKit.setApplicationContext(null);
}
// --- ActivityAware
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
alipayKit.setActivity(binding.getActivity());
}
@Override
public void onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity();
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
onAttachedToActivity(binding);
}
@Override
public void onDetachedFromActivity() {
alipayKit.setActivity(null);
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.v7lin.alipay_kit">
<queries>
<!-- targetSdkVersion>=30 -> Android 11 软件包可见性 -->
<package android:name="com.eg.android.AlipayGphone" />
</queries>
</manifest>
# 基础样式
BasedOnStyle: LLVM
# 缩进宽度
IndentWidth: 4
# 圆括号的换行方式
BreakBeforeBraces: Attach
# 是否允许循环单行
AllowShortLoopsOnASingleLine: false
# 支持一行的if
AllowShortIfStatementsOnASingleLine: false
# switch的case缩进
IndentCaseLabels: true
# 针对OC的block的缩进宽度
ObjCBlockIndentWidth: 4
# 针对OC,属性名后加空格
ObjCSpaceAfterProperty: true
# 每行字符的长度
ColumnLimit: 0
# 注释对齐
AlignTrailingComments: true
# 括号后加空格
SpaceAfterCStyleCast: false
# 不在小括号里加空格
SpacesInParentheses: false
# 不在中括号里加空格
SpacesInSquareBrackets: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/flutter_export_environment.sh
\ No newline at end of file
#import <Flutter/Flutter.h>
@interface AlipayKitPlugin : NSObject <FlutterPlugin>
@end
#import "AlipayKitPlugin.h"
#import <AlipaySDK/AlipaySDK.h>
@implementation AlipayKitPlugin {
FlutterMethodChannel *_channel;
}
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
FlutterMethodChannel *channel = [FlutterMethodChannel
methodChannelWithName:@"v7lin.github.io/alipay_kit"
binaryMessenger:[registrar messenger]];
AlipayKitPlugin *instance = [[AlipayKitPlugin alloc] initWithChannel:channel];
[registrar addApplicationDelegate:instance];
[registrar addMethodCallDelegate:instance channel:channel];
}
static NSString *const METHOD_ISINSTALLED = @"isInstalled";
static NSString *const METHOD_PAY = @"pay";
static NSString *const METHOD_AUTH = @"auth";
static NSString *const METHOD_ONPAYRESP = @"onPayResp";
static NSString *const METHOD_ONAUTHRESP = @"onAuthResp";
static NSString *const ARGUMENT_KEY_ORDERINFO = @"orderInfo";
static NSString *const ARGUMENT_KEY_AUTHINFO = @"authInfo";
static NSString *const ARGUMENT_KEY_ISSHOWLOADING = @"isShowLoading";
- (instancetype)initWithChannel:(FlutterMethodChannel *)channel {
self = [super init];
if (self) {
_channel = channel;
}
return self;
}
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
if ([METHOD_ISINSTALLED isEqualToString:call.method]) {
BOOL isInstalled = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"alipay:"]];
result([NSNumber numberWithBool:isInstalled]);
} else if ([METHOD_PAY isEqualToString:call.method]) {
NSString *orderInfo = call.arguments[ARGUMENT_KEY_ORDERINFO];
// NSNumber * isShowLoading = call.arguments[ARGUMENT_KEY_ISSHOWLOADING];
NSString *scheme = [self fetchUrlScheme];
[[AlipaySDK defaultService] payOrder:orderInfo
fromScheme:scheme
callback:^(NSDictionary *resultDic) {
[self->_channel invokeMethod:METHOD_ONPAYRESP arguments:resultDic];
}];
result(nil);
} else if ([METHOD_AUTH isEqualToString:call.method]) {
NSString *authInfo = call.arguments[ARGUMENT_KEY_AUTHINFO];
// NSNumber * isShowLoading = call.arguments[ARGUMENT_KEY_ISSHOWLOADING];
NSString *scheme = [self fetchUrlScheme];
[[AlipaySDK defaultService] auth_V2WithInfo:authInfo
fromScheme:scheme
callback:^(NSDictionary *resultDic) {
[self->_channel invokeMethod:METHOD_ONAUTHRESP arguments:resultDic];
}];
result(nil);
} else {
result(FlutterMethodNotImplemented);
}
}
- (NSString *)fetchUrlScheme {
NSDictionary *infoDic = [[NSBundle mainBundle] infoDictionary];
NSArray *types = [infoDic objectForKey:@"CFBundleURLTypes"];
for (NSDictionary *type in types) {
if ([@"alipay" isEqualToString:[type objectForKey:@"CFBundleURLName"]]) {
return [type objectForKey:@"CFBundleURLSchemes"][0];
}
}
return nil;
}
#pragma mark - AppDelegate
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [self handleOpenURL:url];
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
return [self handleOpenURL:url];
}
- (BOOL)handleOpenURL:(NSURL *)url {
if ([url.host isEqualToString:@"safepay"]) {
// 支付跳转支付宝钱包进行支付,处理支付结果
__weak typeof(self) weakSelf = self;
[[AlipaySDK defaultService] processOrderWithPaymentResult:url
standbyCallback:^(NSDictionary *resultDic) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf->_channel invokeMethod:METHOD_ONPAYRESP arguments:resultDic];
}];
// 授权跳转支付宝钱包进行支付,处理支付结果
[[AlipaySDK defaultService] processAuth_V2Result:url
standbyCallback:^(NSDictionary *resultDic) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf->_channel invokeMethod:METHOD_ONAUTHRESP arguments:resultDic];
}];
return YES;
}
return NO;
}
@end
!function(){if(!window.AlipayJSBridge){window.alipayjsbridgeSetTitle=function(e){document.title=e,t("alipayjsbridge://setTitle?title="+encodeURIComponent(e))},window.alipayjsbridgeRefresh=function(){t("alipayjsbridge://onRefresh?")},window.alipayjsbridgeBack=function(){t("alipayjsbridge://onBack?")},window.alipayjsbridgeExit=function(e){t("alipayjsbridge://onExit?bsucc="+e)},window.alipayjsbridgeShowBackButton=function(e){t("alipayjsbridge://showBackButton?bshow="+e)},window.AlipayJSBridge={version:"2.0",addListener:function(e,i){a[e]=i},hasListener:function(e){if(!a[e])return!1;return!0},callListener:function(e,i,n){var t;n&&(t=function(e){var i="";e&&(i=encodeURIComponent(JSON.stringify(e)));var a="func=h5JsFuncCallback&cbId="+n+"&data="+i;o(a)});var r=a[e];r?r(i,t):console.log("AlipayJSBridge: no h5JsFunc ",e+i)},callNativeFunc:function(e,a,t){var r="";t&&(r="cb_"+i+++"_"+(new Date).getTime(),n[r]=t);var d="";a&&(d=encodeURIComponent(JSON.stringify(a)));o("func="+e+"&cbId="+r+"&data="+d)},callBackFromNativeFunc:function(e,i){var a=n[e];a&&(a(i),delete n[i])}};var e,i=1,n={},a={};window.CustomEvent?e=new CustomEvent("alipayjsbridgeready"):(e=document.createEvent("Event")).initEvent("alipayjsbridgeready",!0,!0),document.dispatchEvent(e),setTimeout(function(){if(window.AlipayJSBridgeInitArray){var e=window.AlipayJSBridgeInitArray;delete window.AlipayJSBridgeInitArray;for(var i=0;i<e.length;i++)try{e[i](AlipayJSBridge)}catch(e){setTimeout(function(){throw e})}}},0)}function t(e){window.webkit&&window.webkit.messageHandlers&&window.webkit.messageHandlers.MQPJSBridgeScheme&&window.webkit.messageHandlers.MQPJSBridgeScheme.postMessage&&window.webkit.messageHandlers.MQPJSBridgeScheme.postMessage(e)}function o(e){t("alipayjsbridge://callNativeFunc?"+e)}}();
//
// AFServiceCenter.h
// AFServiceSDK
//
// Created by jiajunchen on 02/01/2018.
// Copyright © 2018 antfin. All rights reserved.
//
#import <Foundation/Foundation.h>
@class AFServiceResponse;
/**
SDK支持的业务枚举值
- AFServiceEInvoice: 电子发票
- AFServiceAuth: 账户授权
*/
typedef NS_ENUM(NSUInteger, AFService) {
AFServiceEInvoice,
AFServiceAuth,
AFServiceDeduct
};
extern NSString * const kAFServiceOptionBizParams; // 钱包服务调用入参
extern NSString * const kAFServiceOptionCallbackScheme; // 业务回跳当前app的scheme
extern NSString * const kAFServiceOptionNotUseLanding; // 不使用支付宝提示下载页做补偿,为true时需要商户自己处理用户未安装支付宝的情况
extern NSString * const kAFServiceBizParamsKeyUrl; // 独立签约入参url
typedef void(^AFServiceResultBlock)(AFServiceResponse *response);
@interface AFServiceCenter : NSObject
/**
调用钱包服务
@param service 业务service, 见AFService枚举值
@param params 参数Dictionary, key值详情参见kAFServiceOptionBizParams、kAFServiceOptionCallbackScheme注释
@param block 业务结果回调的block, block参数是AFServiceResponse类型,业务结果通过result属性获取,如果未用户未安装支付宝并且kAFServiceOptionNotUseLanding未设置为true,会使用H5landing页做补偿,这种情况下不会有block回调结果。
*/
+ (void)callService:(AFService)service
withParams:(NSDictionary *)params
andCompletion:(AFServiceResultBlock)block;
/**
处理钱包服务回跳APP的URL
@param url 回跳URL
@param block 业务结果回掉的block,详情见调用接口入参上的block。注意此接口上的block只有在跳转钱包后,当前APP被系统回收的情况下回跳才生效
*/
+ (void)handleResponseURL:(NSURL *)url
withCompletion:(AFServiceResultBlock)block;
@end
//
// AFServiceResponse.h
// AFServiceSDK
//
// Created by jiajunchen on 08/01/2018.
// Copyright © 2018 antfin. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
钱包服务调用结果状态吗
- AFResSuccess: 默认值,业务调用成功,结果数据参见result字段
- AFResInvalidService: service枚举值错误
- AFResInvalidURL: 钱包回跳URL错误
- AFResRepeatCall: 业务重复调用(3s内)
- AFResOpenURLErr: 跳转失败
*/
typedef NS_ENUM(NSUInteger, AFResCode) {
AFResSuccess = 0,
AFResInvalidService = 100,
AFResInvalidURL,
AFResRepeatCall,
AFResOpenURLErr,
};
@interface AFServiceResponse : NSObject
/**
业务调用状态吗
*/
@property (nonatomic, assign) AFResCode responseCode;
/**
业务结果Dictionary, 内容请参考具体业务方接入文档
*/
@property (readonly) NSDictionary *result;
@end
//
// APAuthInfo.h
// APAuth
//
// Created by antfin on 17-10-24.
// Copyright (c) 2017年 AntFin. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface APayAuthInfo : NSObject
@property(nonatomic, copy)NSString *appID;
@property(nonatomic, copy)NSString *pid;
@property(nonatomic, copy)NSString *redirectUri;
/**
* 初始化AuthInfo
*
* @param appIDStr 应用ID
* @param pidStr 商户ID 可不填
* @param uriStr 授权的应用回调地址 比如:alidemo://auth
*
* @return authinfo实例
*/
- (id)initWithAppID:(NSString *)appIDStr
pid:(NSString *)pidStr
redirectUri:(NSString *)uriStr;
- (NSString *)description;
- (NSString *)wapDescription;
@end
//
// AlipaySDK.h
// AlipaySDK
//
// Created by antfin on 17-10-24.
// Copyright (c) 2017年 AntFin. All rights reserved.
//
////////////////////////////////////////////////////////
///////////////// 支付宝标准版本支付SDK ///////////////////
///////// version:15.8.01 motify:2020.12.14///////////
////////////////////////////////////////////////////////
#import <UIKit/UIKit.h>
#import "APayAuthInfo.h"
#import "AFServiceCenter.h"
#import "AFServiceResponse.h"
typedef void(^CompletionBlock)(NSDictionary *resultDic);
typedef enum {
ALIPAY_TIDFACTOR_IMEI,
ALIPAY_TIDFACTOR_IMSI,
ALIPAY_TIDFACTOR_TID,
ALIPAY_TIDFACTOR_CLIENTKEY,
ALIPAY_TIDFACTOR_VIMEI,
ALIPAY_TIDFACTOR_VIMSI,
ALIPAY_TIDFACTOR_CLIENTID,
ALIPAY_TIDFACTOR_APDID,
ALIPAY_TIDFACTOR_MAX
} AlipayTidFactor;
@interface AlipaySDK : NSObject
/**
* 创建支付单例服务
*
* @return 返回单例对象
*/
+ (AlipaySDK *)defaultService;
/**
* 用于设置SDK使用的window,如果没有自行创建window无需设置此接口
*/
@property (nonatomic, weak) UIWindow *targetWindow;
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////支付宝支付相关接口/////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* 支付接口
*
* @param orderStr 支付订单信息字串
* @param schemeStr 调用支付的app注册在info.plist中的scheme
* @param completionBlock 支付结果回调Block,用于wap支付结果回调
跳转支付宝支付时只有当processOrderWithPaymentResult接口的completionBlock为nil时会使用这个bolock
*/
- (void)payOrder:(NSString *)orderStr
fromScheme:(NSString *)schemeStr
callback:(CompletionBlock)completionBlock;
/**
* 支付接口 v2
*
* @param orderStr 支付订单信息字串
* @param dynamicLaunch 是否使用动态配置策略跳转支付宝支付
* @param schemeStr 调用支付的app注册在info.plist中的scheme
* @param completionBlock 支付结果回调Block,用于wap支付结果回调
跳转支付宝支付时只有当processOrderWithPaymentResult接口的completionBlock为nil时会使用这个bolock
*/
- (void)payOrder:(NSString *)orderStr
dynamicLaunch:(BOOL)dynamicLaunch
fromScheme:(NSString *)schemeStr
callback:(CompletionBlock)completionBlock;
/**
* 处理支付宝app支付后跳回商户app携带的支付结果Url
*
* @param resultUrl 支付宝app返回的支付结果url
* @param completionBlock 支付结果回调 为nil时默认使用支付接口的completionBlock
*/
- (void)processOrderWithPaymentResult:(NSURL *)resultUrl
standbyCallback:(CompletionBlock)completionBlock;
/**
* 获取交易token。
*
* @return 交易token,若无则为空。
*/
- (NSString *)fetchTradeToken;
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////支付宝授权 2.0 相关接口////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* 快登授权2.0
*
* @param infoStr 授权请求信息字串
* @param schemeStr 调用授权的app注册在info.plist中的scheme
* @param completionBlock 授权结果回调,需要调用方在appDelegate中调用processAuth_V2Result:standbyCallback:方法获取授权结果
* 若在授权过程中,调用方应用被系统终止则此block无效(此时会调用'processAuth_V2Result:standbyCallback:'传入的standbyCallback)
*/
- (void)auth_V2WithInfo:(NSString *)infoStr
fromScheme:(NSString *)schemeStr
callback:(CompletionBlock)completionBlock;
/**
* 处理支付宝app授权后跳回商户app携带的授权结果Url
*
* @param resultUrl 支付宝app返回的授权结果url
* @param completionBlock 授权结果回调,用于处理跳转支付宝授权过程中商户APP被系统终止的情况
*/
- (void)processAuth_V2Result:(NSURL *)resultUrl
standbyCallback:(CompletionBlock)completionBlock;
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////支付宝授权 1.0 相关接口////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* 快登授权
* @param authInfo 授权相关信息
* @param completionBlock 授权结果回调,若在授权过程中,调用方应用被系统终止,则此block无效,
需要调用方在appDelegate中调用processAuth_V2Result:standbyCallback:方法获取授权结果
*/
- (void)authWithInfo:(APayAuthInfo *)authInfo
callback:(CompletionBlock)completionBlock;
/**
* 处理支付宝app授权后跳回商户app携带的授权结果Url
*
* @param resultUrl 支付宝app返回的授权结果url
* @param completionBlock 授权结果回调
*/
- (void)processAuthResult:(NSURL *)resultUrl
standbyCallback:(CompletionBlock)completionBlock;
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////支付宝 h5 支付转 native 支付接口////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* 从h5链接中获取订单串并支付接口(自版本15.4.0起,推荐使用该接口)
*
* @param urlStr 拦截的 url string
*
* @return YES为成功获取订单信息并发起支付流程;NO为无法获取订单信息,输入url是普通url
*/
- (BOOL)payInterceptorWithUrl:(NSString *)urlStr
fromScheme:(NSString *)schemeStr
callback:(CompletionBlock)completionBlock;
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////支付宝 tid 相关信息获取接口/////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* 获取当前tid相关信息
*
* @return tid相关信息
*/
- (NSString*)queryTidFactor:(AlipayTidFactor)factor;
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////支付宝支付环境相关信息接口//////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* 是否已经使用过
*
* @return YES为已经使用过,NO反之
*/
- (BOOL)isLogined;
/**
* 获取当前版本号
*
* @return 当前版本字符串
*/
- (NSString *)currentVersion;
/**
* 測試所用,realse包无效
*
* @param url 测试环境
*/
- (void)setUrl:(NSString *)url;
/**
* 支付前主动更新本地配置
*
* @param block 更新请求结果回调
*/
- (void)fetchSdkConfigWithBlock:(void(^)(BOOL success))block;
typedef void(^APLogBlock)(NSString *log);
/**
* 接收AlipaySDK的log信息
*
* @param logBlock 打印log的回调block
*/
+ (void)startLogWithBlock:(APLogBlock)logBlock;
/**
* 停止输出log,会释放logBlock
*
*
*/
+ (void)stopLog;
@end
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint alipay_kit.podspec' to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'alipay_kit'
s.version = '2.0.0'
s.summary = 'A powerful Flutter plugin allowing developers to auth/pay with natvie Android & iOS Alipay SDKs.'
s.description = <<-DESC
A powerful Flutter plugin allowing developers to auth/pay with natvie Android & iOS Alipay SDKs.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
s.platform = :ios, '8.0'
# v15.8.01
s.subspec 'vendor' do |sp|
sp.resources = "Libraries/*.bundle"
sp.vendored_frameworks = 'Libraries/*.framework'
sp.frameworks = 'SystemConfiguration', 'CoreTelephony', 'QuartzCore', 'CoreText', 'CoreGraphics', 'UIKit', 'Foundation', 'CFNetwork', 'CoreMotion', 'WebKit'
sp.libraries = 'c++', 'z'
end
# Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
end
library alipay_kit;
export 'src/alipay.dart';
export 'src/model/alipay_auth_result.dart';
export 'src/model/alipay_resp.dart';
import 'dart:async';
import 'dart:convert';
import 'package:alipay_kit/src/crypto/rsa.dart';
import 'package:alipay_kit/src/model/alipay_resp.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
///
class Alipay {
///
Alipay() {
_channel.setMethodCallHandler(_handleMethod);
}
static const String _METHOD_ISINSTALLED = 'isInstalled';
static const String _METHOD_PAY = 'pay';
static const String _METHOD_AUTH = 'auth';
static const String _METHOD_ONPAYRESP = 'onPayResp';
static const String _METHOD_ONAUTHRESP = 'onAuthResp';
static const String _ARGUMENT_KEY_ORDERINFO = 'orderInfo';
static const String _ARGUMENT_KEY_AUTHINFO = 'authInfo';
static const String _ARGUMENT_KEY_ISSHOWLOADING = 'isShowLoading';
static const String SIGNTYPE_RSA = 'RSA';
static const String SIGNTYPE_RSA2 = 'RSA2';
static const String AUTHTYPE_AUTHACCOUNT = 'AUTHACCOUNT';
static const String AUTHTYPE_LOGIN = 'LOGIN';
final MethodChannel _channel =
const MethodChannel('v7lin.github.io/alipay_kit');
final StreamController<AlipayResp> _payRespStreamController =
StreamController<AlipayResp>.broadcast();
final StreamController<AlipayResp> _authRespStreamController =
StreamController<AlipayResp>.broadcast();
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case _METHOD_ONPAYRESP:
_payRespStreamController.add(AlipayResp.fromJson(
(call.arguments as Map<dynamic, dynamic>).cast<String, dynamic>()));
break;
case _METHOD_ONAUTHRESP:
_authRespStreamController.add(AlipayResp.fromJson(
(call.arguments as Map<dynamic, dynamic>).cast<String, dynamic>()));
break;
}
}
/// 支付
Stream<AlipayResp> payResp() {
return _payRespStreamController.stream;
}
/// 登录
Stream<AlipayResp> authResp() {
return _authRespStreamController.stream;
}
/// 检测支付宝是否已安装 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException]
Future<bool> isInstalled() {
return _channel.invokeMethod<bool>(_METHOD_ISINSTALLED);
}
/// 支付
Future<void> payOrderJson({
@required String orderInfo,
String signType = SIGNTYPE_RSA2,
@required String privateKey,
bool isShowLoading = true,
}) {
assert(orderInfo?.isNotEmpty ?? false);
assert(privateKey?.isNotEmpty ?? false);
return payOrderMap(
orderInfo: json.decode(orderInfo) as Map<String, dynamic>,
signType: signType,
privateKey: privateKey,
isShowLoading: isShowLoading,
);
}
/// 支付
Future<void> payOrderMap({
@required Map<String, dynamic> orderInfo,
String signType = SIGNTYPE_RSA2,
@required String privateKey,
bool isShowLoading = true,
}) {
assert(orderInfo?.isNotEmpty ?? false);
assert(privateKey?.isNotEmpty ?? false);
String charset = orderInfo['charset'] as String;
Encoding encoding =
(charset?.isNotEmpty ?? false) ? Encoding.getByName(charset) : null;
encoding ??= utf8;
Map<String, dynamic> clone = <String, dynamic>{
...orderInfo,
'sign_type': signType,
};
String param = _param(clone, encoding);
String sign = _sign(clone, signType, privateKey);
return payOrderSign(
orderInfo:
'$param&sign=${Uri.encodeQueryComponent(sign, encoding: encoding)}',
isShowLoading: isShowLoading,
);
}
/// 支付 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException]
Future<void> payOrderSign({
@required String orderInfo,
bool isShowLoading = true,
}) {
assert(orderInfo?.isNotEmpty ?? false);
return _channel.invokeMethod<void>(
_METHOD_PAY,
<String, dynamic>{
_ARGUMENT_KEY_ORDERINFO: orderInfo,
_ARGUMENT_KEY_ISSHOWLOADING: isShowLoading,
},
);
}
/// 登录
Future<void> auth({
@required String appId, // 支付宝分配给开发者的应用ID
@required String pid, // 签约的支付宝账号对应的支付宝唯一用户号,以2088开头的16位纯数字组成
@required String targetId, // 商户标识该次用户授权请求的ID,该值在商户端应保持唯一
String authType =
AUTHTYPE_AUTHACCOUNT, // 标识授权类型,取值范围:AUTHACCOUNT 代表授权;LOGIN 代表登录
String signType =
SIGNTYPE_RSA2, // 商户生成签名字符串所使用的签名算法类型,目前支持 RSA2 和 RSA ,推荐使用 RSA2
@required String privateKey,
bool isShowLoading = true,
}) {
assert((appId?.isNotEmpty ?? false) && appId.length <= 16);
assert((pid?.isNotEmpty ?? false) && pid.length <= 16);
assert((targetId?.isNotEmpty ?? false) && targetId.length <= 32);
assert(authType == AUTHTYPE_AUTHACCOUNT || authType == AUTHTYPE_LOGIN);
assert(privateKey?.isNotEmpty ?? false);
Map<String, dynamic> authInfo = <String, dynamic>{
'apiname': 'com.alipay.account.auth',
'method': 'alipay.open.auth.sdk.code.get',
'app_id': appId,
'app_name': 'mc',
'biz_type': 'openservice',
'pid': pid,
'product_id': 'APP_FAST_LOGIN',
'scope': 'kuaijie',
'target_id': targetId,
'auth_type': authType,
};
authInfo['sign_type'] = signType;
Encoding encoding = utf8; // utf-8
String param = _param(authInfo, encoding);
String sign = _sign(authInfo, signType, privateKey);
return authSign(
info: '$param&sign=${Uri.encodeQueryComponent(sign, encoding: encoding)}',
isShowLoading: isShowLoading,
);
}
/// 登录 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException]
Future<void> authSign({
@required String info,
bool isShowLoading = true,
}) {
assert(info != null && info.isNotEmpty);
return _channel.invokeMethod<void>(
_METHOD_AUTH,
<String, dynamic>{
_ARGUMENT_KEY_AUTHINFO: info,
_ARGUMENT_KEY_ISSHOWLOADING: isShowLoading,
},
);
}
String _param(Map<String, dynamic> map, Encoding encoding) {
return map.entries
.map((MapEntry<String, dynamic> e) =>
'${e.key}=${Uri.encodeQueryComponent('${e.value}', encoding: encoding)}')
.join('&');
}
String _sign(Map<String, dynamic> map, String signType, String privateKey) {
// 参数排序
List<String> keys = map.keys.toList();
keys.sort();
String content = keys.map((String e) => '$e=${map[e]}').join('&');
String sign;
switch (signType) {
case SIGNTYPE_RSA:
sign = base64
.encode(RsaSigner.sha1Rsa(privateKey).sign(utf8.encode(content)));
break;
case SIGNTYPE_RSA2:
sign = base64
.encode(RsaSigner.sha256Rsa(privateKey).sign(utf8.encode(content)));
break;
default:
throw UnsupportedError('Alipay sign_type($signType) is not supported!');
}
return sign;
}
}
import 'dart:convert';
import 'dart:typed_data';
import 'package:asn1lib/asn1lib.dart';
import 'package:pointycastle/pointycastle.dart';
import 'package:pointycastle/signers/rsa_signer.dart';
class RsaKeyParser {
RSAPublicKey parsePublic(String key) {
List<String> rows = key.split('\n');
String header = rows.first;
if (header == '-----BEGIN RSA PUBLIC KEY-----') {
return _parsePublic(_parseSequence(rows));
}
if (header == '-----BEGIN PUBLIC KEY-----') {
return _parsePublic(_pkcs8PublicSequence(_parseSequence(rows)));
}
throw UnsupportedError('PEMKey($key) is unsupported');
}
RSAPrivateKey parsePrivate(String key) {
List<String> rows = key.split('\n');
String header = rows.first;
if (header == '-----BEGIN RSA PRIVATE KEY-----') {
return _parsePrivate(_parseSequence(rows));
}
if (header == '-----BEGIN PRIVATE KEY-----') {
return _parsePrivate(_pkcs8PrivateSequence(_parseSequence(rows)));
}
throw UnsupportedError('PEMKey($key) is unsupported');
}
RSAPublicKey _parsePublic(ASN1Sequence sequence) {
BigInt modulus = (sequence.elements[0] as ASN1Integer).valueAsBigInteger;
BigInt exponent = (sequence.elements[1] as ASN1Integer).valueAsBigInteger;
return RSAPublicKey(modulus, exponent);
}
RSAPrivateKey _parsePrivate(ASN1Sequence sequence) {
BigInt modulus = (sequence.elements[1] as ASN1Integer).valueAsBigInteger;
BigInt exponent = (sequence.elements[3] as ASN1Integer).valueAsBigInteger;
BigInt p = (sequence.elements[4] as ASN1Integer).valueAsBigInteger;
BigInt q = (sequence.elements[5] as ASN1Integer).valueAsBigInteger;
return RSAPrivateKey(modulus, exponent, p, q);
}
ASN1Sequence _parseSequence(List<String> rows) {
String keyText = rows
.skipWhile((String row) => row.startsWith('-----BEGIN'))
.takeWhile((String row) => !row.startsWith('-----END'))
.map((String row) => row.trim())
.join('');
Uint8List keyBytes = Uint8List.fromList(base64.decode(keyText));
ASN1Parser asn1Parser = ASN1Parser(keyBytes);
return asn1Parser.nextObject() as ASN1Sequence;
}
ASN1Sequence _pkcs8PublicSequence(ASN1Sequence sequence) {
ASN1Object object = sequence.elements[1];
List<int> bytes = object.valueBytes().sublist(1);
ASN1Parser parser = ASN1Parser(Uint8List.fromList(bytes));
return parser.nextObject() as ASN1Sequence;
}
ASN1Sequence _pkcs8PrivateSequence(ASN1Sequence sequence) {
ASN1Object object = sequence.elements[2];
Uint8List bytes = object.valueBytes();
ASN1Parser parser = ASN1Parser(bytes);
return parser.nextObject() as ASN1Sequence;
}
}
class RsaSigner {
RsaSigner(RSASigner rsaSigner, RSAPrivateKey privateKey)
: _rsaSigner = rsaSigner,
_privateKey = privateKey;
final RSASigner _rsaSigner;
final RSAPrivateKey _privateKey;
List<int> sign(List<int> message) {
_rsaSigner
..reset()
..init(true, PrivateKeyParameter<RSAPrivateKey>(_privateKey));
RSASignature signature =
_rsaSigner.generateSignature(Uint8List.fromList(message));
return signature.bytes;
}
static RsaSigner sha1Rsa(String privateKey) {
return RsaSigner(Signer('SHA-1/RSA') as RSASigner,
RsaKeyParser().parsePrivate(privateKey));
}
static RsaSigner sha256Rsa(String privateKey) {
return RsaSigner(Signer('SHA-256/RSA') as RSASigner,
RsaKeyParser().parsePrivate(privateKey));
}
}
class RsaVerifier {
RsaVerifier(RSASigner rsaSigner, RSAPublicKey publicKey)
: _rsaSigner = rsaSigner,
_publicKey = publicKey;
final RSASigner _rsaSigner;
final RSAPublicKey _publicKey;
bool verify(List<int> message, List<int> signature) {
_rsaSigner
..reset()
..init(false, PublicKeyParameter<RSAPublicKey>(_publicKey));
return _rsaSigner.verifySignature(Uint8List.fromList(message),
RSASignature(Uint8List.fromList(signature)));
}
static RsaVerifier sha1Rsa(String publicKey) {
return RsaVerifier(Signer('SHA-1/RSA') as RSASigner,
RsaKeyParser().parsePublic(publicKey));
}
static RsaVerifier sha256Rsa(String publicKey) {
return RsaVerifier(Signer('SHA-256/RSA') as RSASigner,
RsaKeyParser().parsePublic(publicKey));
}
}
int intFromString(String json) {
return (json?.isNotEmpty ?? false) ? int.parse(json) : null;
}
String intToString(int object) {
return object?.toString();
}
bool boolFromString(String json) {
return json != null ? json == true.toString() : null;
}
String boolToString(bool object) {
return object?.toString();
}
import 'package:alipay_kit/src/json/string_converter.dart';
import 'package:json_annotation/json_annotation.dart';
part 'alipay_auth_result.g.dart';
@JsonSerializable(
explicitToJson: true,
fieldRename: FieldRename.snake,
)
class AlipayAuthResult {
AlipayAuthResult({
this.success,
this.resultCode,
this.authCode,
this.userId,
});
factory AlipayAuthResult.fromJson(Map<String, dynamic> json) =>
_$AlipayAuthResultFromJson(json);
@JsonKey(
fromJson: boolFromString,
toJson: boolToString,
)
final bool success;
/// 200 业务处理成功,会返回authCode
/// 1005 账户已冻结,如有疑问,请联系支付宝技术支持
/// 202 系统异常,请稍后再试或联系支付宝技术支持
@JsonKey(
fromJson: intFromString,
toJson: intToString,
)
final int resultCode;
final String authCode;
final String userId;
Map<String, dynamic> toJson() => _$AlipayAuthResultToJson(this);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'alipay_auth_result.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
AlipayAuthResult _$AlipayAuthResultFromJson(Map<String, dynamic> json) {
return AlipayAuthResult(
success: boolFromString(json['success'] as String),
resultCode: intFromString(json['result_code'] as String),
authCode: json['auth_code'] as String,
userId: json['user_id'] as String,
);
}
Map<String, dynamic> _$AlipayAuthResultToJson(AlipayAuthResult instance) =>
<String, dynamic>{
'success': boolToString(instance.success),
'result_code': intToString(instance.resultCode),
'auth_code': instance.authCode,
'user_id': instance.userId,
};
import 'package:alipay_kit/src/json/string_converter.dart';
import 'package:alipay_kit/src/model/alipay_auth_result.dart';
import 'package:json_annotation/json_annotation.dart';
part 'alipay_resp.g.dart';
@JsonSerializable(
explicitToJson: true,
)
class AlipayResp {
AlipayResp({
this.resultStatus,
this.result,
this.memo,
});
factory AlipayResp.fromJson(Map<String, dynamic> json) =>
_$AlipayRespFromJson(json);
/// 支付状态,参考支付宝的文档https://docs.open.alipay.com/204/105695/
/// 返回码,标识支付状态,含义如下:
/// 9000——订单支付成功 下面的result有值
/// 8000——正在处理中
/// 4000——订单支付失败
/// 5000——重复请求
/// 6001——用户中途取消
/// 6002——网络连接出错
@JsonKey(
fromJson: intFromString,
toJson: intToString,
)
final int resultStatus;
/// 支付后结果
final String result;
final String memo;
AlipayAuthResult parseAuthResult() {
if (resultStatus == 9000) {
if (result != null && result.isNotEmpty) {
Map<String, String> params =
Uri.parse('alipay://alipay?$result').queryParameters;
return AlipayAuthResult.fromJson(params);
}
}
return null;
}
Map<String, dynamic> toJson() => _$AlipayRespToJson(this);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'alipay_resp.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
AlipayResp _$AlipayRespFromJson(Map<String, dynamic> json) {
return AlipayResp(
resultStatus: intFromString(json['resultStatus'] as String),
result: json['result'] as String,
memo: json['memo'] as String,
);
}
Map<String, dynamic> _$AlipayRespToJson(AlipayResp instance) =>
<String, dynamic>{
'resultStatus': intToString(instance.resultStatus),
'result': instance.result,
'memo': instance.memo,
};
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
asn1lib:
dependency: "direct main"
description:
name: asn1lib
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.15"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.15.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
json_annotation:
dependency: "direct main"
description:
name: json_annotation
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.1"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.7.0"
pointycastle:
dependency: "direct main"
description:
name: pointycastle
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.2"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
sdks:
dart: ">=2.12.0 <3.0.0"
flutter: ">=1.10.0"
name: alipay_kit
description: A powerful Flutter plugin allowing developers to auth/pay with natvie Android & iOS Alipay SDKs.
version: 2.0.0
homepage: https://github.com/v7lin/fake_alipay
environment:
sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.10.0"
dependencies:
asn1lib: ^0.5.8
pointycastle: ^1.0.0
flutter:
sdk: flutter
json_annotation: '>=2.0.0 <4.0.0'
# The following section is specific to Flutter.
flutter:
plugin:
platforms:
android:
package: io.github.v7lin.alipay_kit
pluginClass: AlipayKitPlugin
ios:
pluginClass: AlipayKitPlugin
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment