• 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中进行修改

  • 相关阅读:
    各种集群服务
    cdn
    网页请求的完整过程
    html
    ajax异步请求技术
    浅谈前端渲染与后端渲染的区别
    html与php
    Ubuntu安装anaconda3
    win10安装Ubuntu系统
    删除排序数组中的重复项
  • 原文地址:https://www.cnblogs.com/inthecloud/p/mpay_plugin.html
Copyright © 2020-2023  润新知