Flutter 澳门通支付插件mpy_plugin


本文导读

能学到什么?

1、flutter和原生之间的通讯

2、澳门通sdk的使用方法

使用的语言

kotlin_version = '1.3.50'
Dart 2.13.4
Flutter 2.2.3

GitHub :https://github.com/InTheClodus/mpy_plugin

创建一个插件项目,当然也可以直接使用as创建

flutter create --org com.lf--template=plugin mpay_plugis

开始之前要先导入几个包,如需下载请前往github中下载  

在android/  下创建 lib

导入这三个包

 

 分别是支付 、 澳门通和微信支付 的包,均可在gihub链接中下载,

在android/build.gradle中做如下配置,

android节点加上这两个声明 省略号是其他自带的,不用管,有这些配置才能添加aar包

android{
....
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
    repositories {
        flatDir {
            dirs 'libs'
        }
    }
}
dependencies节点修改成为如下,本来应该在第三行就可以导入aar包,但是不知道为什么运行之后就提示找不到alipay插件,所以就用了compileOnly 方法导入aar包,
两个网络请求模块是澳门通sdk必须用到的,所以也是需要导入,否则无法运行
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation fileTree(include: ['*.jar'], dir: 'libs')
//    implementation fileTree(dir: 'libs', include: ['*.aar'])
    //    网络模块
    implementation "com.squareup.okhttp3:okhttp:3.12.0"
    implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
    compileOnly fileTree(dir: "../libs", include: ["*.aar"])
}

 

开始编写代码。。。

编辑 mpy_plugin.dart文件,

ConfigType 是配置要初始化的支付方式,开始想法是集成支付宝微信和云闪付这些,不过mpay已经自带支付宝和微信支付了,所以暂时不弄这俩

在flutter插件项目里MethodChannel是最重要的一个方法,它负责进行通讯,需要在Flutter端和Native端两边做如下操作。

1.Flutter 端

static const MethodChannel _flutterPay = const MethodChannel('mpy_plugin');

2.Android端

MethodChannel(flutterPluginBinding.binaryMessenger, "mpy_plugin")

不过这些操作项目创建的时候就已经自动生成,所以不用管它,但是在Native和Flutter端注册的MethodChannel中的字符串值必须一样

接下来看代码,现在有三个方法,其中platformVersion是创建时自带的案例,不用管,它只是获取当前系统版本

init方法是初始化插件时调用,pay是支付时调用,等会会在使用案例写到怎么用

class ConfigType{
  static String mPay = "config.mpay";
}
class MpyPlugin {
  static const MethodChannel _flutterPay = const MethodChannel('mpy_plugin');

  static Future<String?> get platformVersion async {
    final String? version = await _flutterPay.invokeMethod('getPlatformVersion');
    return version;
  }

  static Future<dynamic> pay({required Map<String,dynamic> params})async{
    return await _flutterPay.invokeMapMethod("pay",params);
  }

  /*
   * 初始化
   */
  static Future<void> init(String type,String methods) async {
    final Map<String, dynamic> params = <String, dynamic>{
      "methods": methods,
    };
    await _flutterPay.invokeListMethod(type, params);
  }
}

右键项目,打开Android目录

 

 

 

接着创建一个支付适配器接口

interface PaymentAdapter<T> {
    val method: String
    fun pay(params: MethodCall, result: MethodChannel.Result)
    fun handleResult(result: T)
}

一共三个接口,前面说了mpay的sdk集成了多个支付方式,所以定义一个 method 表示使用什么方式支付,pay 是支付的参数信息,handleResult 是支付返回值

 

然后还需要创建一个支付通道接口 channel暂时没用到,methods 还是和上面一样是支付信息,支付信息是在flutter端向服务器发起请求返回的数据,通过这个数据去进行支付操作

在pay接口中,我们需要判断一下适配器是否为空,避免不必要的错误,直接将错误信息返回给flutter端

interface PaymentChannel {

    val channel: String;

    fun methods(): HashMap<String, PaymentAdapter<PayResult>>;

    fun pay(method: String, params: MethodCall, result: MethodChannel.Result) {
        val adapter = methods()[method];

        if (adapter == null) {
            result.error("500", "适配器是空的", params);
        } else {
            adapter.pay(params, result);
        }

    }
}

新建一个 MPay的包,在MPay包下创建MPay.kt

 

定义MPay传入参数,并继承PaymentChannel,和澳门通SDk的OpenSdkInterfaces,按住alt+enter,实现全部方法,代码如下

class MPay(params: Map<String, String>, val mActivity: Activity): PaymentChannel,
    OpenSdkInterfaces {
    override val channel: String
        get() = TODO("Not yet implemented")

    override fun methods(): HashMap<String, PaymentAdapter<PayResult>> {
        TODO("Not yet implemented")
    }

    override fun OpenSDKInterfaces(p0: PayResult?) {
        TODO("Not yet implemented")
    }

    override fun AliPayInterfaces(p0: PayResult?) {
        TODO("Not yet implemented")
    }

    override fun MPayInterfaces(p0: PayResult?) {
        TODO("Not yet implemented")
    }

    override fun WeChatPayInterfaces(p0: PayResult?) {
        TODO("Not yet implemented")
    }

}

暂时不实现里面的代码,我们来创建三个文件,三个都要继承支付适配器的方法(PaymentAdapter)

Alipay.kt、WeChat.kt、MPayModel.kt

以Alipay为例子,其他两个也一样

class Alipay(val mPay: MPay):PaymentAdapter<PayResult>{
    override val method: String
        get() = TODO("Not yet implemented")

    override fun pay(params: MethodCall, result: MethodChannel.Result) {
        TODO("Not yet implemented")
    }

    override fun handleResult(result: PayResult) {
        TODO("Not yet implemented")
    }

}

 

回到MPay,继续里面的代码,在注册MPay时需要传入几个参数,用来注册使用的支付方法

    val _methods  = params.getOrElse("methods"){ "" }.split(",").fold(HashMap<String, PaymentAdapter<PayResult>>(), { result, code ->
        when (code) {
            "alipay" -> {
                result[code] = Alipay(this)
                result
            }
            "wechat" -> {
                result[code] = WeChat(this)
                result
            }
            "mpay" -> {
                result[code] = MPayModel(this)
                result
            }
            else -> result
        }
    })

在  mthods 方法中调用这个方法

    override fun methods(): HashMap<String, PaymentAdapter<PayResult>> {
        if (_methods.isEmpty()) return HashMap()
        return _methods
    }

 

然后实现pay方法,在该方法里,依旧要再判断适配器是否初始化,以免出现错误,由于我这里发起网络请求是在flutter端做的,传过来的是请求之后的数据,在返回

数据中有一个是 signData的key,它的value是MPay进行支付时所需的参数,如果这个参数是正常的,就调用 MPay  OpenSdk.newPayAll 方法去进行真正的支付,其实到最后我们发现进行支付只需要这一行代码,之所以要写这么多代码有两个原因

1、为了将数据返回到flutter端,我们将数据返回都是靠 onMethodCall 的result,但是按照sdk 提供的demo,返回数据是在一个handle中,我们无法通过这一个handle去返回数据,所以我们才需要去定义接口,按照我们的方式返回数据

2、代码的整洁

    var result : MethodChannel.Result? = null

    override fun pay(method: String, params: MethodCall, result: MethodChannel.Result) {
        this.result = result
        val adapter = methods()[method]
        if (adapter == null) {
            result.error("error", "适配器未初始化", params)
            return
        }
        val data = params.argument<String>("signData");

        if (data is String) {
            OpenSdk.newPayAll(mActivity, data, this)
            return
        }
        result.error("error", "error", "error")
    }

我们还需要一个接收返回结果的方法,用于接收并返回支付结果,返回9000是支付正常,还有很多的支付异常的code,但是这里只判断成功和失败

    fun handleResult(result : PayResult?) {
        val map = HashMap<String, String>();
        map["message"] = result!!.result;
        if (result.resultStatus == "9000") {
            map["result"] = "Y"
        }else{
            map["result"] = "N"
        }
        this.result?.success(map);
    }

我们继承OpenSdkInterfaces的时候需要实现三个方法,现在来实现具体的实现代码

    override fun AliPayInterfaces(p0: PayResult?) {
        _methods["alipay"]?.handleResult(p0!!)
    }

    override fun MPayInterfaces(p0: PayResult?) {
        _methods["mpay"]?.handleResult(p0!!)
    }

    override fun WeChatPayInterfaces(p0: PayResult?) {
        _methods["wxpay"]?.handleResult(p0!!)
    }

接下来看看完整的MPay代码

class MPay(params: Map<String, String>, val mActivity: Activity): PaymentChannel,
    OpenSdkInterfaces {

    val _methods  = params.getOrElse("methods"){ "" }.split(",").fold(HashMap<String, PaymentAdapter<PayResult>>(), { result, code ->
        when (code) {
            "alipay" -> {
                result[code] = Alipay(this)
                result
            }
            "wechat" -> {
                result[code] = WeChat(this)
                result
            }
            "mpay" -> {
                result[code] = MPayModel(this)
                result
            }
            else -> result
        }
    })

    override val channel = "map"

    var result : MethodChannel.Result? = null

    override fun pay(method: String, params: MethodCall, result: MethodChannel.Result) {
        this.result = result
        val adapter = methods()[method]
        if (adapter == null) {
            result.error("error", "适配器未初始化", params)
            return
        }
        val data = params.argument<String>("signData");

        if (data is String) {
            OpenSdk.newPayAll(mActivity, data, this)
            return
        }
        result.error("error", "error", "error")
    }

    fun handleResult(result : PayResult?) {
        val map = HashMap<String, String>();
        map["message"] = result!!.result;
        if (result.resultStatus == "9000") {
            map["result"] = "Y"
        }else{
            map["result"] = "N"
        }
        this.result?.success(map);
    }

    override fun methods(): HashMap<String, PaymentAdapter<PayResult>> {
        if (_methods.isEmpty()) return HashMap()
        return _methods
    }

    override fun OpenSDKInterfaces(p0: PayResult?) {

    }

    override fun AliPayInterfaces(p0: PayResult?) {
        _methods["alipay"]?.handleResult(p0!!)
    }

    override fun MPayInterfaces(p0: PayResult?) {
        _methods["mpay"]?.handleResult(p0!!)
    }

    override fun WeChatPayInterfaces(p0: PayResult?) {
        _methods["wxpay"]?.handleResult(p0!!)
    }
}
View Code

现在来实现我们开始创建的 Alipay,MPayModel,WeChat那三个文件,只需要实现两个方法即可,下面还是用AliPay做为例子,另外两个只需要修改 method 为wechat,和mpay即可

class Alipay(val mPay: MPay): PaymentAdapter<PayResult> {

    override val method = "alipay";

    override fun pay(params: MethodCall, result: MethodChannel.Result) {

    }

    override fun handleResult(result: PayResult) {
        mPay.handleResult(result);
    }
}

接着进行 MpayPlugin 编写代码,由于我们在MPay中需要使用Activty,所以我们需要在MpayPlugin中得到 activity对象,想要在这里拿到activity,我们就需要去继承 ActivtyAware ,然后实现它的四个方法

在前面声明两个对象,一个是支付通道一个activty,后面会用到

  private val channels = HashMap<String, PaymentChannel>()
  private var mActivity: Activity? = null

继承 ActivtyAware 方法后必须要实现的四个方法,只需要实现一个具体代码就可以了

  /// 绑定 activty
  override fun onAttachedToActivity(binding: ActivityPluginBinding) {
    mActivity = binding.activity
  }

  override fun onDetachedFromActivityForConfigChanges() {
  }

  override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
  }

  override fun onDetachedFromActivity() {
  }

修改 onMethodCall里面的代码,修改成为如下代码,在这里传入的MethodCall对象是map的格式,mthod就是每一次传入的map的key,如果这个key是等于 config.mpay 就去注册支付方式,这里没有写返回数据,如果需要通知flutter注册成功,可以加一句  

return result.success("注册成功")
success 里返回的内容是Any 相当于 java的 object,是可以返回任意类型数据的。
因为目前就写了两个方法,要么是注册要么是支付,所以这里就直接用else了,至于method是哪来的,等会会在调用案例中说到,它就是支付方式,我们会在返回的信息中将它加进去,
判断语句 的意思就是 如果我们注册的支付通道里没有注册我们现在传递过来支付方式,就走默认result 为空,如果支付方式有注册才会调用支付result就是返回值
  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    when (call.method) {
      "config.mpay" -> {
        channels["pay"] = MPay(call.arguments(), mActivity!!)
      }
      else -> {
        val payType = call.argument<String>("method")
        if (channels.containsKey(call.method) && payType != null) {
          channels[call.method]!!.pay(payType, call, result)
          return
        }
        result.notImplemented()
      }
    }
  }

 

再来看看全部代码,解释一下自带的一些方法的意思,

onAttachedToEngine
onMethodCall
onDetachedFromEngine
它们三个都是创建项目的时候就已经存在,其中 onAttachedToEngine 和 onDetachedFromEngine 尽量不要去动,这两个方法是flutter新的加载插件的方式.,既然是新的加载方法,
那自然还有个旧的加载方法,如果需要支持旧写法就加入下列代码,不过,话说回来,flutter2.0出来这么久了,应该不会还有人在用1.几的吧
  companion object {
    private var registrar: PluginRegistry.Registrar? = null
    private var mActivity: Activity? = null
    var context: Context? = null
    private var mMethodChannel: MethodChannel? = null

    @JvmStatic
    fun registerWith(registrar: PluginRegistry.Registrar) {
      val channel = MethodChannel(registrar.messenger(), "mpy_plugin")
      channel.setMethodCallHandler(MpyPlugin())
      context = registrar.context()
      MpyPlugin.registrar = registrar
      mActivity = registrar.activity()
      channel.setMethodCallHandler(MpyPlugin())
    }
  }

全部的代码

class MpyPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {

  private lateinit var channel : MethodChannel

  private val channels = HashMap<String, PaymentChannel>()
  private var mActivity: Activity? = null

  override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
    channel = MethodChannel(flutterPluginBinding.binaryMessenger, "mpy_plugin")
    channel.setMethodCallHandler(this)
  }

  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    when (call.method) {
      "config.mpay" -> {
        channels["pay"] = MPay(call.arguments(), mActivity!!)
      }
      else -> {
        val payType = call.argument<String>("method")
        if (channels.containsKey(call.method) && payType != null) {
          channels[call.method]!!.pay(payType, call, result)
          return
        }
        result.notImplemented()
      }
    }
  }

  override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
    channel.setMethodCallHandler(null)
  }

  /// 绑定 activty
  override fun onAttachedToActivity(binding: ActivityPluginBinding) {
    mActivity = binding.activity
  }

  override fun onDetachedFromActivityForConfigChanges() {
  }

  override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
  }

  override fun onDetachedFromActivity() {
  }
}
View Code

 

example 案例 

api请求参数不一定都是一样的按照自己的需求去修改即可

需要主要两个地方,这就是在MpayPlugin支付判断的method的所在位置,method 必须和请求参数里的  "pay_channel": "mpay" value一致

 

paramss.putIfAbsent("method", () => "mpay");

 

import 'dart:collection';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:mpy_plugin/mpy_plugin.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  void initState() {
    super.initState();
    MpyPlugin.init(ConfigType.mPay, "alipay,wechat,mpay");
  }
  dynamic result;
  Future<void> pays() async {
    Map<String, dynamic> params = <String, dynamic>{
      "org_id": "替换你的id", // 订单ID
      "sign_type": "MD5", // 加密方式
      "sign": "替换你的密钥", /// 密钥
      "pay_channel": "mpay", /// 支付方式
      "total_fee": "0.10", // 金额
      "body": "我的商品1", // 商品名
      "sub_appid": "替换你的appid", // 微信支付appid
      "subject": "替换店铺",// 店铺
      "extend_params": {
        "sub_merchant_name": "儒林教育", // app Name
        "sub_merchant_id": "替换你的编号", //z子商户编号
        "sub_merchant_industry": "替换你的行业", //子商户行业
      },
    };
    Options options = Options(headers: {
      "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
      "Content-Type": "application/json",
      "accept": "*/*",
      "connection": "Keep-Alive"
    });
    Response rep = await Dio().post(
        "替换你的后台api接口",
        data: params,
        options: options);
    print(rep.data);
    HashMap<String, dynamic> paramss = HashMap.from(rep.data);
    paramss.putIfAbsent("method", () => "mpay");
    setState(() async{
      result = await MpyPlugin.pay(params: paramss);
      print("====$result");
      print("----------");
    });

  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child:Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text("$result"),
              TextButton(
                onPressed: pays,
                child: Text("测试"),
              )
            ],
          ),
        ),
      ),
    );
  }
}

 

调用案例返回的数据就是这样,如果需要返回更多参数就在Mpay.kt中进行修改

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM