💳

【Flutter】MethodChannelでSquareアプリを呼び出す

2024/02/09に公開

MethodChannelとは?

FlutterのDart環境からネイティブ(Android / iOS他)のメソッドを呼び出したり、その逆を行うためのAPI。

やりたいこと

  • POSアプリからSquareを開いて決済したい
  • Reader SDKは使いたくない

前提

あらかじめSquare Developer Portalでアプリを作成してください。作成したアプリのProductionの方のアプリIDをメモしてください。

また、作成したFlutterアプリのパッケージ名及びSHA-1のFingerprintをPoint of Sale APIに追加してください。

実装

今回はAndroid端末で動かせるように実装していきます。
コードについては「Square APIで学ぶMethod Channel」を参考にしています。

https://zenn.dev/heyhey1028/scraps/1b4b83d5d66c55

また、openSquareonActivityResultについては公式サンプルを参考にしています。

https://github.com/square/point-of-sale-android-sdk/blob/master/sample-bikeshop/src/main/java/com/example/owensbikes/MainActivity.java

依存パッケージ

Squareのpoint-of-sale-sdkが必要なので、dependenciesに追加します。

build.gradle
dependencies {
    implementation 'com.squareup.sdk:point-of-sale-sdk:2.+'
}

Flutter側

まずは作成するMethodChannelを決めます。

static const squareChannel = MethodChannel('ezpos/square');

次に、作成したMethodChannelからopenSquareを呼び出すようにします。

Future<void> _openSquareReaderPayment() async {
    final arguments = <String, dynamic>{
      'price': 500, //合計金額
      'memo': '商品名やその他メモなど',
    };
    try {
      // 決済の取引IDを取得
      final transactionID =
          await squareChannel.invokeMethod<String?>('openSquare', arguments);
      if (transactionID != null) {
        // 決済完了時の処理
      } else {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Squareでの支払いに失敗しました'),
          ),
        );
      }
    } on PlatformException catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Squareでの支払いに失敗しました: ${e.message}'),
        ),
      );
    }
  }

FlutterからSquareを呼び出す際は、この_openSquareReaderPayment()を使います。

Android(Kotlin)側

android/app/src/main/kotlin/com/.../MainActivity.ktをいじっていきます。

  1. 必要なパッケージのインポート
MainActivity.kt
// MethodChannel用
import androidx.annotation.NonNull
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodCall

// Square用
import com.squareup.sdk.pos.ChargeRequest;
import com.squareup.sdk.pos.CurrencyCode;
import com.squareup.sdk.pos.PosClient;
import com.squareup.sdk.pos.PosSdk;
import com.squareup.sdk.pos.PosApi;
import java.util.concurrent.TimeUnit;
import android.content.Intent;
import android.net.Uri;
import android.app.Activity
  1. posClientResultCHARGE_REQUEST_CODEの定義
MainActivity.kt
private val CHARGE_REQUEST_CODE = 0xCAFE; // 同じであることが確認できればなんでも良い
private var posClient: PosClient? = null;
private var fResult: MethodChannel.Result? = null;
  1. invokeMethodで呼び出されたときの処理
MainActivity.kt
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)

    val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "ezpos/square")
    channel.setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result ->
      if (methodCall.method == "openSquare") {
        fResult = result
        val args = methodCall.arguments as Map<String, Any>
        // 型チェック
        if (args["price"] is Int && args["memo"] is String) {
          val price = args["price"] as Int
          val memo = args["memo"] as String
          // posClientの初期化
          posClient = PosSdk.createClient(this, "SquareのアプリケーションID")
          openSquare(price, memo)
        } else {
          result.error("INVALID_ARGUMENT", "Invalid arguments", null)
        }
      } else {
        result.notImplemented()
      }
    }
  }
  1. Squareを開く
MainActivity.kt
private fun openSquare(price: Int, memo: String) {
    // リクエストの作成(日本円、noteにメモをセット)
    // autoReturnで決済完了後に自動で戻す
    val chargeRequest: ChargeRequest.Builder = ChargeRequest.Builder(price, CurrencyCode.JPY)
      .note(memo)
      .autoReturn(PosApi.AUTO_RETURN_TIMEOUT_MIN_MILLIS, TimeUnit.MILLISECONDS)
    try {
      // インテントの取得
      val chargeIntent = posClient!!.createChargeIntent(chargeRequest.build())
      // 呼び出し
      startActivityForResult(chargeIntent, CHARGE_REQUEST_CODE)
    } catch (e: Exception) {
      fResult!!.error("ACTIVITY_FAILED", e.message, null)
    }
}
  1. Squareから戻ってきたときの処理
MainActivity.kt
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == CHARGE_REQUEST_CODE) {
      // 正しくデータが取得できなかった場合
      if (data == null) {
        fResult!!.error("INVALID_RESULT", "Invalid result", null)
        return
      }
      if (resultCode == Activity.RESULT_OK) {
        // transactionIdを取得して戻す
        val res = posClient!!.parseChargeSuccess(data)
        fResult!!.success(res.clientTransactionId)
      } else {
        val error = posClient!!.parseChargeError(data)
        fResult!!.error("CHARGE_FAILED", "Charge failed", error.debugDescription)
      }
    }
}

動作


支払いボタンを押すとSquareアプリに遷移し、Squareで決済完了するとアプリに戻ってきます。
(今回はテストなので現金で決済していますが、Square Readerも使えるはずです)

終わりに

Square Point of Sale SDK for iOSの実装例はすぐに出てきたのですが、for Androidの実装例はほぼなかったため少し手こずりました。

Discussion