Invalid Address in ApplePay with Stripe

1.2k Views Asked by At

I am using Apple Pay with Stripe and it works fine when there is no shipping is available.

When there is a shipping address selected it always gives invalid address error. (It works with Stripe Sandbox Key)

I am using STKPaymentContext API and follow steps from the below link.

https://stripe.com/docs/mobile/ios/basic

in the configuration, I have written this.

let config = STPPaymentConfiguration.shared()
config.requiredShippingAddressFields = [.postalAddress, .phoneNumber,.name]

Not sure what is wrong here.

Here is how it looks.

enter image description here

HERE IS MY CODE

extension CheckoutTableViewController:STPPaymentContextDelegate{
    
    func paymentContext(_ paymentContext: STPPaymentContext, didUpdateShippingAddress address: STPAddress, completion: @escaping STPShippingMethodsCompletionBlock) {
        
        guard
            let buyerPostalCode = address.postalCode,
            let buyerCountry = address.country,
            let productId = self.productDetailsData?.productId
            else{
            completion(.invalid, nil, nil, nil)
            return
        }
        
        guard let phone = address.phone, phone.count > 0 else {
            completion(.invalid,RatesError.phoneNumberRequired,[],nil)
            return
        }
        
        var shipmentItem:[String:Any] = [:]
        shipmentItem["order_amount"] = self.productCost
        shipmentItem["actual_weight"] = 8
        shipmentItem["height"] = 7
        shipmentItem["width"] = 10
        shipmentItem["length"] = 13
        shipmentItem["currency"] = "USD"
        shipmentItem["destination_postal_code"] = buyerPostalCode
        shipmentItem["destination_country_code"] = buyerCountry
        shipmentItem["product_id"] = productId
        shipmentItem["category"] = "fashion"
      
        enum RatesError:Error,LocalizedError{
            case NoDeliveryOptionsFound
            case phoneNumberRequired
            public var errorDescription: String? {
                switch self {
                case .NoDeliveryOptionsFound:
                    return "No couriers are available at the address.\nPlease try with different address."
                case .phoneNumberRequired:
                    return "Please enter phone number."
                }
            }
        }
        
        fetchShippingOptions(forItem: shipmentItem, completionSuccess: {[weak self] (response) in
            
            guard let self = `self` else {
                return
            }
            
            if
                let responseValue = response as? [String:Any],
                let rates = responseValue["rates"] as? [[String:Any]]{
                self.shippingRates = []
                for rate in rates{
                    if let fullName = rate["courier_display_name"] as? String,
                        let identifier = rate["courier_id"] as? String,
                        let amount = rate["shipment_charge_total"] as? Double,
                        let detail = rate["full_description"] as? String
                    {
                    
                        let method = PKShippingMethod()
                        method.amount = NSDecimalNumber.init(value: amount.currency)
                        method.identifier = identifier
                        method.label = fullName
                        method.detail = detail.replacingOccurrences(of: fullName, with: "")
                        self.shippingRates.append(method)
                    }
                }
                completion(.valid, nil, self.shippingRates, self.shippingRates.first)
            }else{
                completion(.invalid,RatesError.NoDeliveryOptionsFound,[],nil)
            }
        }) { (error) in
            completion(.invalid,error,[],nil)
        }
    }
    
    func paymentContextDidChange(_ paymentContext: STPPaymentContext) { 
        if let paymentOption = paymentContext.selectedPaymentOption {
            self.lblPaymentMethod.text = paymentOption.label
        } else {
            self.lblPaymentMethod.text = "Select Payment"
        }
        if let shippingMethod = paymentContext.selectedShippingMethod {
            if let selectedRate = self.shippingRates.first(where: { (method) -> Bool in
                guard let leftValue = method.identifier, let rightValue = shippingMethod.identifier else{
                    return false
                }
                return leftValue == rightValue
            }){
                self.lblAddress.text = selectedRate.label
                self.shippingCharges = Double(truncating: selectedRate.amount).currency
                self.lblShippingCharges.text = "$\(shippingCharges)"
                self.getStripeFees(forAmount: self.productCost + self.shippingCharges)
            }
        } else {
            self.lblAddress.text = "Select Address"
        }
        
        self.updateTotalCost()
    }
    
    func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
        let alertController = UIAlertController(
            title: "Error",
            message: error.localizedDescription,
            preferredStyle: .alert
        )
        let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: { action in
            // Need to assign to _ because optional binding loses @discardableResult value
            // https://bugs.swift.org/browse/SR-1681
            _ = self.navigationController?.popViewController(animated: true)
        })
        let retry = UIAlertAction(title: "Retry", style: .default, handler: { action in
            self.paymentContext?.retryLoading()
        })
        alertController.addAction(cancel)
        alertController.addAction(retry)
        self.present(alertController, animated: true, completion: nil)
    }
    
    func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPPaymentStatusBlock) {
        self.callPaymentIntentAPI(paymentContext, didCreatePaymentResult: paymentResult, completion: completion)
    }
    
    func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
        OperationQueue.main.addOperation {
            SVProgressHUD.dismiss()
        }
        let title: String
        let message: String
        switch status {
        case .error:
            title = "Error"
            message = error?.localizedDescription ?? ""
            UIAlertController.showAlert(withTitle: title, andMessage: message, andButtonTitles: ["Okay"]) {[weak self] (selectedIndex) in
                OperationQueue.main.addOperation {
                    self?.navigationController?.popViewController(animated: true)
                }
            }
        case .success:
            title = "Success"
            message = "Your purchase was successful!"
            UIAlertController.showAlert(withTitle: title, andMessage: message, andButtonTitles: ["Okay"]) {[weak self] (selectedIndex) in
                OperationQueue.main.addOperation {
                    self?.onPaymentCompletion?()
                    var isControllerFound:Bool = false
                    for controller in self?.navigationController?.viewControllers ?? []{
                        if (controller is ProductDetailsViewController) || (controller is ChatVC){
                            isControllerFound = true
                            self?.navigationController?.popToViewController(controller, animated: true)
                            break
                        }
                    }
                    if !isControllerFound{
                        self?.navigationController?.popViewController(animated: true)
                    }
                }
            }
        case .userCancellation:
            return()
        @unknown default:
            return()
        }
    }
}
1

There are 1 best solutions below

0
On BEST ANSWER

Finally, I found an error.

Apple calls didUpdateShippingAddress method at the time of payment but it doesn't provide all information for security purposes. So in my case phone number validation was causing that error.

So I removed the below code from that method.

    guard let phone = address.phone, phone.count > 0 else {
        completion(.invalid,RatesError.phoneNumberRequired,[],nil)
        return
    }