1 // 2 // AddToWalletButtonView.swift 3 // stripe-react-native 4 // 5 // Created by Charles Cruzan on 3/28/22. 6 // 7 8 import Foundation 9 import Stripe 10 11 @objc(ABI48_0_0AddToWalletButtonView) 12 class AddToWalletButtonView: UIView { 13 var pushProvisioningContext: STPPushProvisioningContext? = nil 14 var addToWalletButton: PKAddPassButton? = nil 15 16 @objc var testEnv: Bool = false 17 @objc var iOSButtonStyle: NSString? 18 @objc var cardDetails: NSDictionary? 19 @objc var ephemeralKey: NSDictionary? 20 @objc var onCompleteAction: ABI48_0_0RCTDirectEventBlock? 21 22 required init?(coder: NSCoder) { 23 fatalError("init(coder:) has not been implemented") 24 } 25 didSetPropsnull26 override func didSetProps(_ changedProps: [String]!) { 27 if let addToWalletButton = addToWalletButton { 28 addToWalletButton.removeFromSuperview() 29 } 30 31 let style = Mappers.mapToPKAddPassButtonStyle(style: iOSButtonStyle as String?) 32 33 self.addToWalletButton = PKAddPassButton.init(addPassButtonStyle: style) 34 35 if let addToWalletButton = self.addToWalletButton { 36 addToWalletButton.addTarget(self, action: #selector(beginPushProvisioning), for: .touchUpInside) 37 self.addSubview(addToWalletButton) 38 } 39 } 40 beginPushProvisioningnull41 @objc func beginPushProvisioning() { 42 if (!PushProvisioningUtils.canAddPaymentPass(primaryAccountIdentifier: cardDetails?["primaryAccountIdentifier"] as? String ?? "", isTestMode: self.testEnv)) { 43 onCompleteAction!( 44 Errors.createError( 45 ErrorType.Failed, 46 "This app cannot add cards to Apple Pay. For information on requesting the necessary entitlement, see the Card Issuers section at developer.apple.com/apple-pay/." 47 ) as? [AnyHashable : Any] 48 ) 49 return 50 } 51 52 guard let cardHolderName = cardDetails?["name"] as? String else { 53 onCompleteAction!( 54 Errors.createError( 55 ErrorType.Failed, 56 "Missing parameters. `cardDetails.name` must be supplied in the props to <AddToWalletButton />" 57 ) as? [AnyHashable : Any] 58 ) 59 return 60 } 61 62 if (cardHolderName.isEmpty) { 63 onCompleteAction!( 64 Errors.createError( 65 ErrorType.Failed, 66 "`cardDetails.name` is required, but the passed string was empty" 67 ) as? [AnyHashable : Any] 68 ) 69 return 70 } 71 72 let config = STPPushProvisioningContext.requestConfiguration( 73 withName: cardHolderName, 74 description: cardDetails?["description"] as? String, 75 last4: cardDetails?["lastFour"] as? String, 76 brand: Mappers.mapToCardBrand(cardDetails?["brand"] as? String), 77 primaryAccountIdentifier: cardDetails?["primaryAccountIdentifier"] as? String 78 ) 79 80 // We can use STPFakeAddPaymentPassViewController ONLY IN TEST MODE. If STPFakeAddPaymentPassViewController is 81 // used with a live mode card, the flow will fail and show a 'Signing certificate was invalid' error. 82 let controller = { 83 return self.testEnv ? STPFakeAddPaymentPassViewController(requestConfiguration: config, delegate: self) : PKAddPaymentPassViewController(requestConfiguration: config, delegate: self) 84 }() 85 86 let vc = findViewControllerPresenter(from: UIApplication.shared.delegate?.window??.rootViewController ?? UIViewController()) 87 vc.present(controller!, animated: true, completion: nil) 88 } 89 90 override init(frame: CGRect) { 91 super.init(frame: frame) 92 } 93 layoutSubviewsnull94 override func layoutSubviews() { 95 if let addToWalletButton = self.addToWalletButton { 96 addToWalletButton.frame = self.bounds 97 } 98 } 99 } 100 101 102 extension AddToWalletButtonView: PKAddPaymentPassViewControllerDelegate { 103 func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController, generateRequestWithCertificateChain certificates: [Data], nonce: Data, nonceSignature: Data, completionHandler handler: @escaping (PKAddPaymentPassRequest) -> Void) { 104 self.pushProvisioningContext = STPPushProvisioningContext(keyProvider: self) 105 106 self.pushProvisioningContext?.addPaymentPassViewController(controller, generateRequestWithCertificateChain: certificates, nonce: nonce, nonceSignature: nonceSignature, completionHandler: handler); 107 } 108 addPaymentPassViewControllernull109 func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController, didFinishAdding pass: PKPaymentPass?, error: Error?) { 110 if let error = error as NSError? { 111 onCompleteAction!( 112 Errors.createError( 113 error.code == PKAddPaymentPassError.userCancelled.rawValue ? ErrorType.Canceled : ErrorType.Failed, 114 error as NSError? 115 ) as? [AnyHashable : Any] 116 ) 117 } else { 118 onCompleteAction!([ 119 "error": NSNull(), 120 ] as [AnyHashable : Any]) 121 } 122 controller.dismiss(animated: true, completion: nil) 123 } 124 } 125 126 127 extension AddToWalletButtonView: STPIssuingCardEphemeralKeyProvider { createIssuingCardKeynull128 func createIssuingCardKey(withAPIVersion apiVersion: String, completion: @escaping STPJSONResponseCompletionBlock) { 129 if let ephemeralKey = self.ephemeralKey as? [AnyHashable : Any] { 130 completion(ephemeralKey, nil) 131 } else { 132 onCompleteAction!( 133 Errors.createError( 134 ErrorType.Failed, 135 "Missing parameters. `ephemeralKey` must be supplied in the props to <AddToWalletButton />" 136 ) as? [AnyHashable : Any] 137 ) 138 completion(nil, nil) 139 } 140 } 141 } 142