Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2022 App Services

WWDC22 · 36 min · App Services

What’s new in Wallet and Apple Pay

Discover the latest updates to Wallet & Apple Pay. We’ll show you how to support Orders in Wallet for your apps and websites and securely validate someone’s age and identity with the Identity Verification API. We’ll also explore PassKit support for SwiftUI, and discuss how you how you can improve your Apple Pay experience with Automatic Payments.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 11 snippets

AddPassToWalletButton swift · at 2:39 ↗
@State var addedToWallet: Bool

@ViewBuilder private var airlineButton: some View {
    if let pass = createAirlinePass() {
        AddPassToWalletButton([pass]) { added in
            addedToWallet = added
        }
        .frame(width: 250, height: 50)
        .addPassToWalletButtonStyle(.blackOutline)
    } else {
        // Fallback
    }
}
PayWithApplePayButton swift · at 3:40 ↗
// Create a payment request
let paymentRequest = PKPaymentRequest()
// ...

// Create a payment authorization change method
func authorizationChange(phase: PayWithApplePayButtonPaymentAuthorizationPhase) { ... }

PayWithApplePayButton(
    .plain,
    request: paymentRequest,
    onPaymentAuthorizationChange: authorizationChange
) {
    // Fallback
}
.frame(width: 250, height: 50)
.payWithApplePayButtonStyle(.automatic)
Multi-merchant payments swift · at 6:34 ↗
// Create a payment request
let paymentRequest = PKPaymentRequest()
// ...

// Set total amount
paymentRequest.paymentSummaryItems = [
    PKPaymentSummaryItem(label: "Total", amount: 500)
]

// Create a multi token context for each additional merchant in the payment
let multiTokenContexts = [
    PKPaymentTokenContext(
        merchantIdentifier: "com.example.air-travel",
        externalIdentifier: "com.example.air-travel",
        merchantName: "Air Travel",
        merchantDomain: "air-travel.example.com",
        amount: 150
    ),
    PKPaymentTokenContext(
        merchantIdentifier: "com.example.hotel",
        externalIdentifier: "com.example.hotel",
        merchantName: "Hotel",
        merchantDomain: "hotel.example.com",
        amount: 300
    ),
    PKPaymentTokenContext(
        merchantIdentifier: "com.example.car-rental",
        externalIdentifier: "com.example.car-rental",
        merchantName: "Car Rental",
        merchantDomain: "car-rental.example.com",
        amount: 50
    )
]
paymentRequest.multiTokenContexts = multiTokenContexts
Automatic Payments - Recurring payment request swift · at 10:14 ↗
// Specify the amount and billing periods
let regularBilling = PKRecurringPaymentSummaryItem(label: "Membership", amount: 20)

let trialBilling = PKRecurringPaymentSummaryItem(label: "Trial Membership", amount: 10)

let trialEndDate = Calendar.current.date(byAdding: .month, value: 1, to: Date.now)
trialBilling.endDate = trialEndDate
regularBilling.startDate = trialEndDate

// Create a recurring payment request
let recurringPaymentRequest = PKRecurringPaymentRequest(
    paymentDescription: "Book Club Membership",
    regularBilling: regularBilling,
    managementURL: URL(string: "https://www.example.com/managementURL")!
)
recurringPaymentRequest.trialBilling = trialBilling

recurringPaymentRequest.billingAgreement = """
50% off for the first month. You will be charged $20 every month after that until you cancel. \ You may cancel at any time to avoid future charges. To cancel, go to your Account and click \ Cancel Membership.
"""

recurringPaymentRequest.tokenNotificationURL = URL(
    string: "https://www.example.com/tokenNotificationURL"
)!

// Update the payment request
let paymentRequest = PKPaymentRequest()
// ...
paymentRequest.recurringPaymentRequest = recurringPaymentRequest

// Include in the summary items
let total = PKRecurringPaymentSummaryItem(label: "Book Club", amount: 10)
total.endDate = trialEndDate
paymentRequest.paymentSummaryItems = [trialBilling, regularBilling, total]
Automatic Payments - Automatic reload payment request swift · at 12:39 ↗
// Specify the reload amount and threshold
let automaticReloadBilling = PKAutomaticReloadPaymentSummaryItem(
    label: "Coffee Shop Reload",
    amount: 25
)
reloadItem.thresholdAmount = 5

// Create an automatic reload payment request
let automaticReloadPaymentRequest = PKAutomaticReloadPaymentRequest(
    paymentDescription: "Coffee Shop",
    automaticReloadBilling: automaticReloadBilling,
    managementURL: URL(string: "https://www.example.com/managementURL")!
)

automaticReloadPaymentRequest.billingAgreement = """
Coffee Shop will add $25.00 to your card immediately, and will automatically reload your \
card with $25.00 whenever the balance falls below $5.00. You may cancel at any time to avoid \ future charges. To cancel, go to your Account and click Cancel Reload.
"""

automaticReloadPaymentRequest.tokenNotificationURL = URL(
    string: "https://www.example.com/tokenNotificationURL"
)!

// Update the payment request
let paymentRequest = PKPaymentRequest()
// ...
paymentRequest.automaticReloadPaymentRequest = automaticReloadPaymentRequest

// Include in the summary items
let total = PKAutomaticReloadPaymentSummaryItem(
    label: "Coffee Shop",
    amount: 25
)
total.thresholdAmount = 5
paymentRequest.paymentSummaryItems = [total]
Order Tracking (swift) swift · at 19:17 ↗
func onAuthorizationChange(phase: PayWithApplePayButtonPaymentAuthorizationPhase) {
    switch phase {
    // ...
    case .didAuthorize(let payment, let resultHandler):
        server.createOrder(with: payment) { serverResult in
            guard case .success(let orderDetails) = serverResult else { /* handle error */ }
            let result = PKPaymentAuthorizationResult(status: .success, errors: nil)
            result.orderDetails = PKPaymentOrderDetails(
                orderTypeIdentifier: orderDetails.orderTypeIdentifier,
                orderIdentifier: orderDetails.orderIdentifier,
                webServiceURL: orderDetails.webServiceURL,
                authenticationToken: orderDetails.authenticationToken,
            )
            resultHandler(result)
        }
    }
}
Order Tracking (JS) javascript · at 20:13 ↗
paymentRequest.show().then((response) => {
    server.createOrder(response).then((orderDetails) => {
        let details = { };
        if (response.methodName === "https://apple.com/apple-pay") {
            details.data = {
                orderDetails: {
                    orderTypeIdentifier: orderDetails.orderTypeIdentifier,
                    orderIdentifier: orderDetails.orderIdentifier,
                    webServiceURL: orderDetails.webServiceURL,
                    authenticationToken: orderDetails.authenticationToken,
                },
            };
        }
        response.complete("success", details);
    });
});
VerifyIdentityWithWalletButton 2 swift · at 27:05 ↗
@ViewBuilder var verifiyIdentityButton: some View {
    VerifyIdentityWithWalletButton(
        .verifyIdentity,
        request: createRequest(),
    ) { result in
        // ...
    } fallback: {
        // verify identity another way
    }
}
Create a PKIdentityRequest swift · at 27:18 ↗
func createRequest() -> PKIdentityRequest {
    let descriptor = PKIdentityDriversLicenseDescriptor()
    descriptor.addElements([.age(atLeast: 18)],
                            intentToStore: .willNotStore)
    descriptor.addElements([.givenName, .familyName, .portrait],
                            intentToStore: .mayStore(days: 30))

    let request = PKIdentityRequest()
    request.descriptor = descriptor
    request.merchantIdentifier = // configured in Developer account
    request.nonce = // bound to user session
}
VerifyIdentityWithWalletButton 3 swift · at 27:19 ↗
@ViewBuilder var verifiyIdentityButton: some View {
    VerifyIdentityWithWalletButton(
        .verifyIdentity,
        request: createRequest(),
    ) { result in
        // ...
    } fallback: {
        // verify identity another way
    }
}
VerifyIdentityWithWalletButton 4 swift · at 29:37 ↗
@ViewBuilder var verifiyIdentityButton: some View {
    VerifyIdentityWithWalletButton(
        .verifyIdentity,
        request: createRequest(),
    ) { result in
        switch result {
        case .success(let document):
            // send document to server for decryption and verification
        case .failure(let error):
            switch error {
            case PKIdentityError.cancelled:
                // handle cancellation
            default:
                // handle other errors
            }
        }
    } fallback: {
        // verify identity another way
    }
}

Resources