集成苹果支付(swift4.0)

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

你要知道
  • 建议ios 系统 > 9 . 因为苹果支付, 从ios9 开始慢慢才完善.
  • 调起的wallet , 它只是一个桥梁,它并不会帮你扣钱,但只是获取到用户信用卡的资料和支付密码返回给你,连密码正确错误它都不知道.
  • 一般来说, 一个公司想用apple pay , 不会自己跑去和每个银行签约, 一般都是借助第三方. 因为第三方已经和银行签约了,比如: Stripe / mpgs
你要准备
  • 你的开发证书需要开通apple pay 功能, 从而获得 Merchant Identifier
  • 在xcode 中设置


  • 最后支付完成, 你还要调用它的方法告诉它结果, 然后它会显示给用户看, 再dismiss .
  • 所以大致可以分为: 调起支付 -> 拿到支付结果 -> 传给server -> 获取支付结果来自server -> 调用苹果支付的方法, 告诉它结果. 它自动dismiss .
  • Apple Pay Docs
    接下来就开始了, 我们直接上代码

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容