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

2.2.0-nullsafety

parent a5d75a7e
## 2.2.0-nullsafety
* 同 2.2.0-nullsafety-Android-Only
## 2.2.0-nullsafety-Android-Only
* Alipay 单例
## 2.1.0-nullsafety-Android-Only
* 同 2.1.0-nullsafety
## 2.1.0-nullsafety
* nullsafety
* 不再支持 Android embedding v1
## 2.0.0 ## 2.0.0
* 升级 Android/iOS SDK * 升级 Android/iOS SDK
......
# alipay_kit # alipay_kit
[![Build Status](https://cloud.drone.io/api/badges/v7lin/alipay_kit/status.svg)](https://cloud.drone.io/v7lin/alipay_kit) [![Build Status](https://cloud.drone.io/api/badges/rxreader/alipay_kit/status.svg)](https://cloud.drone.io/rxreader/alipay_kit)
[![Codecov](https://codecov.io/gh/v7lin/alipay_kit/branch/master/graph/badge.svg)](https://codecov.io/gh/v7lin/alipay_kit) [![Codecov](https://codecov.io/gh/rxreader/alipay_kit/branch/master/graph/badge.svg)](https://codecov.io/gh/rxreader/alipay_kit)
[![GitHub Tag](https://img.shields.io/github/tag/v7lin/alipay_kit.svg)](https://github.com/v7lin/alipay_kit/releases) [![GitHub Tag](https://img.shields.io/github/tag/rxreader/alipay_kit.svg)](https://github.com/rxreader/alipay_kit/releases)
[![Pub Package](https://img.shields.io/pub/v/alipay_kit.svg)](https://pub.dartlang.org/packages/alipay_kit) [![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) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/rxreader/alipay_kit/blob/master/LICENSE)
flutter版支付宝SDK flutter版支付宝SDK
## fake 系列 libraries ## fake 系列 libraries
* [flutter版微信SDK](https://github.com/v7lin/wechat_kit) * [flutter版微信SDK](https://github.com/rxreader/wechat_kit)
* [flutter版腾讯(QQ)SDK](https://github.com/v7lin/tencent_kit) * [flutter版腾讯(QQ)SDK](https://github.com/rxreader/tencent_kit)
* [flutter版新浪微博SDK](https://github.com/v7lin/weibo_kit) * [flutter版新浪微博SDK](https://github.com/rxreader/weibo_kit)
* [flutter版支付宝SDK](https://github.com/v7lin/alipay_kit) * [flutter版支付宝SDK](https://github.com/rxreader/alipay_kit)
* [flutter版walle渠道打包工具](https://github.com/v7lin/walle_kit) * [flutter版walle渠道打包工具](https://github.com/rxreader/walle_kit)
## dart/flutter 私服 ## dart/flutter 私服
* [simple_pub_server](https://github.com/v7lin/simple_pub_server) * [simple_pub_server](https://github.com/rxreader/simple_pub_server)
## docs ## docs
...@@ -29,6 +29,15 @@ flutter版支付宝SDK ...@@ -29,6 +29,15 @@ flutter版支付宝SDK
## android ## android
```groovy
buildscript {
dependencies {
// Android 11兼容,需升级Gradle到3.5.4/3.6.4/4.x.y
classpath 'com.android.tools.build:gradle:3.5.4'
}
}
```
``` ```
# 不需要做任何额外接入工作 # 不需要做任何额外接入工作
# 混淆已打入 Library,随 Library 引用,自动添加到 apk 打包混淆 # 混淆已打入 Library,随 Library 引用,自动添加到 apk 打包混淆
...@@ -59,13 +68,17 @@ iOS 9系统策略更新,限制了http协议的访问,此外应用需要在 ...@@ -59,13 +68,17 @@ iOS 9系统策略更新,限制了http协议的访问,此外应用需要在
## flutter ## flutter
* break change
* 2.2.0: Alipay 单例
* 2.1.0: nullsafety & 不再支持 Android embedding v1
* snapshot * snapshot
``` ```
dependencies: dependencies:
alipay_kit: alipay_kit:
git: git:
url: https://github.com/v7lin/alipay_kit.git url: https://github.com/rxreader/alipay_kit.git
``` ```
* release * release
......
This diff is collapsed.
group 'io.github.v7lin.alipay_kit' group 'io.github.v7lin.alipay_kit'
version '2.0.0' version '2.2.0'
buildscript { buildscript {
repositories { repositories {
google()
maven{ maven{
allowInsecureProtocol = true allowInsecureProtocol = true
url 'http://127.0.0.1:8081/repository/maven-public/' url 'http://127.0.0.1:8081/repository/maven-public/'
} }
google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.4' classpath 'com.android.tools.build:gradle:4.1.0'
} }
} }
rootProject.allprojects { rootProject.allprojects {
repositories { repositories {
google()
maven{ maven{
allowInsecureProtocol = true allowInsecureProtocol = true
url 'http://127.0.0.1:8081/repository/maven-public/' url 'http://127.0.0.1:8081/repository/maven-public/'
} }
google()
} }
} }
...@@ -56,6 +56,4 @@ android { ...@@ -56,6 +56,4 @@ android {
dependencies { dependencies {
implementation 'androidx.annotation:annotation:1.0.0' implementation 'androidx.annotation:annotation:1.0.0'
implementation 'com.qiaomeng:noutdid.alipaysdk:1.0.0@aar' implementation 'com.qiaomeng:noutdid.alipaysdk:1.0.0@aar'
// v15.8.01
//vendorImplementation(name: 'alipaysdk-15.8.01.210112203525', ext: 'aar')
} }
...@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME ...@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
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; 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.NonNull;
import com.alipay.sdk.app.AuthTask;
import com.alipay.sdk.app.PayTask;
import java.lang.ref.WeakReference;
import java.util.Map;
import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.PluginRegistry.Registrar; import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
/** /**
* AlipayKitPlugin * AlipayKitPlugin
*/ */
public class AlipayKitPlugin implements FlutterPlugin, ActivityAware { public class AlipayKitPlugin implements FlutterPlugin, ActivityAware, MethodCallHandler {
// 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 static final String METHOD_ISINSTALLED = "isInstalled";
private static final String METHOD_PAY = "pay";
private static final String METHOD_AUTH = "auth";
private final AlipayKit alipayKit; private static final String METHOD_ONPAYRESP = "onPayResp";
private static final String METHOD_ONAUTHRESP = "onAuthResp";
public AlipayKitPlugin() { private static final String ARGUMENT_KEY_ORDERINFO = "orderInfo";
alipayKit = new AlipayKit(); private static final String ARGUMENT_KEY_AUTHINFO = "authInfo";
} private static final String ARGUMENT_KEY_ISSHOWLOADING = "isShowLoading";
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private MethodChannel channel;
private Context applicationContext;
private Activity activity;
// --- FlutterPlugin
@Override @Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
alipayKit.setApplicationContext(binding.getApplicationContext()); channel = new MethodChannel(binding.getBinaryMessenger(), "v7lin.github.io/alipay_kit");
alipayKit.setActivity(null); channel.setMethodCallHandler(this);
alipayKit.startListening(binding.getBinaryMessenger()); applicationContext = binding.getApplicationContext();
} }
@Override @Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
alipayKit.stopListening(); channel.setMethodCallHandler(null);
alipayKit.setActivity(null); channel = null;
alipayKit.setApplicationContext(null); applicationContext = null;
} }
// --- ActivityAware // --- ActivityAware
@Override @Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
alipayKit.setActivity(binding.getActivity()); activity = binding.getActivity();
} }
@Override @Override
...@@ -66,6 +82,80 @@ public class AlipayKitPlugin implements FlutterPlugin, ActivityAware { ...@@ -66,6 +82,80 @@ public class AlipayKitPlugin implements FlutterPlugin, ActivityAware {
@Override @Override
public void onDetachedFromActivity() { public void onDetachedFromActivity() {
alipayKit.setActivity(null); activity = null;
}
// --- MethodCallHandler
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull 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 ignore) {
}
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();
}
} }
} }
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# #
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'alipay_kit' s.name = 'alipay_kit'
s.version = '2.0.0' s.version = '2.2.0'
s.summary = 'A powerful Flutter plugin allowing developers to auth/pay with natvie Android & iOS Alipay SDKs.' s.summary = 'A powerful Flutter plugin allowing developers to auth/pay with natvie Android & iOS Alipay SDKs.'
s.description = <<-DESC s.description = <<-DESC
A powerful Flutter plugin allowing developers to auth/pay with natvie Android & iOS Alipay SDKs. A powerful Flutter plugin allowing developers to auth/pay with natvie Android & iOS Alipay SDKs.
......
...@@ -3,15 +3,16 @@ import 'dart:convert'; ...@@ -3,15 +3,16 @@ import 'dart:convert';
import 'package:alipay_kit/src/crypto/rsa.dart'; import 'package:alipay_kit/src/crypto/rsa.dart';
import 'package:alipay_kit/src/model/alipay_resp.dart'; import 'package:alipay_kit/src/model/alipay_resp.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
/// ///
class Alipay { class Alipay {
/// ///
Alipay() { Alipay._();
_channel.setMethodCallHandler(_handleMethod);
} static Alipay get instance => _instance;
static final Alipay _instance = Alipay._();
static const String _METHOD_ISINSTALLED = 'isInstalled'; static const String _METHOD_ISINSTALLED = 'isInstalled';
static const String _METHOD_PAY = 'pay'; static const String _METHOD_PAY = 'pay';
...@@ -30,8 +31,9 @@ class Alipay { ...@@ -30,8 +31,9 @@ class Alipay {
static const String AUTHTYPE_AUTHACCOUNT = 'AUTHACCOUNT'; static const String AUTHTYPE_AUTHACCOUNT = 'AUTHACCOUNT';
static const String AUTHTYPE_LOGIN = 'LOGIN'; static const String AUTHTYPE_LOGIN = 'LOGIN';
final MethodChannel _channel = late final MethodChannel _channel =
const MethodChannel('v7lin.github.io/alipay_kit'); const MethodChannel('v7lin.github.io/alipay_kit')
..setMethodCallHandler(_handleMethod);
final StreamController<AlipayResp> _payRespStreamController = final StreamController<AlipayResp> _payRespStreamController =
StreamController<AlipayResp>.broadcast(); StreamController<AlipayResp>.broadcast();
...@@ -62,19 +64,17 @@ class Alipay { ...@@ -62,19 +64,17 @@ class Alipay {
} }
/// 检测支付宝是否已安装 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException] /// 检测支付宝是否已安装 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException]
Future<bool> isInstalled() { Future<bool> isInstalled() async {
return _channel.invokeMethod<bool>(_METHOD_ISINSTALLED); return await _channel.invokeMethod<bool?>(_METHOD_ISINSTALLED) ?? false;
} }
/// 支付 /// 支付
Future<void> payOrderJson({ Future<void> payOrderJson({
@required String orderInfo, required String orderInfo,
String signType = SIGNTYPE_RSA2, String signType = SIGNTYPE_RSA2,
@required String privateKey, required String privateKey,
bool isShowLoading = true, bool isShowLoading = true,
}) { }) {
assert(orderInfo?.isNotEmpty ?? false);
assert(privateKey?.isNotEmpty ?? false);
return payOrderMap( return payOrderMap(
orderInfo: json.decode(orderInfo) as Map<String, dynamic>, orderInfo: json.decode(orderInfo) as Map<String, dynamic>,
signType: signType, signType: signType,
...@@ -85,23 +85,19 @@ class Alipay { ...@@ -85,23 +85,19 @@ class Alipay {
/// 支付 /// 支付
Future<void> payOrderMap({ Future<void> payOrderMap({
@required Map<String, dynamic> orderInfo, required Map<String, dynamic> orderInfo,
String signType = SIGNTYPE_RSA2, String signType = SIGNTYPE_RSA2,
@required String privateKey, required String privateKey,
bool isShowLoading = true, bool isShowLoading = true,
}) { }) {
assert(orderInfo?.isNotEmpty ?? false); final String? charset = orderInfo['charset'] as String?;
assert(privateKey?.isNotEmpty ?? false); final Encoding encoding = Encoding.getByName(charset) ?? utf8;
String charset = orderInfo['charset'] as String; final Map<String, dynamic> clone = <String, dynamic>{
Encoding encoding =
(charset?.isNotEmpty ?? false) ? Encoding.getByName(charset) : null;
encoding ??= utf8;
Map<String, dynamic> clone = <String, dynamic>{
...orderInfo, ...orderInfo,
'sign_type': signType, 'sign_type': signType,
}; };
String param = _param(clone, encoding); final String param = _param(clone, encoding);
String sign = _sign(clone, signType, privateKey); final String sign = _sign(clone, signType, privateKey);
return payOrderSign( return payOrderSign(
orderInfo: orderInfo:
'$param&sign=${Uri.encodeQueryComponent(sign, encoding: encoding)}', '$param&sign=${Uri.encodeQueryComponent(sign, encoding: encoding)}',
...@@ -111,10 +107,9 @@ class Alipay { ...@@ -111,10 +107,9 @@ class Alipay {
/// 支付 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException] /// 支付 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException]
Future<void> payOrderSign({ Future<void> payOrderSign({
@required String orderInfo, required String orderInfo,
bool isShowLoading = true, bool isShowLoading = true,
}) { }) {
assert(orderInfo?.isNotEmpty ?? false);
return _channel.invokeMethod<void>( return _channel.invokeMethod<void>(
_METHOD_PAY, _METHOD_PAY,
<String, dynamic>{ <String, dynamic>{
...@@ -126,22 +121,18 @@ class Alipay { ...@@ -126,22 +121,18 @@ class Alipay {
/// 登录 /// 登录
Future<void> auth({ Future<void> auth({
@required String appId, // 支付宝分配给开发者的应用ID required String appId, // 支付宝分配给开发者的应用ID
@required String pid, // 签约的支付宝账号对应的支付宝唯一用户号,以2088开头的16位纯数字组成 required String pid, // 签约的支付宝账号对应的支付宝唯一用户号,以2088开头的16位纯数字组成
@required String targetId, // 商户标识该次用户授权请求的ID,该值在商户端应保持唯一 required String targetId, // 商户标识该次用户授权请求的ID,该值在商户端应保持唯一
String authType = String authType =
AUTHTYPE_AUTHACCOUNT, // 标识授权类型,取值范围:AUTHACCOUNT 代表授权;LOGIN 代表登录 AUTHTYPE_AUTHACCOUNT, // 标识授权类型,取值范围:AUTHACCOUNT 代表授权;LOGIN 代表登录
String signType = String signType =
SIGNTYPE_RSA2, // 商户生成签名字符串所使用的签名算法类型,目前支持 RSA2 和 RSA ,推荐使用 RSA2 SIGNTYPE_RSA2, // 商户生成签名字符串所使用的签名算法类型,目前支持 RSA2 和 RSA ,推荐使用 RSA2
@required String privateKey, required String privateKey,
bool isShowLoading = true, 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(authType == AUTHTYPE_AUTHACCOUNT || authType == AUTHTYPE_LOGIN);
assert(privateKey?.isNotEmpty ?? false); final Map<String, dynamic> authInfo = <String, dynamic>{
Map<String, dynamic> authInfo = <String, dynamic>{
'apiname': 'com.alipay.account.auth', 'apiname': 'com.alipay.account.auth',
'method': 'alipay.open.auth.sdk.code.get', 'method': 'alipay.open.auth.sdk.code.get',
'app_id': appId, 'app_id': appId,
...@@ -154,9 +145,9 @@ class Alipay { ...@@ -154,9 +145,9 @@ class Alipay {
'auth_type': authType, 'auth_type': authType,
}; };
authInfo['sign_type'] = signType; authInfo['sign_type'] = signType;
Encoding encoding = utf8; // utf-8 final Encoding encoding = utf8; // utf-8
String param = _param(authInfo, encoding); final String param = _param(authInfo, encoding);
String sign = _sign(authInfo, signType, privateKey); final String sign = _sign(authInfo, signType, privateKey);
return authSign( return authSign(
info: '$param&sign=${Uri.encodeQueryComponent(sign, encoding: encoding)}', info: '$param&sign=${Uri.encodeQueryComponent(sign, encoding: encoding)}',
isShowLoading: isShowLoading, isShowLoading: isShowLoading,
...@@ -165,10 +156,9 @@ class Alipay { ...@@ -165,10 +156,9 @@ class Alipay {
/// 登录 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException] /// 登录 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException]
Future<void> authSign({ Future<void> authSign({
@required String info, required String info,
bool isShowLoading = true, bool isShowLoading = true,
}) { }) {
assert(info != null && info.isNotEmpty);
return _channel.invokeMethod<void>( return _channel.invokeMethod<void>(
_METHOD_AUTH, _METHOD_AUTH,
<String, dynamic>{ <String, dynamic>{
...@@ -187,9 +177,9 @@ class Alipay { ...@@ -187,9 +177,9 @@ class Alipay {
String _sign(Map<String, dynamic> map, String signType, String privateKey) { String _sign(Map<String, dynamic> map, String signType, String privateKey) {
// 参数排序 // 参数排序
List<String> keys = map.keys.toList(); final List<String> keys = map.keys.toList();
keys.sort(); keys.sort();
String content = keys.map((String e) => '$e=${map[e]}').join('&'); final String content = keys.map((String e) => '$e=${map[e]}').join('&');
String sign; String sign;
switch (signType) { switch (signType) {
case SIGNTYPE_RSA: case SIGNTYPE_RSA:
......
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:asn1lib/asn1lib.dart';
import 'package:pointycastle/pointycastle.dart'; import 'package:pointycastle/pointycastle.dart';
import 'package:pointycastle/signers/rsa_signer.dart'; import 'package:pointycastle/signers/rsa_signer.dart';
class RsaKeyParser { class RsaKeyParser {
RSAPublicKey parsePublic(String key) { RSAPublicKey parsePublic(String key) {
List<String> rows = key.split('\n'); final List<String> rows = key.split('\n');
String header = rows.first; final String header = rows.first;
if (header == '-----BEGIN RSA PUBLIC KEY-----') { if (header == '-----BEGIN RSA PUBLIC KEY-----') {
return _parsePublic(_parseSequence(rows)); return _parsePublic(_parseSequence(rows));
} }
...@@ -19,8 +18,8 @@ class RsaKeyParser { ...@@ -19,8 +18,8 @@ class RsaKeyParser {
} }
RSAPrivateKey parsePrivate(String key) { RSAPrivateKey parsePrivate(String key) {
List<String> rows = key.split('\n'); final List<String> rows = key.split('\n');
String header = rows.first; final String header = rows.first;
if (header == '-----BEGIN RSA PRIVATE KEY-----') { if (header == '-----BEGIN RSA PRIVATE KEY-----') {
return _parsePrivate(_parseSequence(rows)); return _parsePrivate(_parseSequence(rows));
} }
...@@ -31,41 +30,41 @@ class RsaKeyParser { ...@@ -31,41 +30,41 @@ class RsaKeyParser {
} }
RSAPublicKey _parsePublic(ASN1Sequence sequence) { RSAPublicKey _parsePublic(ASN1Sequence sequence) {
BigInt modulus = (sequence.elements[0] as ASN1Integer).valueAsBigInteger; final BigInt modulus = (sequence.elements![0] as ASN1Integer).integer!;
BigInt exponent = (sequence.elements[1] as ASN1Integer).valueAsBigInteger; final BigInt exponent = (sequence.elements![1] as ASN1Integer).integer!;
return RSAPublicKey(modulus, exponent); return RSAPublicKey(modulus, exponent);
} }
RSAPrivateKey _parsePrivate(ASN1Sequence sequence) { RSAPrivateKey _parsePrivate(ASN1Sequence sequence) {
BigInt modulus = (sequence.elements[1] as ASN1Integer).valueAsBigInteger; final BigInt modulus = (sequence.elements![1] as ASN1Integer).integer!;
BigInt exponent = (sequence.elements[3] as ASN1Integer).valueAsBigInteger; final BigInt exponent = (sequence.elements![3] as ASN1Integer).integer!;
BigInt p = (sequence.elements[4] as ASN1Integer).valueAsBigInteger; final BigInt? p = (sequence.elements?[4] as ASN1Integer?)?.integer;
BigInt q = (sequence.elements[5] as ASN1Integer).valueAsBigInteger; final BigInt? q = (sequence.elements?[5] as ASN1Integer?)?.integer;
return RSAPrivateKey(modulus, exponent, p, q); return RSAPrivateKey(modulus, exponent, p, q);
} }
ASN1Sequence _parseSequence(List<String> rows) { ASN1Sequence _parseSequence(List<String> rows) {
String keyText = rows final String keyText = rows
.skipWhile((String row) => row.startsWith('-----BEGIN')) .skipWhile((String row) => row.startsWith('-----BEGIN'))
.takeWhile((String row) => !row.startsWith('-----END')) .takeWhile((String row) => !row.startsWith('-----END'))
.map((String row) => row.trim()) .map((String row) => row.trim())
.join(''); .join('');
Uint8List keyBytes = Uint8List.fromList(base64.decode(keyText)); final Uint8List keyBytes = Uint8List.fromList(base64.decode(keyText));
ASN1Parser asn1Parser = ASN1Parser(keyBytes); final ASN1Parser asn1Parser = ASN1Parser(keyBytes);
return asn1Parser.nextObject() as ASN1Sequence; return asn1Parser.nextObject() as ASN1Sequence;
} }
ASN1Sequence _pkcs8PublicSequence(ASN1Sequence sequence) { ASN1Sequence _pkcs8PublicSequence(ASN1Sequence sequence) {
ASN1Object object = sequence.elements[1]; final ASN1Object object = sequence.elements![1];
List<int> bytes = object.valueBytes().sublist(1); final List<int> bytes = object.valueBytes!.sublist(1);
ASN1Parser parser = ASN1Parser(Uint8List.fromList(bytes)); final ASN1Parser parser = ASN1Parser(Uint8List.fromList(bytes));
return parser.nextObject() as ASN1Sequence; return parser.nextObject() as ASN1Sequence;
} }
ASN1Sequence _pkcs8PrivateSequence(ASN1Sequence sequence) { ASN1Sequence _pkcs8PrivateSequence(ASN1Sequence sequence) {
ASN1Object object = sequence.elements[2]; final ASN1Object object = sequence.elements![2];
Uint8List bytes = object.valueBytes(); final Uint8List bytes = object.valueBytes!;
ASN1Parser parser = ASN1Parser(bytes); final ASN1Parser parser = ASN1Parser(bytes);
return parser.nextObject() as ASN1Sequence; return parser.nextObject() as ASN1Sequence;
} }
} }
...@@ -82,7 +81,7 @@ class RsaSigner { ...@@ -82,7 +81,7 @@ class RsaSigner {
_rsaSigner _rsaSigner
..reset() ..reset()
..init(true, PrivateKeyParameter<RSAPrivateKey>(_privateKey)); ..init(true, PrivateKeyParameter<RSAPrivateKey>(_privateKey));
RSASignature signature = final RSASignature signature =
_rsaSigner.generateSignature(Uint8List.fromList(message)); _rsaSigner.generateSignature(Uint8List.fromList(message));
return signature.bytes; return signature.bytes;
} }
......
import 'package:json_annotation/json_annotation.dart';
class NullableStringToBoolConverter implements JsonConverter<bool, String?> {
const NullableStringToBoolConverter();
@override
bool fromJson(String? json) {
return json == true.toString();
}
@override
String? toJson(bool object) {
return object.toString();
}
}
class NullableStringToNullableIntConverter
implements JsonConverter<int?, String?> {
const NullableStringToNullableIntConverter();
@override
int? fromJson(String? json) {
if (json is String) {
return int.tryParse(json);
}
return null;
}
@override
String? toJson(int? object) {
return object?.toString();
}
}
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:alipay_kit/src/json/jser_converter.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
part 'alipay_auth_result.g.dart'; part 'alipay_auth_result.g.dart';
...@@ -9,7 +9,7 @@ part 'alipay_auth_result.g.dart'; ...@@ -9,7 +9,7 @@ part 'alipay_auth_result.g.dart';
) )
class AlipayAuthResult { class AlipayAuthResult {
AlipayAuthResult({ AlipayAuthResult({
this.success, required this.success,
this.resultCode, this.resultCode,
this.authCode, this.authCode,
this.userId, this.userId,
...@@ -18,23 +18,17 @@ class AlipayAuthResult { ...@@ -18,23 +18,17 @@ class AlipayAuthResult {
factory AlipayAuthResult.fromJson(Map<String, dynamic> json) => factory AlipayAuthResult.fromJson(Map<String, dynamic> json) =>
_$AlipayAuthResultFromJson(json); _$AlipayAuthResultFromJson(json);
@JsonKey( @NullableStringToBoolConverter()
fromJson: boolFromString,
toJson: boolToString,
)
final bool success; final bool success;
/// 200 业务处理成功,会返回authCode /// 200 业务处理成功,会返回authCode
/// 1005 账户已冻结,如有疑问,请联系支付宝技术支持 /// 1005 账户已冻结,如有疑问,请联系支付宝技术支持
/// 202 系统异常,请稍后再试或联系支付宝技术支持 /// 202 系统异常,请稍后再试或联系支付宝技术支持
@JsonKey( @NullableStringToNullableIntConverter()
fromJson: intFromString, final int? resultCode;
toJson: intToString,
)
final int resultCode;
final String authCode; final String? authCode;
final String userId; final String? userId;
Map<String, dynamic> toJson() => _$AlipayAuthResultToJson(this); Map<String, dynamic> toJson() => _$AlipayAuthResultToJson(this);
} }
...@@ -8,17 +8,20 @@ part of 'alipay_auth_result.dart'; ...@@ -8,17 +8,20 @@ part of 'alipay_auth_result.dart';
AlipayAuthResult _$AlipayAuthResultFromJson(Map<String, dynamic> json) { AlipayAuthResult _$AlipayAuthResultFromJson(Map<String, dynamic> json) {
return AlipayAuthResult( return AlipayAuthResult(
success: boolFromString(json['success'] as String), success: const NullableStringToBoolConverter()
resultCode: intFromString(json['result_code'] as String), .fromJson(json['success'] as String?),
authCode: json['auth_code'] as String, resultCode: const NullableStringToNullableIntConverter()
userId: json['user_id'] as String, .fromJson(json['result_code'] as String?),
authCode: json['auth_code'] as String?,
userId: json['user_id'] as String?,
); );
} }
Map<String, dynamic> _$AlipayAuthResultToJson(AlipayAuthResult instance) => Map<String, dynamic> _$AlipayAuthResultToJson(AlipayAuthResult instance) =>
<String, dynamic>{ <String, dynamic>{
'success': boolToString(instance.success), 'success': const NullableStringToBoolConverter().toJson(instance.success),
'result_code': intToString(instance.resultCode), 'result_code': const NullableStringToNullableIntConverter()
.toJson(instance.resultCode),
'auth_code': instance.authCode, 'auth_code': instance.authCode,
'user_id': instance.userId, 'user_id': instance.userId,
}; };
import 'package:alipay_kit/src/json/string_converter.dart'; import 'package:alipay_kit/src/json/jser_converter.dart';
import 'package:alipay_kit/src/model/alipay_auth_result.dart'; import 'package:alipay_kit/src/model/alipay_auth_result.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
...@@ -25,21 +25,22 @@ class AlipayResp { ...@@ -25,21 +25,22 @@ class AlipayResp {
/// 5000——重复请求 /// 5000——重复请求
/// 6001——用户中途取消 /// 6001——用户中途取消
/// 6002——网络连接出错 /// 6002——网络连接出错
@JsonKey( @NullableStringToNullableIntConverter()
fromJson: intFromString, final int? resultStatus;
toJson: intToString,
)
final int resultStatus;
/// 支付后结果 /// 支付后结果
final String result; final String? result;
final String memo; final String? memo;
AlipayAuthResult parseAuthResult() { bool get isSuccessful => resultStatus == 9000;
if (resultStatus == 9000) {
if (result != null && result.isNotEmpty) { bool get isCancelled => resultStatus == 6001;
Map<String, String> params =
AlipayAuthResult? parseAuthResult() {
if (isSuccessful) {
if (result?.isNotEmpty ?? false) {
final Map<String, String> params =
Uri.parse('alipay://alipay?$result').queryParameters; Uri.parse('alipay://alipay?$result').queryParameters;
return AlipayAuthResult.fromJson(params); return AlipayAuthResult.fromJson(params);
} }
......
...@@ -8,15 +8,17 @@ part of 'alipay_resp.dart'; ...@@ -8,15 +8,17 @@ part of 'alipay_resp.dart';
AlipayResp _$AlipayRespFromJson(Map<String, dynamic> json) { AlipayResp _$AlipayRespFromJson(Map<String, dynamic> json) {
return AlipayResp( return AlipayResp(
resultStatus: intFromString(json['resultStatus'] as String), resultStatus: const NullableStringToNullableIntConverter()
result: json['result'] as String, .fromJson(json['resultStatus'] as String?),
memo: json['memo'] as String, result: json['result'] as String?,
memo: json['memo'] as String?,
); );
} }
Map<String, dynamic> _$AlipayRespToJson(AlipayResp instance) => Map<String, dynamic> _$AlipayRespToJson(AlipayResp instance) =>
<String, dynamic>{ <String, dynamic>{
'resultStatus': intToString(instance.resultStatus), 'resultStatus': const NullableStringToNullableIntConverter()
.toJson(instance.resultStatus),
'result': instance.result, 'result': instance.result,
'memo': instance.memo, 'memo': instance.memo,
}; };
This diff is collapsed.
name: alipay_kit name: alipay_kit
description: A powerful Flutter plugin allowing developers to auth/pay with natvie Android & iOS Alipay SDKs. description: A powerful Flutter plugin allowing developers to auth/pay with natvie Android & iOS Alipay SDKs.
version: 2.0.0 version: 2.2.0-nullsafety #-Android-Only
homepage: https://github.com/v7lin/fake_alipay # author: v7lin <v7lin@qq.com>
homepage: https://github.com/rxreader/alipay_kit
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"
flutter: ">=1.10.0" flutter: ">=1.20.0"
dependencies: dependencies:
asn1lib: ^0.5.8
pointycastle: ^1.0.0
flutter: flutter:
sdk: flutter sdk: flutter
json_annotation: '>=2.0.0 <4.0.0' pointycastle: ^3.0.0-nullsafety.2
json_annotation: ^4.0.0
dev_dependencies:
flutter_test:
sdk: flutter
pedantic:
build_runner:
json_serializable:
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter. # The following section is specific to Flutter.
flutter: flutter:
# This section identifies this Flutter project as a plugin project.
# The 'pluginClass' and Android 'package' identifiers should not ordinarily
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin: plugin:
platforms: platforms:
android: android:
......
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