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