iOS开发

Create a Shopping App with Apple

2023-11-07  本文已影响0人  _浅墨_

支付逻辑


// Note: The code below was taken from the sample app from https://developer.apple.com/documentation/passkit/apple_pay/offering_apple_pay_in_your_app - shortened and adapted for this application

import Foundation
import PassKit

// Typealias so we don't always need to rewrite the type (Bool) -> Void
typealias PaymentCompletionHandler = (Bool) -> Void

class PaymentHandler: NSObject {
    
    var paymentController: PKPaymentAuthorizationController?
    var paymentSummaryItems = [PKPaymentSummaryItem]()
    var paymentStatus = PKPaymentAuthorizationStatus.failure
    var completionHandler: PaymentCompletionHandler?
    
    static let supportedNetworks: [PKPaymentNetwork] = [
        .visa,
        .masterCard,
    ]
    
    // This applePayStatus function is not used in this app. Use it to check for the ability to make payments using canMakePayments(), and check for available payment cards using canMakePayments(usingNetworks:). You can also display a custom PaymentButton according to the result. See https://developer.apple.com/documentation/passkit/apple_pay/offering_apple_pay_in_your_app under "Add the Apple Pay Button" section
    class func applePayStatus() -> (canMakePayments: Bool, canSetupCards: Bool) {
        return (PKPaymentAuthorizationController.canMakePayments(),
                PKPaymentAuthorizationController.canMakePayments(usingNetworks: supportedNetworks))
    }
    
    // Define the shipping methods (this app only offers delivery) and the delivery dates
    func shippingMethodCalculator() -> [PKShippingMethod] {
        
        let today = Date()
        let calendar = Calendar.current
        
        let shippingStart = calendar.date(byAdding: .day, value: 5, to: today)
        let shippingEnd = calendar.date(byAdding: .day, value: 10, to: today)
        
        if let shippingEnd = shippingEnd, let shippingStart = shippingStart {
            let startComponents = calendar.dateComponents([.calendar, .year, .month, .day], from: shippingStart)
            let endComponents = calendar.dateComponents([.calendar, .year, .month, .day], from: shippingEnd)
            
            let shippingDelivery = PKShippingMethod(label: "Delivery", amount: NSDecimalNumber(string: "0.00"))
            shippingDelivery.dateComponentsRange = PKDateComponentsRange(start: startComponents, end: endComponents)
            shippingDelivery.detail = "Sweaters sent to your address"
            shippingDelivery.identifier = "DELIVERY"
            
            return [shippingDelivery]
        }
        return []
    }
    
    func startPayment(products: [Product], total: Int, completion: @escaping PaymentCompletionHandler) {
        completionHandler = completion
        
        // Iterate over the products array, create a PKPaymentSummaryItem for each and append to the paymentSummaryItems array
        products.forEach { product in
            let item = PKPaymentSummaryItem(label: product.name, amount: NSDecimalNumber(string: "\(product.price).00"), type: .final)
            paymentSummaryItems.append(item)
        }
        
        // Add a PKPaymentSummaryItem for the total to the paymentSummaryItems array
        let total = PKPaymentSummaryItem(label: "Total", amount: NSDecimalNumber(string: "\(total).00"), type: .final)
        paymentSummaryItems.append(total)
        
        // Create a payment request and add all data to it
        let paymentRequest = PKPaymentRequest()
        paymentRequest.paymentSummaryItems = paymentSummaryItems // Set paymentSummaryItems to the paymentRequest
        paymentRequest.merchantIdentifier = "merchant.io.designcode.sweatershopapp"
        paymentRequest.merchantCapabilities = .capability3DS // A security protocol used to authenticate users
        paymentRequest.countryCode = "US"
        paymentRequest.currencyCode = "USD"
        paymentRequest.supportedNetworks = PaymentHandler.supportedNetworks // Types of cards supported
        paymentRequest.shippingType = .delivery
        paymentRequest.shippingMethods = shippingMethodCalculator()
        paymentRequest.requiredShippingContactFields = [.name, .postalAddress]
        
        // Display the payment request in a sheet presentation
        paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
        paymentController?.delegate = self
        paymentController?.present(completion: { (presented: Bool) in
            if presented {
                debugPrint("Presented payment controller")
            } else {
                debugPrint("Failed to present payment controller")
                if let completionHandler = self.completionHandler {
                    completionHandler(false)
                }
            }
        })
    }
}

// Set up PKPaymentAuthorizationControllerDelegate conformance
extension PaymentHandler: PKPaymentAuthorizationControllerDelegate {

    // Handle success and errors related to the payment
    func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {

        let errors = [Error]()
        let status = PKPaymentAuthorizationStatus.success

        self.paymentStatus = status
        completion(PKPaymentAuthorizationResult(status: status, errors: errors))
    }

    func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
        controller.dismiss {
            // The payment sheet doesn't automatically dismiss once it has finished, so dismiss the payment sheet
            DispatchQueue.main.async {
                if self.paymentStatus == .success {
                    if let completionHandler = self.completionHandler {
                        completionHandler(true)
                    }
                } else {
                    if let completionHandler = self.completionHandler {
                        completionHandler(false)
                    }
                }
            }
        }
    }
}

开始付款

当用户点击“使用Apple Pay结账”按钮时,他们将看到Apple Pay的付款表单。

首先,让我们处理 CartManager。在 CartManager类的顶部添加以下变量。我们创建了一个 PaymentHandler 实例,以及一个名为 paymentSuccess 的发布变量,它让我们知道付款是否成功。

// ./CartManager.swift

let paymentHandler = PaymentHandler()
@Published var paymentSuccess = false

CartManager 类底部,创建 pay 方法。

// ./CartManager.swift

func pay() {
    paymentHandler.startPayment(products: products, total: total) { success in
        self.paymentSuccess = success
        self.products = []
        self.total = 0
    }
}

接下来处理UI。

// ./Views/CartView.swift

PaymentButton(action: cartManager.pay)

CartView 中,如果付款成功,我们将显示一条感谢用户购买的消息。否则,我们将显示检查用户购物车中产品数量。

// ./Views/CartView.swift

if cartManager.paymentSuccess {
    Text("感谢您的购买!您很快就会在我们的舒适毛衣中感到温暖!您还将很快收到确认邮件。")
        .padding()
} else {
    // 如果 cartManager.products.count > 0 代码...
}

重置 CartView

一旦用户完成购买并返回到 ContentView,我们希望将 CartManager 中的 paymentSuccess 状态重置为 false。

// ./CartManager.swift

@Published var paymentSuccess = false // 删除 private(set) 部分

CartView 上的 ScrollView 中添加onDisappear 逻辑,以便用户可以进行另一次购买。

// ./Views/CartView.swift

.onDisappear {
    if cartManager.paymentSuccess {
        cartManager.paymentSuccess = false
    }
}

测试整个App

效果图

您可以单击删除图标来从购物车中删除物品,产品列表和价格会立即更新。

当单击 “使用Apple Pay结账” 按钮时,我们会看到一个付款表单。一旦输入了所有必要的信息并付款,我们将听到一个“TING”的声音,表示成功支付了。

然后,Apple Pay表单将被关闭,我们将回到CartView,并收到一条感谢购买的消息。

当返回到ContentView并再次进入CartView时,将看到完成购买后购物车被清空。接下来,可以进行另一次购买。

项目源码:https://github.com/MFiOS/SweaterShopApp

上一篇下一篇

猜你喜欢

热点阅读