集成苹果支付(swift4.0)

2019-01-03  本文已影响0人  Jason_风筝

苹果支付虽然在国内用的不多, 但一些海外的app很大部分会用到,所以这里我们讨论一下怎么swift 怎么集成苹果

你要知道
你要准备

import UIKit
import PassKit
import AddressBook
class ApplepayService {

    private var payResultCallback: CallbackHandler? = nil
    var merchantIdentifier: String? = "你的appleMerchantIdentifier, 就是上图那个"
  
    // 支持的支付类型和网络类型
    private var supportedPaymentNetworks = [PKPaymentNetwork.visa, PKPaymentNetwork.masterCard, PKPaymentNetwork.amex]
    private let merchantCapabilities = PKMerchantCapability.RawValue(UInt8(PKMerchantCapability.capability3DS.rawValue) | UInt8(PKMerchantCapability.capabilityEMV.rawValue))

    typealias completionBlock = (PKPaymentAuthorizationStatus) -> ()
    private var completion: completionBlock? 
    //  这个方法, 可以当成初始化的方法, 因为9.2 才开始支持 chinaUnionPay,所以要做个判断.
    override func initialize() {
        if #available(iOS 9.2, *) {
            supportedPaymentNetworks.append(PKPaymentNetwork.chinaUnionPay)
        }
    }

    override func handleMessage(method: String, body: NSDictionary, callback: CallbackHandler?) {
        switch method {
        case "makePaymentRequest":   /// 发起支付
            if merchantIdentifier == nil {
                showAlert(nil, "miss apple merchant identifier")
                return
            }
            payResultCallback = callback
            makePaymentRequest(body, callback)
            break;
        case "completeLastTransaction":  // 支付完成的后要调用它告诉结果
            if merchantIdentifier == nil {
                showAlert(nil, "miss apple merchant identifier")
                return
            }
            completeLastTransaction(body, callback)
            break;
        default:
            break;
        }
    }

    /*
     body: {
        items: [   // 一定要, 要不会报错 
            {
                label: '3 x Basket Items',
                amount: 49.99
            }],
     
        shippingMethods: [  // 运输方式
            {
                identifier: 'NextDay',
                label: 'NextDay',
                detail: 'Arrives tomorrow by 5pm.',
            amount: 3.99
            }],
        currencyCode: 'HKD',   
        countryCode: 'HK',
        billingAddressRequirement: 'none', // none/all/postcode/email/phone
        shippingAddressRequirement: 'none', // none/all/postcode/email/phone
        shippingType: 'shipping'  // shipping/delivery/store/service
     }
     
     */
    private func makePaymentRequest(_ body: NSDictionary, _ callback: CallbackHandler?) {
        print("apple pay body:\(body) callback:\(callback)")
        guard canMakePayments() else { return }

        completion = nil

        let request = PKPaymentRequest();
        request.supportedNetworks = supportedPaymentNetworks
        request.merchantCapabilities = PKMerchantCapability(rawValue: merchantCapabilities)

        //request info
        if let currencyCode = body.value(forKey: "currencyCode") as? String {
            request.currencyCode = currencyCode
        }
        if let countryCode = body.value(forKey: "countryCode") as? String {
            request.countryCode = countryCode
        }

        if let merchantIdentifier = merchantIdentifier {
            request.merchantIdentifier = merchantIdentifier
        }

        request.requiredBillingAddressFields = billingAddressRequirementFromBody(body)
        request.requiredShippingAddressFields = shippingAddressRequirementFromArgumentsBody(body)
        if #available(iOS 8.3, *) {
            request.shippingType = shippingTypeFromBody(body)
        }
        request.shippingMethods = shippingMethodsFromBody(body)
        request.paymentSummaryItems = itemsFromBody(body)


        let authVC = PKPaymentAuthorizationViewController(paymentRequest: request)
        authVC?.delegate = self

        if let authVC = authVC {
            vc?.present(authVC, animated: true, completion: nil)
        } else {
          // 如果这个错,要看一下是不是参数没传对.
            showAlert(nil, "PKPaymentAuthorizationViewController was nil.")
            return
        }


    }
    // 判断是否能苹果支付
    private func canMakePayments() -> Bool {
        var canPayment = false
        if PKPaymentAuthorizationViewController.canMakePayments() {
            if (floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_8_0) { // < ios8.0
                showAlert(nil, "This device cannot make payments.")
            } else if #available(iOS 9.0, *) {
                if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: supportedPaymentNetworks, capabilities: PKMerchantCapability(rawValue: merchantCapabilities)) {
                    // This device can make payments and has a supported card"
                    canPayment = true
                } else {
                    showAlert(nil, "This device can make payments but has no supported cards.")
                }
            } else if #available(iOS 8.0, *) {
                if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: supportedPaymentNetworks) {
                    // This device can make payments and has a supported card , in ios 8
                    canPayment = true
                } else {
                    showAlert(nil, "This device can make payments but has no supported cards.")
                }
            } else {
                showAlert(nil, "This device cannot make payments.")
            }
        } else {
            showAlert(nil, "This device cannot make payments.")
        }
        return canPayment;
    }

    // Pay the call, pass the result
    /*
     body: {paymentAuthorizationStatus: success / failure / invalid-billing-address / invalid-shipping-address / invalid-shipping-contact / require-pin/ incorrect-pin/ locked-pin  }
     */
    private func completeLastTransaction(_ body: NSDictionary, _ callback: CallbackHandler?) {
        let paymentAuthorizationStatusString = self.paymentAuthorizationStatusFromBody(body)
        completion?(paymentAuthorizationStatusString)
    }
    /*
     {
        shippingMethods :{
     [{
     label:String
     amount:DecimalNumber
     detail:String?
     identifier:String?
     }]
            }
     }
     
     */
    private func shippingMethodsFromBody(_ body: NSDictionary) -> [PKShippingMethod] {
        var methods: [PKShippingMethod] = []
        if let tempMethods = body.value(forKey: "shippingMethods") as? Array<Dictionary<String, Any>> {
            for tempMethod in tempMethods {
                print("payment method:\(tempMethod)")
                let method = PKShippingMethod()
                if let lable = tempMethod["label"] as? String, let amount = tempMethod["amount"], let decimalValue = (amount as AnyObject).decimalValue {
                    let amountNumber = NSDecimalNumber(decimal: decimalValue)
                    method.label = lable
                    method.amount = amountNumber
                }
                let identifier = tempMethod["identifier"] as? String
                let detail = tempMethod["detail"] as? String
                method.detail = detail
                method.identifier = identifier
                methods.append(method)
            }
        }
        return methods;
    }

    /*
     {
        items :{
            [{
            label:String
            amount:DecimalNumber
            }]
        }
     }
     */
    private func itemsFromBody(_ body: NSDictionary) -> [PKPaymentSummaryItem] {
        var items: [PKPaymentSummaryItem] = []
        if let tempItems = body.value(forKey: "items") as? Array<Dictionary<String, Any>> {
            for item in tempItems {
//                print("payment item:\(item)")
                if let lable = item["label"] as? String, let amount = item["amount"], let decimalValue = (amount as AnyObject).decimalValue {
                    let amountNumber = NSDecimalNumber(decimal: decimalValue)
                    let newItem = PKPaymentSummaryItem(label: lable, amount: amountNumber)
                    items.append(newItem)
                }
            }
        }
        return items
    }
    // shipping/delivery/store/service
    @available(iOS 8.3, *)
    private func shippingTypeFromBody(_ body: NSDictionary) -> PKShippingType {
        if let shippingType = body.value(forKey: "shippingType") as? String {
            if shippingType == "shipping" {
                return PKShippingType.shipping
            } else if shippingType == "delivery" {
                return PKShippingType.delivery
            } else if shippingType == "store" {
                return PKShippingType.storePickup
            } else if shippingType == "service" {
                return PKShippingType.servicePickup
            }
        }
        return PKShippingType.shipping
    }

    /*
     // param:none/all/postcode/email/phone
     {shippingAddressRequirement:"none"}
     */
    private func shippingAddressRequirementFromArgumentsBody(_ body: NSDictionary) -> PKAddressField {
        if let shippingAddressRequirement = body.value(forKey: "shippingAddressRequirement") as? String {
            if shippingAddressRequirement == "none" {
                return PKAddressField.init(rawValue: 0) // none
            } else if shippingAddressRequirement == "all" {
                return PKAddressField.all
            } else if shippingAddressRequirement == "postcode" {
                return PKAddressField.postalAddress
            } else if shippingAddressRequirement == "name" {
                if #available(iOS 8.3, *) {
                    return PKAddressField.name
                }
            } else if shippingAddressRequirement == "email" {
                return PKAddressField.email
            } else if shippingAddressRequirement == "phone" {
                return PKAddressField.phone
            }
        }
        return PKAddressField.init(rawValue: 0) // none
    }

    /*
     // param:none/all/postcode/email/phone
     {billingAddressRequirement:"none"}
     */
    private func billingAddressRequirementFromBody(_ body: NSDictionary) -> PKAddressField {
        if let billingAddressRequirement = body.value(forKey: "billingAddressRequirement") as? String {
            if billingAddressRequirement == "none" {
                return PKAddressField.init(rawValue: 0) // none
            } else if billingAddressRequirement == "all" {
                return PKAddressField.all
            } else if billingAddressRequirement == "postcode" {
                return PKAddressField.postalAddress
            } else if billingAddressRequirement == "name" {
                if #available(iOS 8.3, *) {
                    return PKAddressField.name
                }
            } else if billingAddressRequirement == "email" {
                return PKAddressField.email
            } else if billingAddressRequirement == "phone" {
                return PKAddressField.phone
            }
        }
        return PKAddressField.init(rawValue: 0) // none
    }

    /*
     body: {paymentAuthorizationStatus: success / failure / invalid-billing-address / invalid-shipping-address / invalid-shipping-contact / require-pin/ incorrect-pin/ locked-pin  }
     */
    /// Pay the call, pass the result
    private func paymentAuthorizationStatusFromBody(_ body: NSDictionary) -> PKPaymentAuthorizationStatus {

        if let paymentAuthorizationStatus = body.value(forKey: "paymentAuthorizationStatus") as? String {
            if paymentAuthorizationStatus == "success" {
                return PKPaymentAuthorizationStatus.success
            } else if paymentAuthorizationStatus == "failure" {
                return PKPaymentAuthorizationStatus.failure
            } else if paymentAuthorizationStatus == "invalid-billing-address" {
                return PKPaymentAuthorizationStatus.invalidBillingPostalAddress
            } else if paymentAuthorizationStatus == "invalid-shipping-address" {
                return PKPaymentAuthorizationStatus.invalidShippingPostalAddress
            } else if paymentAuthorizationStatus == "invalid-shipping-contact" {
                return PKPaymentAuthorizationStatus.invalidShippingContact
            } else if paymentAuthorizationStatus == "require-pin" {
                if #available(iOS 9.2, *) {
                    return PKPaymentAuthorizationStatus.pinRequired
                }
            } else if paymentAuthorizationStatus == "incorrect-pin" {
                if #available(iOS 9.2, *) {
                    return PKPaymentAuthorizationStatus.pinIncorrect
                }
            } else if paymentAuthorizationStatus == "locked-pin" {
                if #available(iOS 9.2, *) {
                    return PKPaymentAuthorizationStatus.pinLockout
                }
            }
        }
        return PKPaymentAuthorizationStatus.failure
    }



}
/// PKPaymentAuthorizationViewControllerDelegate --------start
extension ApplepayService: PKPaymentAuthorizationViewControllerDelegate {

    func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
        controller.dismiss(animated: true, completion: nil)
    }

    // payment result , 这个方法就是用户支付后, 会调用的方法, 通过它获取到用户的资料.. 
    //  基本需要的资料都在这里了, 
    func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
        // , 要先拿到它, 最后要通过调用它告诉apple 支付结果, 它才会自动dismiss
        self.completion = completion
  
//        let paymentData =  String(data: payment.token.paymentData, encoding: .utf8);
        
        let response = formatPaymentForApplication(payment)
        payResultCallback?.success(response: response)

    }
    
//    @available(iOS 11.0, *)
//    func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Swift.Void) {
//        print("payment:\(payment)")
//    }

    /*
     {
     "paymentData": "<BASE64 ENCODED TOKEN WILL APPEAR HERE>",
     "transactionIdentifier": "Simulated Identifier",
     "paymentMethodDisplayName": "MasterCard 1234",
     "paymentMethodNetwork": "MasterCard",
     "paymentMethodTypeCard": "credit",
     "billingEmailAddress": "",
     "billingSupplementarySubLocality": "",
     "billingNameFirst": "First",
     "billingNameMiddle": "",
     "billingNameLast": "NAME",
     "billingAddressStreet": "Street 1\n",
     "billingAddressCity": "London",
     "billingAddressState": "London",
     "billingPostalCode": "POST CODE",
     "billingCountry": "United Kingdom",
     "billingISOCountryCode": "gb",
     "shippingEmailAddress": "",
     "shippingPhoneNumber": "",
     "shippingNameFirst": "First",
     "shippingNameMiddle": "",
     "shippingNameLast": "Name",
     "shippingSupplementarySubLocality": "",
     "shippingAddressStreet": "Street Line 1\nStreet Line 2",
     "shippingAddressCity": "London",
     "shippingAddressState": "London",
     "shippingPostalCode": "POST CODE",
     "shippingCountry": "United Kingdom",
     "shippingISOCountryCode": "gb",
     }
     */
    private func formatPaymentForApplication(_ payment: PKPayment) -> Dictionary<String, Any> {
        // 这里base64 了, 然后再传给server , server 还要decode , 这里看需求, 看你server 想要用怎么给它. 
        let paymentData = payment.token.paymentData.base64EncodedString()
        var response = Dictionary<String, Any>()
        response["paymentData"] = paymentData
        response["transactionIdentifier"] = payment.token.transactionIdentifier
        var typeCard = "error"
        if #available(iOS 9.0, *) {
            response["paymentMethodDisplayName"] = payment.token.paymentMethod.displayName
            response["paymentMethodNetwork"] = payment.token.paymentMethod.network

            switch payment.token.paymentMethod.type {
            case PKPaymentMethodType.unknown:
                typeCard = "unknown"
                break;
            case PKPaymentMethodType.debit:
                typeCard = "debit"
                break;
            case PKPaymentMethodType.credit:
                typeCard = "credit"
                break;
            case PKPaymentMethodType.prepaid:
                typeCard = "prepaid"
                break;
            case PKPaymentMethodType.store:
                typeCard = "store"
                break;
            }
        } else {
            typeCard = "error"
        }
        response["paymentMethodTypeCard"] = typeCard
        if #available(iOS 9.0, *), let billingContact = payment.billingContact {
            if let emailAddress = billingContact.emailAddress {
                response["billingEmailAddress"] = emailAddress
            }
            if #available(iOS 9.2, *), let supplementarySubLocality = billingContact.supplementarySubLocality {
                response["billingSupplementarySubLocality"] = supplementarySubLocality
            }
            if let name = billingContact.name {
                if let givenName = name.givenName {
                    response["billingNameFirst"] = givenName
                }
                if let middleName = name.middleName {
                    response["billingNameMiddle"] = middleName
                }
                if let familyName = name.familyName {
                    response["billingNameLast"] = familyName
                }
            }
            if let postalAddress = billingContact.postalAddress {
                response["billingAddressStreet"] = postalAddress.street
                response["billingAddressCity"] = postalAddress.city
                response["billingAddressState"] = postalAddress.state
                response["billingPostalCode"] = postalAddress.postalCode
                response["billingCountry"] = postalAddress.country
                response["billingISOCountryCode"] = postalAddress.isoCountryCode
            }

            if let shippingContact = payment.shippingContact {
                if let emailAddress = shippingContact.emailAddress {
                    response["shippingEmailAddress"] = emailAddress
                }
                if let phoneNumber = shippingContact.phoneNumber {
                    response["shippingPhoneNumber"] = phoneNumber.stringValue
                }
                if let name = shippingContact.name {
                    if let givenName = name.givenName {
                        response["shippingNameFirst"] = givenName
                    }
                    if let middleName = name.middleName {
                        response["shippingNameMiddle"] = middleName
                    }
                    if let familyName = name.familyName {
                        response["shippingNameLast"] = familyName
                    }
                }
                if #available(iOS 9.2, *), let supplementarySubLocality = shippingContact.supplementarySubLocality {
                    response["shippingSupplementarySubLocality"] = supplementarySubLocality
                }
                if let postalAddress = shippingContact.postalAddress {
                    response["shippingAddressStreet"] = postalAddress.street
                    response["shippingAddressCity"] = postalAddress.city
                    response["shippingAddressState"] = postalAddress.state
                    response["shippingPostalCode"] = postalAddress.postalCode
                    response["shippingCountry"] = postalAddress.country
                    response["shippingISOCountryCode"] = postalAddress.isoCountryCode
                }
            }

        } else if #available(iOS 8.0, *) {
            if let shippingAddress = payment.shippingAddress {

                if let PersonAddressStreetKey = kABPersonAddressStreetKey as? ABPropertyID, let shippingAddressStreet = ABRecordCopyValue(shippingAddress, PersonAddressStreetKey).takeRetainedValue() as? String {
                    response["shippingAddressStreet"] = shippingAddressStreet
                }
                if let PersonAddressCityKey = kABPersonAddressCityKey as? ABPropertyID, let shippingAddressCity = ABRecordCopyValue(shippingAddress, PersonAddressCityKey).takeRetainedValue() as? String {
                    response["shippingAddressCity"] = shippingAddressCity
                }
                if let PersonAddressZIPKey = kABPersonAddressZIPKey as? ABPropertyID, let shippingPostalCode = ABRecordCopyValue(shippingAddress, PersonAddressZIPKey).takeRetainedValue() as? String {
                    response["shippingPostalCode"] = shippingPostalCode
                }
                if let PersonAddressStateKey = kABPersonAddressStateKey as? ABPropertyID, let shippingAddressState = ABRecordCopyValue(shippingAddress, PersonAddressStateKey).takeRetainedValue() as? String {
                    response["shippingAddressState"] = shippingAddressState
                }
                if let PersonAddressCountryCodeKey = kABPersonAddressCountryCodeKey as? ABPropertyID, let shippingCountry = ABRecordCopyValue(shippingAddress, PersonAddressCountryCodeKey).takeRetainedValue() as? String {
                    response["shippingCountry"] = shippingCountry
                }
                if let PersonAddressCityKey = kABPersonAddressCityKey as? ABPropertyID, let shippingISOCountryCode = ABRecordCopyValue(shippingAddress, PersonAddressCityKey).takeRetainedValue() as? String {
                    response["shippingISOCountryCode"] = shippingISOCountryCode
                }
                if let shippingEmailAddress = ABRecordCopyValue(shippingAddress, kABPersonEmailProperty).takeRetainedValue() as? String {
                    response["shippingEmailAddress"] = shippingEmailAddress
                }

            }
        }
        return response
    }

}
/// PKPaymentAuthorizationViewControllerDelegate --------end

end.

20190128 补充: 感觉像是苹果的bug , 如果swift 用以上方法, 在ios 8-9 的系统可能会报passKit.framework 异常, 我是这么解决的, 新建一个 .m 文件, 也就是OC , 然后在里面导入 AddressBook 与 PassKit 就可以了, .m 会参与编译.

@import AddressBook;
#import <PassKit/PassKit.h>

@interface BridgePasskit : NSObject

@end
@implementation BridgePasskit

@end
上一篇下一篇

猜你喜欢

热点阅读