如何正确从Stripe订阅响应对象中获取结构化项?

huangapple go评论88阅读模式
英文:

How do i properly get struct items from stripe subscription response object?

问题

我正在尝试从Stripe订阅响应对象的结构中获取一些数据。这是响应对象结构的链接stribe subscription object

这是我目前的代码和我想要做的事情:

type SubscriptionDetails struct {
    CustomerId     string    `json:"customer_id"`
    SubscriptionId string    `json:"subscription_id"`
    StartAt        time.Time `json:"start_at"`
    EndAt          time.Time `json:"end_at"`
    Interval       string    `json:"interval"`
    Plan           string    `json:"plan"`
    PlanId         string    `json:"plan_id"`
    SeatCount      uint8     `json:"seat_count"`
    PricePerSeat   float64   `json:"price_per_seat"`
}

func CreateStripeSubscription(CustomerId string, planId string) (*SubscriptionDetails, error) {
    stripe.Key = StripeKey

    params := &stripe.SubscriptionParams{
        Customer: stripe.String(CustomerId),
        Items: []*stripe.SubscriptionItemsParams{
            &stripe.SubscriptionItemsParams{
                Price: stripe.String(planId),
            },
        },
    }
    result, err := sub.New(params)

    if err != nil {
        return nil, err
    }

    data := &SubscriptionDetails{}
    data.CustomerId = result.Customer.ID
    data.SubscriptionId = result.ID
    data.StartAt = time.Unix(result.CurrentPeriodStart, 0)
    data.EndAt = time.Unix(result.CurrentPeriodEnd, 0)
    data.Interval = result.Items.Data[0].Price.Recurring.Interval
    data.Plan = result.Items.Data[0].Price.Nickname
    data.SeatCount = result.Items.Data[0].Quantity
    data.PricePerSeat = float64(result.Items.Data[0].Price.UnitAmount) / 100

    return data, nil
}

对于data.CustomerIddata.PricePerSeat,你可以使用result.Customer.IDresult.Items.Data[0].Price.UnitAmount来获取数据。

更新:

这是来自Stripe的订阅对象的结构:

type FilesStripeCreateSubscription struct {
    ID                 string      `json:"id"`
    CancelAt           interface{} `json:"cancel_at"`
    CancelAtPeriodEnd  bool        `json:"cancel_at_period_end"`
    CanceledAt         interface{} `json:"canceled_at"`
    CurrentPeriodEnd   int64       `json:"current_period_end"`
    CurrentPeriodStart int64       `json:"current_period_start"`
    Customer           string      `json:"customer"`
    Items              struct {
        Data []struct {
            ID                string      `json:"id"`
            BillingThresholds interface{} `json:"billing_thresholds"`
            Created           int64       `json:"created"`
            Metadata          struct {
            } `json:"metadata"`
            Object string `json:"object"`
            Price  struct {
                ID               string      `json:"id"`
                Active           bool        `json:"active"`
                Currency         string      `json:"currency"`
                CustomUnitAmount interface{} `json:"custom_unit_amount"`
                Metadata         struct {
                } `json:"metadata"`
                Nickname  string `json:"nickname"`
                Object    string `json:"object"`
                Product   string `json:"product"`
                Recurring struct {
                    AggregateUsage interface{} `json:"aggregate_usage"`
                    Interval       string      `json:"interval"`
                    IntervalCount  int64       `json:"interval_count"`
                    UsageType      string      `json:"usage_type"`
                } `json:"recurring"`
                TaxBehavior       string      `json:"tax_behavior"`
                TiersMode         interface{} `json:"tiers_mode"`
                TransformQuantity interface{} `json:"transform_quantity"`
                Type              string      `json:"type"`
                UnitAmount        int64       `json:"unit_amount"`
                UnitAmountDecimal int64       `json:"unit_amount_decimal,string"`
            } `json:"price"`
            Quantity     int64         `json:"quantity"`
            Subscription string        `json:"subscription"`
            TaxRates     []interface{} `json:"tax_rates"`
        } `json:"data"`
    } `json:"items"`
}
英文:

I am trying to grab some data from the struct of the stripe response objects for subscriptions. Here is link to the structure of the response object stribe subscription object

Here is what i have and what am trying to do

type SubscriptionDetails struct {
CustomerId             string  `json:"customer_id"`
SubscritpionId         string  `json:"subscritpion_id"`
StartAt                time.Time  `json:"start_at"`
EndAt                  time.Time  `json:"end_at"`
Interval               string  `json:"interval"`
Plan                   string  `json:"plan"`
PlanId                 string  `json:"plan_id"`
SeatCount              uint8  `json:"seat_count"`
PricePerSeat           float64  `json:"price_per_seat"`
}
func CreateStripeSubscription(CustomerId string, planId string) (*SubscriptionDetails, error) {
stripe.Key = StripeKey
params := &stripe.SubscriptionParams{
Customer: stripe.String(CustomerId),
Items: []*stripe.SubscriptionItemsParams{
&stripe.SubscriptionItemsParams{
Price: stripe.String(planId),
},
},
}
result, err := sub.New(params)
if err != nil {
return nil, err
}
data := &SubscriptionDetails{}
data.CustomerId           = result.Customer
data.SubscritpionId       =  result.ID
data.StartAt              =  result.CurrentPeriodStart
data.EndAt                =  result.CurrentPeriodEnd
data.Interval             =  result.Items.Data.Price.Recurring.Interval
data.Plan                 =  result.Items.Data.Price.Nickname
data.SeatCount            =  result.Items.Data.Quantity
data.PricePerSeat         =  result.Items.Data.Price.UnitAmount
return data, nil	
}

there are some items that are easy to get directly like ID field i got easily with result.ID and no complaints but for other items here are the error messages am getting

cannot use result.CurrentPeriodStart (type int64) as type time.Time in assignment
...
cannot use result.Customer (type *stripe.Customer) as type string in assignment
...
result.Items.Data.price undefined (type []*stripe.SubscriptionItem has no field or method price)

So how do i get the data for data.CustomerId and data.PricePerSeat?

UPDATE:

here is structure of the subscription object from stripe

type FilesStripeCreateSubscription struct {
ID                    string      `json:"id"`
CancelAt             interface{}   `json:"cancel_at"`
CancelAtPeriodEnd    bool          `json:"cancel_at_period_end"`
CanceledAt           interface{}   `json:"canceled_at"`
CurrentPeriodEnd     int64         `json:"current_period_end"`
CurrentPeriodStart   int64         `json:"current_period_start"`
Customer             string        `json:"customer"`
Items                struct {
Data []struct {
ID                string      `json:"id"`
BillingThresholds interface{} `json:"billing_thresholds"`
Created           int64       `json:"created"`
Metadata          struct {
} `json:"metadata"`
Object string `json:"object"`
Price  struct {
ID               string      `json:"id"`
Active           bool        `json:"active"`
Currency         string      `json:"currency"`
CustomUnitAmount interface{} `json:"custom_unit_amount"`
Metadata         struct {
} `json:"metadata"`
Nickname  string `json:"nickname"`
Object    string `json:"object"`
Product   string `json:"product"`
Recurring struct {
AggregateUsage interface{} `json:"aggregate_usage"`
Interval       string      `json:"interval"`
IntervalCount  int64       `json:"interval_count"`
UsageType      string      `json:"usage_type"`
} `json:"recurring"`
TaxBehavior       string      `json:"tax_behavior"`
TiersMode         interface{} `json:"tiers_mode"`
TransformQuantity interface{} `json:"transform_quantity"`
Type              string      `json:"type"`
UnitAmount        int64       `json:"unit_amount"`
UnitAmountDecimal int64       `json:"unit_amount_decimal,string"`
} `json:"price"`
Quantity     int64         `json:"quantity"`
Subscription string        `json:"subscription"`
TaxRates     []interface{} `json:"tax_rates"`
} `json:"data"`
} `json:"items"`
}

答案1

得分: 1

看到你提到的三个错误:

  1. cannot use result.CurrentPeriodStart (type int64) as type time.Time in assignment

result.CurrentPeriodStart 的类型是 int64,而你试图将其赋值给类型为 time.Time 的字段,这显然会失败。
API 以 Unix 格式发送时间,你需要解析它以将其转换为 time.Time。对其他时间字段也要进行相同的操作。

data.StartAt = time.Unix(result.CurrentPeriodStart, 0)
  1. cannot use result.Customer (type *stripe.Customer) as type string in assignment

与上面类似,result.Customer 的类型是 *stripe.Customer,而你试图将其赋值给类型为 string 的字段。Customer ID 是 Customer 结构体内的一个字段

data.CustomerId = result.Customer.ID
  1. result.Items.Data.price undefined (type []*stripe.SubscriptionItem has no field or method price)

stripe.SubscriptionItem 结构体没有 price 字段。我不确定你想要什么。建议阅读订阅对象文档

英文:

Looking at the 3 errors you have mentioned

  1. cannot use result.CurrentPeriodStart (type int64) as type time.Time in assignment

Type of result.CurrentPeriodStart is int64 and you are trying to set it into a field of type time.Time, which will obviously fail.
The API is sending the time in unix format, which you need to parse to get it into time.Time. Do this for other time fields also

data.StartAt = time.Unix(result.CurrentPeriodStart, 0)
  1. cannot use result.Customer (type *stripe.Customer) as type string in assignment

Similar issue as above, the field result.Customer is of type *stripe.Customer while you are trying to set it into a field of type string. Customer ID is a field inside the struct Customer

data.CustomerId = result.Customer.ID
  1. result.Items.Data.price undefined (type []*stripe.SubscriptionItem has no field or method price)

stripe.SubscriptionItem struct does not have a field price. I am not sure what you want here. I suggest reading the subscriptions object documentation.

答案2

得分: 1

要访问价格 result.Items.Data[0].Price.UnitAmount

如果你使用调试器,只需在 sub.New 行后设置一个断点,然后探索结果变量的内容。

英文:

To access the price result.Items.Data[0].Price.UnitAmount

If you use a debugger, just put a breakpoint after the sub.New line and explore the content of the result variable

答案3

得分: 1

首先让我们来看一下代码的内部工作原理,返回的内容,然后逐个解决问题。

当我们使用params调用sub.New()方法时,它会返回Subscription类型。

注意:我只会显示类型的有限定义,因为添加完整的结构会使答案变得冗长,并且与问题的上下文无关。

让我们来看一下Subscription类型:

type Subscription struct {
  ...
  // 当前周期开始的时间,该订阅已开具发票。
  CurrentPeriodStart int64 `json:"current_period_start"`
  // 拥有该订阅的客户的ID。
  Customer *Customer `json:"customer"`
  ...
  // 订阅项列表,每个项都附有价格。
  Items *SubscriptionItemList `json:"items"`
  ...
}

让我们先看一下第一个错误:

cannot use result.CurrentPeriodStart (type int64) as type time.Time in assignment

根据Subscription类型,我们可以看到CurrentPeriodStart的类型是int64,而你正在尝试将其设置为SubscriptionDetails类型的StartAt字段,而SubscriptionDetails类型的StartAt字段的类型是time.Time。由于类型不同,一个类型不能赋值给另一个类型,为了解决这个问题,我们需要显式地将其转换为time.Time,可以按照以下方式进行转换:

data.StartAt = time.Unix(result.CurrentPeriodStart, 0)

time.Unix方法从传递的int64值创建time.Time类型并返回,我们可以将其赋值给StartAt字段。

现在让我们继续处理第二个错误:

cannot use result.Customer (type *stripe.Customer) as type string in assignment

根据Subscription的定义,Customer字段的类型是*Customer,而不是字符串类型。由于你正在尝试将*Customer类型赋值给字符串类型的CustomerId字段,这是不可能的,导致了上述错误。正确的数据不在这里,正确的数据在*Customer类型内部的ID字段中,可以按照以下方式检索到正确的数据:

data.CustomerId = result.Customer.ID

让我们继续处理最后一个错误:

result.Items.Data.price undefined (type []*stripe.SubscriptionItem has no field or method price)

再次查看Subscription的定义,我们可以看到Items字段的类型是*SubscriptionItemList类型,如果我们查看*SubscriptionItemList的定义:

type SubscriptionItemList struct {
	APIResource
	ListMeta
	Data []*SubscriptionItem `json:"data"`
}

它包含一个名为Data的字段,而Data的类型是[]*SubscriptionItem,请注意它是*SubscriptionItem的切片[],这就是错误的原因,因为Data字段是*SubscriptionItem的切片,我们可以按照以下方式解决这个问题:

data.PricePerSeat = result.Items.Data[0].price.UnitAmount

还有一些可能出现的错误,我想指出,请继续阅读下面的内容以解决这些问题。

现在让我们来看一下*SubscriptionItem的定义:

type SubscriptionItem struct {
  ...
  Price *Price `json:"price"`
  ...
}

它包含Price字段,请注意名称以大写字母开头,在共享代码中,它以小写字母引用,这可能会导致另一个问题。最后,如果我们查看Price的定义:

type Price struct {
  ...
  // 要收取的单位金额,如果可能的话,表示为整数。仅在`billing_scheme=per_unit`时设置。
  UnitAmount int64 `json:"unit_amount"`

  // 要收取的单位金额,表示为最多12位小数的十进制字符串。仅在`billing_scheme=per_unit`时设置。
  UnitAmountDecimal float64 `json:"unit_amount_decimal,string"`
  ...
}

它包含UnitAmount字段,这是我们正在使用的字段,但是这里有一个问题,UnitAmount的类型是int64,而PricePerSeat的类型是float64,将不同类型赋值给彼此将再次导致错误。你可以将int64转换为float64,或者更好的是,你可以使用Price类型中的UnitAmountDecimal字段,该字段以float64格式包含相同的数据,这样可以减少在使用UnitAmount字段时必须进行的显式转换。因此,根据上述解释,我们得到以下解决方案:

data.PricePerSeat = result.Items.Data[0].Price.UnitAmountDecimal
英文:

Lets first go over the code which works under the hood, what is being returned and then we look at the problems one at a time.

When we call sub.New() method with params it returns Subscription type

Note: I will only show limited definition for types since adding the complete structure will make the answer big and not specific to the question context

Lets Look Subscription type

type Subscription struct {
  ...
  // Start of the current period that the subscription has been invoiced for.
  CurrentPeriodStart int64 `json:"current_period_start"`
  // ID of the customer who owns the subscription.
  Customer *Customer `json:"customer"`
  ...
  // List of subscription items, each with an attached price.
  Items *SubscriptionItemList `json:"items"`
  ...
}

Let looks over the first error

cannot use result.CurrentPeriodStart (type int64) as type time.Time in assignment

According to the Subscription type we can see CurrentPeriodStart is of type int64 while you are trying to set it to StartAt field of SubscriptionDetails type which is of type time.Time since the types are different one type cannot be assigned to other, to solve the issue we need to explicitly convert it to time.Time which can be done as follows:

data.StartAt = time.Unix(result.CurrentPeriodStart, 0)

time.Unix method creates time.Time type from the passed int64 value and return which we can assign to StartAt field

Now lets move on to the second error

cannot use result.Customer (type *stripe.Customer) as type string in assignment

As we can see from Subscription definition Customer field is of type *Customer it is not string type since you are trying to assign *Customer type to CustomerId field of string type which is not possible that causes the above error, referred data is incorrect then where is the correct data the correct data is available inside *Customer type withing ID field which can be retrieved as follows

data.CustomerId = result.Customer.ID

Lets go over the last error

result.Items.Data.price undefined (type []*stripe.SubscriptionItem has no field or method price)

Again if we look at Subscription definition we can see Items field is of type *SubscriptionItemList type and if we look at *SubscriptionItemList definition

type SubscriptionItemList struct {
	APIResource
	ListMeta
	Data []*SubscriptionItem `json:"data"`
}

It contains a field name Data and Data is of type []*SubscriptionItem notice it is slice [] of *SubscriptionItem, which is the cause of the error since Data field is slice of *SubscriptionItem we can solve the problem as below:

data.PricePerSeat = result.Items.Data[0].price.UnitAmount

There are few more errors which can occur that I would like to point out please continue reading below to solve those issue

Now lets look at *SubscriptionItem definition

type SubscriptionItem struct {
  ...
  Price *Price `json:"price"`
  ...
}

It contains Price field notice the name starts with capital letter and in shared code it is referred with small letter which can cause another issue and finally if we look at Price definition

type Price struct {
  ...
  // The unit amount in %s to be charged, represented as a whole integer if possible. Only set if `billing_scheme=per_unit`.
  UnitAmount int64 `json:"unit_amount"`

  // The unit amount in %s to be charged, represented as a decimal string with at most 12 decimal places. Only set if `billing_scheme=per_unit`.
  UnitAmountDecimal float64 `json:"unit_amount_decimal,string"`
  ...
}

It contains UnitAmount field which is what we are using, but there is a catch here UnitAmount is of type int64 but PricePerSeat is of type float64 assigning different type to each other will again cause error so either you can convert int64 to float64 or even better you can use UnitAmountDecimal field available within Price type containing the same data in float64 format which will reduce the explicit conversion that we would have to do when using UnitAmount field, so as per the explanation we get below solution

data.PricePerSeat = result.Items.Data[0].Price.UnitAmountDecimal

huangapple
  • 本文由 发表于 2022年8月28日 10:40:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/73515634.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定