【问题标题】:How to parse paymentMethod Object?如何解析 paymentMethod 对象?
【发布时间】:2021-08-14 02:41:58
【问题描述】:

我正在尝试列出我用户的所有付款方式。我主要是想获取品牌、有效期和信用卡号的后4位。

我尝试了多种方法来解析从我的服务器发回的 JSON 支付方式对象,但我还没有成功。这是我最近一次尝试中所做的。

在我的服务器中,我获得了所有用户付款方式的列表,并使用此函数将原始数据发送回我的客户端:

exports.listPaymentMethods = functions.https.onCall(async (data, context) => {
  const customerId = data.customer_id;
  const paymentMethods = await stripe.paymentMethods.list({
    customer: customerId,
    type: "card",
  });
  const pymtMethodData = paymentMethods.data;
  return {
    data: pymtMethodData,
  };
});

这是我从客户端调用函数的方式(它成功打印了原始数据):

func listPaymentMethods(customerID: String) {
    
    FirebaseReferenceManager.functions.httpsCallable("listPaymentMethods").call(["customer_id": customerID]) { (response, error) in
        
        if let error = error {
            print("failed to list customer's payment methods: \(error.localizedDescription)")
        }
        
        if let response = response?.data as? [String: Any] {
            let data = response["data"]
            print(data!) 
            self.parseJSON(data: data as! Data)
        }
        
    }
    
}

这是我用来存储支付方式数据的结构:

struct PaymentMethod: Decodable {
    let brand: String
    let expMonth: Int
    let expYear: Int
    let last4: Int
}

我尝试使用此函数解析数据但不成功:

func parseJSON(data: Data) {
    let decoder = JSONDecoder()
    do {
        let decodedData = try decoder.decode(PaymentMethod.self, from: data)
        print(decodedData.brand)
        
    } catch {
        print(error)
    }
}

这是我从print(data!)得到的原始数据

(
    {
    "billing_details" =         {
        address =             {
            city = "<null>";
            country = US;
            line1 = "<null>";
            line2 = "<null>";
            "postal_code" = 123456;
            state = "<null>";
        };
        email = "<null>";
        name = "<null>";
        phone = "<null>";
    };
    card =         {
        brand = visa;
        checks =             {
            "address_line1_check" = "<null>";
            "address_postal_code_check" = pass;
            "cvc_check" = pass;
        };
        country = US;
        "exp_month" = 12;
        "exp_year" = 2045;
        fingerprint = KKSbyUhUcKKSyto0;
        funding = credit;
        "generated_from" = "<null>";
        last4 = 4242;
        networks =             {
            available =                 (
                visa
            );
            preferred = "<null>";
        };
        "three_d_secure_usage" =             {
            supported = 1;
        };
        wallet = "<null>";
    };
    created = 1621941861;
    customer = "cus_JWaE2JEtbcI0JM";
    id = "pm_1IuyMzLVD5W50FFK2heBUTai";
    livemode = 0;
    metadata =         {
    };
    object = "payment_method";
    type = card;
}

)

我基本上是在尝试解析 JSON 数据,以便我可以拥有一个 PaymentMethod 数组(我在上面添加的结构),以便独立地引用该 PaymentMethod 中的每个单独的数据。

任何能引导我走向正确方向的东西都将非常感激! :)

【问题讨论】:

  • catch 打印的错误是什么?您是否使用诸如邮递员之类的第三方应用程序对您的 API 调用进行了任何测试,以确认返回的数据具有类似于支付模型的所有键?
  • @TusharSharma 它并没有那么远。当我尝试调用函数 self.parseJSON(data: data as! Data) 时,应用程序崩溃并且错误提示“无法将 NSArray 类型的值转换为 NSData”,我刚刚添加了上面的函数,以便您查看它的调用位置。
  • 所以您的 JSON 中有一个名为 data 的键。它不存在于您的结构PaymentMethod 中,它发生了什么? print(error) 的输出是什么?
  • self.parseJSON(data: data as! Data) 失败?完整的错误信息是什么?我猜这不能转换为数据,因为data 不是Data 实例。也许是字典?
  • @Larme JSON 中没有名为 data 的键。那是指从服务器发回的数据。

标签: ios node.js swift stripe-payments


【解决方案1】:

response.data 已经被解析为 Swift 对象,它是一个[[String: Any]]。不再是Data,不需要JSONDecoder。

在PaymentMethod 上添加自定义初始化:

init?(paymentDict: [String: Any]) {
    guard let card = paymentDict["card"] as? [String: Any] else { 
        print("There is no \"card\" key or it's not a [String: Any] in: \(paymentDict)")
        return nil
    }
    guard let brand = card["brand"] as? String else {
        print("There is no \"brand\" key or it's not a String in: \(card)")
        return nil
    }
    guard let expMonth = card["expMonth"] as? Int else {
        print("There is no \"expMonth\" key or it's not a Int in: \(card)")
        return nil
    }
    guard let expYear = card["expYear"] as? Int else {
        print("There is no \"expYear\" key or it's not a Int in: \(card)")
        return nil
    }
    guard let last4 = card["last4"] as? Int else {
        print("There is no \"last4\" key or it's not a Int in: \(card)")
        return nil
    }
    self.brand = brand
    self.expMonth = expMonth
    self.expYear = expYear
    self.last4 = last4
}

由于response.data 是Dictionary 的Array 而不是Dictionary,如果您只对解析第一个感兴趣:

if let response = response?.data as? [String: Any] {
    if let data = response["data"] {
        if let firstPaymentDictionary = data.first {
            if let payment = PaymentMethod(paymentDict: firstPaymentDictionary) {
                print("Payment found: \(payment)")
            } else {
                print("Invalid payment)
            }
        } else {
            print("No payment found in `\(data)`")
        }
    } else {
        print("No data in \(response)")
    }
}

旁注:
我在if let 中添加了所有else 并在guard let 的else 中添加了显式打印,以让您知道可能出现的问题及其含义。因为if let/guard let 应该优先于!。

【讨论】:

    猜你喜欢
    • 2019-12-25
    • 2018-05-30
    • 2012-09-28
    • 2017-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多