๐ณ PassKit & Apple Pay
์ง๊ฐ ์ฑ ํตํฉ๊ณผ Apple Pay ๊ฒฐ์ ๋ฅผ ๊ตฌํํฉ๋๋ค. ํญ ํ ๋ฒ์ผ๋ก ๊ฒฐ์ ์๋ฃ!
โจ PassKit์ด๋?
PassKit์ ๋ ๊ฐ์ง ์ฃผ์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค: (1) Wallet Pass ์์ฑ (๋ฉค๋ฒ์ญ ์นด๋, ์ฟ ํฐ, ํฐ์ผ ๋ฑ), (2) Apple Pay ๊ฒฐ์ ์ฒ๋ฆฌ
๐ณ Apple Pay ๊ฒฐ์ ๊ตฌํ
ApplePayManager.swift
import PassKit class ApplePayManager: NSObject, PKPaymentAuthorizationViewControllerDelegate { static let shared = ApplePayManager() // Apple Pay ์ง์ ์ฌ๋ถ ํ์ธ func canMakePayments() -> Bool { return PKPaymentAuthorizationViewController.canMakePayments() } // ๊ฒฐ์ ์์ฒญ ์์ฑ func createPaymentRequest() -> PKPaymentRequest { let request = PKPaymentRequest() request.merchantIdentifier = "merchant.com.yourcompany.app" request.supportedNetworks = [.visa, .masterCard, .amex] request.merchantCapabilities = .capability3DS request.countryCode = "KR" request.currencyCode = "KRW" // ๊ฒฐ์ ํญ๋ชฉ let item = PKPaymentSummaryItem( label: "ํ๋ฆฌ๋ฏธ์ ๋ฉค๋ฒ์ญ", amount: NSDecimalNumber(value: 9900) ) request.paymentSummaryItems = [item] return request } // ๊ฒฐ์ ์ํธ ํ์ func startPayment(from viewController: UIViewController) { let request = createPaymentRequest() guard let paymentVC = PKPaymentAuthorizationViewController(paymentRequest: request) else { return } paymentVC.delegate = self viewController.present(paymentVC, animated: true) } // ๊ฒฐ์ ์๋ฃ ์ฒ๋ฆฌ func paymentAuthorizationViewController( _ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void ) { // ๋ฐฑ์๋๋ก payment.token ์ ์ก processPayment(payment.token) { success in if success { completion(PKPaymentAuthorizationResult(status: .success, errors: nil)) } else { completion(PKPaymentAuthorizationResult(status: .failure, errors: nil)) } } } func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) { controller.dismiss(animated: true) } func processPayment(_ token: PKPaymentToken, completion: @escaping (Bool) -> Void) { // ์ค์ ๊ฒฐ์ ์ฒ๋ฆฌ ๋ก์ง completion(true) } }
๐จ SwiftUI์์ Apple Pay ๋ฒํผ
ApplePayButton.swift
import SwiftUI import PassKit struct ApplePayButtonView: UIViewRepresentable { var action: () -> Void func makeUIView(context: Context) -> PKPaymentButton { let button = PKPaymentButton( paymentButtonType: .buy, paymentButtonStyle: .black ) button.addTarget( context.coordinator, action: #selector(Coordinator.buttonTapped), for: .touchUpInside ) return button } func updateUIView(_ uiView: PKPaymentButton, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(action: action) } class Coordinator: NSObject { var action: () -> Void init(action: @escaping () -> Void) { self.action = action } @objc func buttonTapped() { action() } } } // ์ฌ์ฉ ์์ struct CheckoutView: View { var body: some View { VStack { Text("์ด 9,900์") .font(.title) ApplePayButtonView { // Apple Pay ๊ฒฐ์ ์์ ApplePayManager.shared.startPayment(from: UIApplication.shared.windows.first!.rootViewController!) } .frame(height: 50) .padding() } } }
๐ซ Wallet Pass ์์ฑ
๋ฉค๋ฒ์ญ ์นด๋, ์ฟ ํฐ, ํฐ์ผ ๋ฑ์ Wallet ์ฑ์ ์ถ๊ฐํ ์ ์์ต๋๋ค.
CreatePass.swift
import PassKit func addPassToWallet(passURL: URL) { guard let passData = try? Data(contentsOf: passURL) else { return } guard let pass = try? PKPass(data: passData) else { return } let addPassVC = PKAddPassesViewController(pass: pass) // present addPassVC } // Pass ์ ๋ฐ์ดํธ ํธ์ ์๋ฆผ func sendPassUpdateNotification(passTypeIdentifier: String, serialNumber: String) { // ์๋ฒ์์ Apple Push Notification ์ ์ก // ์ฌ์ฉ์ Wallet์ Pass๊ฐ ์๋์ผ๋ก ์ ๋ฐ์ดํธ๋จ }
๐ฑ Pass Library ์ ๊ทผ
PassLibrary.swift
import PassKit class PassManager { let library = PKPassLibrary() // ์ฌ์ฉ์์ Wallet์ ํน์ Pass๊ฐ ์๋์ง ํ์ธ func hasPass(passTypeIdentifier: String, serialNumber: String) -> Bool { return library.containsPass( withPassTypeIdentifier: passTypeIdentifier, serialNumber: serialNumber ) } // ํน์ Pass ๊ฐ์ ธ์ค๊ธฐ func getPass(passTypeIdentifier: String, serialNumber: String) -> PKPass? { return library.pass( withPassTypeIdentifier: passTypeIdentifier, serialNumber: serialNumber ) } // ๋ชจ๋ Pass ๊ฐ์ ธ์ค๊ธฐ func getAllPasses() -> [PKPass] { return library.passes } // Pass ์ญ์ func removePass(_ pass: PKPass) { library.removePass(pass) } }
๐ Pass ์ ๋ฐ์ดํธ ๊ฐ์ง
PassNotifications.swift
import PassKit class PassObserver { let library = PKPassLibrary() func observePassChanges() { NotificationCenter.default.addObserver( self, selector: #selector(passLibraryDidChange), name: .PKPassLibraryDidChange, object: library ) NotificationCenter.default.addObserver( self, selector: #selector(passLibraryRemoteDidChange), name: .PKPassLibraryRemotePaymentPassesDidChange, object: library ) } @objc func passLibraryDidChange() { print("Pass ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ณ๊ฒฝ๋จ") // UI ์ ๋ฐ์ดํธ } @objc func passLibraryRemoteDidChange() { print("์๊ฒฉ ๊ฒฐ์ Pass ๋ณ๊ฒฝ๋จ") } }
๐ฐ ๋ฐฐ์ก ์ ๋ณด ์์ฒญ
ShippingMethods.swift
func createPaymentRequest() -> PKPaymentRequest { let request = PKPaymentRequest() // ... ๊ธฐ๋ณธ ์ค์ // ๋ฐฐ์ก ์ ๋ณด ์์ฒญ request.requiredShippingContactFields = [.name, .postalAddress, .phoneNumber] // ๋ฐฐ์ก ๋ฐฉ๋ฒ let standard = PKShippingMethod( label: "์ผ๋ฐ ๋ฐฐ์ก", amount: NSDecimalNumber(value: 3000) ) standard.identifier = "standard" standard.detail = "3-5์ผ ์์" let express = PKShippingMethod( label: "๋น ๋ฅธ ๋ฐฐ์ก", amount: NSDecimalNumber(value: 5000) ) express.identifier = "express" express.detail = "1-2์ผ ์์" request.shippingMethods = [standard, express] return request } // ๋ฐฐ์ก ์ ๋ณด ๋ณ๊ฒฝ ์ func paymentAuthorizationViewController( _ controller: PKPaymentAuthorizationViewController, didSelect shippingMethod: PKShippingMethod, handler completion: @escaping (PKPaymentRequestShippingMethodUpdate) -> Void ) { // ๋ฐฐ์ก ๋ฐฉ๋ฒ์ ๋ฐ๋ผ ์ด์ก ์ ๋ฐ์ดํธ let item = PKPaymentSummaryItem(label: "์ํ", amount: 9900) let shipping = PKPaymentSummaryItem(label: shippingMethod.label, amount: shippingMethod.amount) let total = PKPaymentSummaryItem( label: "์ด ํฉ๊ณ", amount: item.amount.adding(shipping.amount) ) let update = PKPaymentRequestShippingMethodUpdate(paymentSummaryItems: [item, shipping, total]) completion(update) }
๐ฏ ์ฟ ํฐ ์ ์ฉ
CouponCode.swift
func createPaymentRequest() -> PKPaymentRequest { let request = PKPaymentRequest() // ... ๊ธฐ๋ณธ ์ค์ // ์ฟ ํฐ ์ฝ๋ ์ ๋ ฅ ์ง์ (iOS 15+) request.supportsCouponCode = true return request } func paymentAuthorizationViewController( _ controller: PKPaymentAuthorizationViewController, didChangeCouponCode couponCode: String, handler completion: @escaping (PKPaymentRequestCouponCodeUpdate) -> Void ) { // ์ฟ ํฐ ์ ํจ์ฑ ๊ฒ์ฌ if couponCode == "WELCOME10" { let discount = PKPaymentSummaryItem( label: "ํ ์ธ (-10%)", amount: NSDecimalNumber(value: -990) ) let item = PKPaymentSummaryItem(label: "์ํ", amount: 9900) let total = PKPaymentSummaryItem(label: "์ด ํฉ๊ณ", amount: 8910) let update = PKPaymentRequestCouponCodeUpdate( paymentSummaryItems: [item, discount, total] ) completion(update) } else { let error = PKPaymentRequest.Error( .couponCodeInvalid, localizedDescription: "์ ํจํ์ง ์์ ์ฟ ํฐ ์ฝ๋์ ๋๋ค" ) let update = PKPaymentRequestCouponCodeUpdate(errors: [error], paymentSummaryItems: [], shippingMethods: []) completion(update) } }
๐ก HIG ์ฒดํฌ๋ฆฌ์คํธ
โ
๊ณต์ Apple Pay ๋ฒํผ ์ฌ์ฉ (์ง์ ๋์์ธ ๊ธ์ง)
โ
๊ฒฐ์ ์ ์ด์ก ๋ช
ํํ ํ์
โ
๋ฐฐ์ก๋น, ์ธ๊ธ ๋ฑ ์ถ๊ฐ ๋น์ฉ ๋ช
์
โ
์๋ฌ ๋ฉ์์ง๋ ์ฌ์ฉ์ ์นํ์ ์ผ๋ก
โ
๊ฒฐ์ ์๋ฃ ํ ๋ช
ํํ ํผ๋๋ฐฑ