Home > other >  Returning data through method channel to flutter from swift delegate function
Returning data through method channel to flutter from swift delegate function

Time:12-21

I have a flutter app, but I decided to natively implement apple pay with swift. I send all the necessary data from flutter to swift to make a payment. Delegate function applePayContext(didCompleteWith) returns payment status ("Success", "Error", "Cancelled") and I want to pass that value back to flutter. However result(status) doesn't work.

How can I achieve this? Need to return status where I say //TODO return result to Flutter

My code:

import Flutter
import Foundation
import PassKit
import StripeApplePay
import UIKit

var itemName = ""
var price = ""
var freetrialid = -1
var deviceid = "-1"
var optionId = 0
var startDate = ""

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, ObservableObject, ApplePayContextDelegate {

  static let backendAPIURL = URL(
    string: "xxxxxxxxxxx")!

  @Published var paymentStatus: STPApplePayContext.PaymentStatus?
  @Published var lastPaymentError: Error?

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller: FlutterViewController = window?.rootViewController as! FlutterViewController

    let channel = FlutterMethodChannel(
      name: "methodChannel", binaryMessenger: controller.binaryMessenger)

    channel.setMethodCallHandler { [unowned self] (methodCall, result) in

      if methodCall.method == "payWithApple" {
        guard let args = methodCall.arguments else {
          return
        }

        let myArgs = args as? [String: [String: Any]?]

        for key in myArgs!.keys {
          print(key)
          //not needed
        }

        for value in myArgs!.values {
          print(value as Any)
          itemName = value!["name"] as! String
          price = value!["price"] as! String
          freetrialid = value!["freetrialid"] as! Int
          deviceid = value!["deviceid"] as! String
          optionId = value!["optionId"] as! Int
          startDate = value!["startDate"] as! String
        }

        pay(itemName: itemName, price: price)

      }
    }

    GeneratedPluginRegistrant.register(with: self)

    return true
  }

  func pay(itemName: String, price: String) {
    print("== PAY ==")
    StripeAPI.defaultPublishableKey = "xxxxxxx"
    // Configure a payment request
    let pr = StripeAPI.paymentRequest(
      withMerchantIdentifier: "xxxxxxxxx", country: "AU", currency: "AUD")

    pr.paymentSummaryItems = [
      PKPaymentSummaryItem(label: itemName, amount: NSDecimalNumber(string: price))
    ]

    // Present the Apple Pay Context:
    let applePayContext = STPApplePayContext(paymentRequest: pr, delegate: self)
    applePayContext?.presentApplePay()

  }

  func applePayContext(
    _ context: STPApplePayContext, didCreatePaymentMethod paymentMethod: StripeAPI.PaymentMethod,
    paymentInformation: PKPayment, completion: @escaping STPIntentClientSecretCompletionBlock
  ) {
    // When the Apple Pay sheet is confirmed, create a PaymentIntent on your backend from the provided PKPayment information.
    getClientSecret { secret in
      if let clientSecret = secret["token"] {
        // Call the completion block with the PaymentIntent's client secret.
        completion(clientSecret as! String, nil)
      } else {
        completion(nil, NSError())
      }
    }
  }

  func applePayContext(
    _ context: STPApplePayContext, didCompleteWith status: STPApplePayContext.PaymentStatus,
    error: Error?
  ) {
    switch status {

    case .success:
      // Payment succeeded, show a receipt view
      print("SUCCESS")
      // TODO return result to Flutter
      break

    case .error:
      // Payment failed, show the error
      print("ERROR")
      // TODO return result to Flutter
      break

    case .userCancellation:
      // User canceled the payment
      print("CANCELL")
      // TODO return result to Flutter
      break

    @unknown default:
      fatalError()
    }
  }

  private func getClientSecret(completion: @escaping ([String: Any]) -> Void) {

    let json: [String: Any] =
      [
        "freetrialid": freetrialid,
        "deviceid": deviceid,
        "optionid": optionId,
        "start_date": startDate,
        "ishiit": false,
        "isflex": true,
        "islime": true,
        "freetrialapp": true,
      ]

    let jsonData = try? JSONSerialization.data(withJSONObject: json)

    let username = "xxxxxxxx"
    let password = "xxxxxxxx"
    let loginString = String(format: "%@:%@", username, password)
    let loginData = loginString.data(using: String.Encoding.utf8)!
    let base64LoginString = loginData.base64EncodedString()

    // create post request
    let url = URL(
      string: "xxxxxxxxx")!  //PUT Your URL
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Accept")
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
    // insert json data to the request
    request.httpBody = jsonData

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
      guard let data = data, error == nil else {
        print(error?.localizedDescription ?? "No data")
        return
      }
      let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
      if let responseJSON = responseJSON as? [String: Any] {
        print(responseJSON)  //Code after Successfull POST Request
        completion(responseJSON)
      }
    }

    task.resume()
  }
}

Usually I would return values under

pay(itemName: itemName, price: price)

However in this case it is not possible

CodePudding user response:

You should save the callback like this:

  ...
  static let backendAPIURL = URL(string: "xxxxxxxxxxx")!
  var callback: FlutterResult?
  ...
  self.callback = result
  pay(itemName: itemName, price: price)
  ...

And then use it on the completion:

switch status {

case .success:
  // Payment succeeded, show a receipt view
  print("SUCCESS")
  // TODO return result to Flutter
  
  self.callback?("SUCCESS")

case .error:
  // Payment failed, show the error
  print("ERROR")
  // TODO return result to Flutter
  self.callback?(FlutterError(code: "400",
                              message: "Error.",
                              details: nil))
  break

case .userCancellation:
  // User canceled the payment
  print("CANCELL")
  // TODO return result to Flutter
  self.callback?("CANCELL")
  break

@unknown default:
  fatalError()
}
  • Related