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()
}