morizmartiner
morizmartiner

Reputation: 311

Braintree Drop-In using SwiftUI

I am trying to set up payment with Braintree, but Braintree does not yet support SwiftUI so I have to integrate it with UIKit. I created a wrapper using UIViewControllerRepresentable and I am presenting it as a modal using the sheet function; however, it does not work as expected, it seems it is opening two modals.

The screen when I open the modal:enter image description here

Here's my wrapper:

import SwiftUI
import BraintreeDropIn

struct BTDropInRepresentable: UIViewControllerRepresentable {
    var authorization: String
    var handler: BTDropInControllerHandler

    init(authorization: String, handler: @escaping BTDropInControllerHandler) {
        self.authorization = authorization
        self.handler = handler
    }

    func makeUIViewController(context: Context) -> BTDropInController {
        let bTDropInController = BTDropInController(authorization: authorization, request: BTDropInRequest(), handler: handler)!
        return bTDropInController
    }

    func updateUIViewController(_ uiViewController: BTDropInController, context: UIViewControllerRepresentableContext<BTDropInRepresentable>) {
    }
}

Here is where I am trying to open the modal:

Button(action: {
    self.checkout = true
}) {
    HStack {
        Spacer()
        Text("Checkout")
            .fontWeight(.bold)
            .font(.body)
        Spacer()
    }
    .padding(.vertical, 12)
    .foregroundColor(.white)
    .background(Color.blue)
}.sheet(isPresented: self.$checkout) {
    BTDropInRepresentable(authorization: self.token!, handler:  { (controller, result, error) in
                       if (error != nil) {
                           print("ERROR")
                       } else if (result?.isCancelled == true) {
                           print("CANCELLED")
                       } else if result != nil {
                        print("SUCCESS")
                           // Use the BTDropInResult properties to update your UI
                           // result.paymentOptionType
                           // result.paymentMethod
                           // result.paymentIcon
                           // result.paymentDescription
            }
        controller.dismiss(animated: true, completion: nil)
    })
}

Does anyone have experience with Braintree in SwiftUI or with a similar situation? Am I doing something wrong or forgetting something? I know writing my own views for Braintree checkout is an option, but I'd like to avoid that.

Thanks!

Upvotes: 1

Views: 839

Answers (2)

Gopala Krishnan S
Gopala Krishnan S

Reputation: 241

Refer the code for Braintree Payment

// swiftUI

import SwiftUI
import BraintreeCard
import BraintreeDropIn
import BraintreeApplePay
// View
struct ContentView: View {
    @StateObject var vm = ViewModel()
    @State var methodnoun = ""
    @State var viewController = UIViewControllerRep()
    var body: some View {
        VStack {
            Spacer()
            // Create ClientToken you have to create your server
            Button("create Client") {
                
                //make API call nage hit the server and ClientToken
                vm.fetchClientToken()
            }
            .frame(width: 200, height: 40)
            .background(.blue)
            .foregroundColor(.white)
            
            
            Spacer()
            Button("Open the Braintree PaymentSheet") {
                viewController.showDropIn(clientTokenOrTokenizationKey: vm.clientTolken1)
            }  .frame(width: 300, height: 40)
                .background(.blue)
                .foregroundColor(.white)
            Spacer()
            Spacer()
            
            //loadViewController webView
            viewController
            Spacer()
        }
    }
}

// View Model
class ViewModel: ObservableObject {
    @Published var clientTolken1: String = ""
    
    @Published var title: String = ""
    @Published var messageBody: String = ""
    @Published var showSuccessAlert: Bool = false
    
    func fetchClientToken() {
        // TODO: Switch this URL to your own authenticated API
        let clientTokenURL = NSURL(string: "http://localhost:3000/client_token")!
        let clientTokenRequest = NSMutableURLRequest(url: clientTokenURL as URL)
        clientTokenRequest.setValue("text/plain", forHTTPHeaderField: "Accept")

        URLSession.shared.dataTask(with: clientTokenRequest as URLRequest) { (data, response, error) -> Void in
            // TODO: Handle errors
         
            let clientToken = String(data: data!, encoding: String.Encoding.utf8)
            self.clientTolken1 = clientToken ?? ""

//            self.postNonceToServer(paymentMethodNonce: self.clientTolken1)
//            print(clientToken)
            // As an example, you may wish to present Drop-in at this point.
            // Continue to the next section to learn more...
            }.resume()
    }
    [View Page ][1]
    
    func postNonceToServer(paymentMethodNonce: String) {
        // Update URL with your server
        do {
            let paymentURL = URL(string: "http://localhost:3000/checkout")!
            var request = URLRequest(url: paymentURL)
            request.httpBody = "payment_method_nonce=\(paymentMethodNonce)".data(using: String.Encoding.utf8)
            request.httpMethod = "POST"

            URLSession.shared.dataTask(with: request) { (data, response, error) -> Void in
                // TODO: Handle success or failure
                guard let response = response as? HTTPURLResponse else {
                    return
                }
                
                print("Error Code : \(response.statusCode)")
    //            let clientToken = String(data: data!, encoding: String.Encoding.utf8)
                let json = try? JSONSerialization.jsonObject(with: data ?? Data(), options: [])
                print(json)
                print("---------------")
                let dict = json as? [String: Any]
                print(dict?["transaction"])
                
                if (error != nil) {
                    print("Error : \(error)")
                }
                
                print(error?.localizedDescription)
                
            }.resume()
        } catch {
            print(error.localizedDescription)
        }
       
    }
    
}



// create UIViewControllerRepresentable for integrating UIViewController and Swift

struct UIViewControllerRep: UIViewControllerRepresentable {
    let viewController = VmController()
    func makeUIViewController(context: Context) -> some VmController {

        return viewController
    }
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {

    }

    func showDropIn(clientTokenOrTokenizationKey: String) {
        viewController.showDropIn(clientTokenOrTokenizationKey: clientTokenOrTokenizationKey)
    }
}




// Create ViewController

class VmController: UIViewController {
    func showDropIn(clientTokenOrTokenizationKey: String) {
        let request =  BTDropInRequest()
        print(clientTokenOrTokenizationKey)
        let dropIn = BTDropInController(authorization: clientTokenOrTokenizationKey, request: request)
        { (controller, result, error) in
            if (error != nil) {
                print("ERROR")
            } else if (result?.isCanceled == true) {
                print("CANCELED")
            } else if let result = result {
                print("Result paymentMethodType : \(result.paymentMethodType)")
                print("Result paymentMethod nonce: \(String(describing: result.paymentMethod?.nonce))")
                print("Result paymentIcon   : \(result.paymentIcon)")
                print("Result Description   : \(result.paymentDescription)")
            }
            controller.dismiss(animated: true, completion: nil)
        } ?? BTDropInController()
        self.present(dropIn, animated: true, completion: nil)
    }
}

Braintree WebView get Card details

refer the official link and setup your client-side and server-side setup. https://developer.paypal.com/braintree/docs/start/hello-client/

Upvotes: 0

gotnull
gotnull

Reputation: 27224

Have a look at the error you're receiving and I bet it relates to a custom URL scheme that you'll need to add:

Register a URL type

https://developers.braintreepayments.com/guides/paypal/client-side/ios/v4

Also you need to setup your Drop-in payment methods as well, which are all detailed in that guide I linked.

Upvotes: 1

Related Questions