#michaelcrilly-paymentlink
1 messages ยท Page 1 of 1 (latest)
the easiest way is probably to include those information as metadata on your payment link. The metadata associated with this Payment Link will automatically be copied to checkout sessions created by this payment link.
I've attached metadata to the product, if that's what you mean? What object types do I need to get from the API to retrieve the metadata? Right now I'm looking at Charge objects.
sorry, lets take a step back, when you say Stripe Link, are you referring to https://stripe.com/en-gb-us/payments/payment-links or https://stripe.com/docs/payments/link?
I believe so. Here's an example (test data) product: https://buy.stripe.com/test_fZe5nX9FB3mV5i0cMO
https://stripe.com/en-gb-us/payments/payment-links - this, I believe.
okay so, when you navigate to https://dashboard.stripe.com/test/payment-links - you can view a list of payment links that you've created right?
Correct. I have three - two are deactivated and a third is active and is the one I sent above.
Do I need to be working with: https://stripe.com/docs/api/payment_links/payment_links ?
Indeed. I see that!
this is one option to include details about what you're selling
So I've attached some metadata to that active link. Are you saying I need to work with the payment_links API instead of working with the charges API?
you don't necessarily need to create a Payment Link via the API. There're several ways to retrieve the information that you want. More importantly though, you should retrieve the Checkout Sessions instead of the PaymentIntents or Charges
I'll just create Payment Link(s) via the console. All I want to do here is get the successful Checkout Sessions, but I cannot work out how-to do that without an existing session object.
you can't retrieve only successful Checkout Sessions - you will need to filter by a start and end date, paginate through the list and look for status : complete : https://stripe.com/docs/api/checkout/sessions/list
actually onesec, you don't need to use metadata
i := session.List(params) - this is literally impossible
Because I don't have a session object, nor is there a (Go) package called session or sessions. At least, I cannot go get ... one.
Yeah, i shouldn't need metadata if I can retrieve the Checkout Session as it'll likely have everything I need, but it's this session.* object I'm struggling to understand. I don't know where/how I go about making or retrieving that object.
Yes.
v72
session.New(...), perhaps?
https://pkg.go.dev/github.com/stripe/stripe-go/v72/checkout/session - this is the best reference I can find to session.
that's the right one, have you imported "github.com/stripe/stripe-go/v72/checkout/session"?
I have. Now I have a session object... it has no List() method as per your API: https://pkg.go.dev/github.com/stripe/stripe-go/v72@v72.111.0#CheckoutSession
this works for me
import (
"fmt"
"github.com/stripe/stripe-go/v72"
"github.com/stripe/stripe-go/v72/checkout/session"
)
func main() {
stripe.Key = "sk_test_..."
params := &stripe.CheckoutSessionListParams{}
params.Filters.AddFilter("limit", "", "3")
i := session.List(params)
for i.Next() {
s := i.CheckoutSession()
fmt.Println(s)
}
And for me. So I didn't need to create a Session object.
OMG... it has everything I need... I feel like crying... it only took days to get here lol
you can see the code examples at the right hand side of the page : https://stripe.com/docs/api/checkout/sessions/list
Indeed. Thank you.
OK... so .LineItems is always nil
Ah hang on, I might be accessing the wrong thing.
Hmm, I'm trying .LineItems and .PaymentLink.LineItems, and they're both always null (nil).
you need to expand line items, it's not included by default : https://stripe.com/docs/api/checkout/sessions/object#checkout_session_object-line_items
you can see an example of how to expand : https://stripe.com/docs/api/expanding_objects?lang=go
Thanks you for your patience, Alex.
[ERROR] Request error from Stripe (status 400): {"status":400,"message":"This property cannot be expanded (customer). You may want to try expanding 'data.customer' instead.","request_id":"req_3zb54YCkiOMyv9","type":"invalid_request_error"}
I'm taking deep breathes...
And no, data.customer doesn't work neither.
params.AddExpand("data.line_items")
This is referring to the customer.
I've tried both.
Both work, but the .Customer object on the Session is nil, again.
OK, I see. Stripe Link has, somehow, taken payments without creating a customer object: +--------------------------+--------+-----+------------+ | CUSTOMER | CHARGE | PID | PRODUCT(S) | +--------------------------+--------+-----+------------+ | ..............@gmail.com | $29.00 | | N/A | | ..............@gmail.com | $29.00 | | N/A | | ........@gmail.com | $29.00 | | N/A | | ........@gmail.com | $29.00 | | N/A | | ........@gmail.com | $29.00 | | N/A | | ........@gmail.com | $29.00 | | N/A | | ....@opsfactory.com.au | $3.00 | | N/A | +--------------------------+--------+-----+------------+
So if I skip over CheckoutSessions that have a nil .Customer attribute, I get what I'm after... but not everything
I cannot get the LineItems now the PyamentLink.LineItems - they're always nil or empty.
Despite expanding them.
I can share my code, but you deleted it from the thread last time, hence why I haven't shared it so far.
None of the data I'm working with here is production or private.
it'd be better if you didn't share emails here since this is a public channel
Sure. I understand. They're my email addresses, but I'll refrain from doing that.
sure, go ahead and paste your code here
stripe.Key = "sk_test_..."
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Customer", "Charge", "PID", "Product(s)"})
params := &stripe.CheckoutSessionListParams{}
params.AddExpand("data.customer")
params.AddExpand("data.line_items")
i := session.List(params)
for i.Next() {
s := i.CheckoutSession()
if s.Customer == nil {
continue
}
deets := []string{
s.Customer.Email,
fmt.Sprintf("$%.2f", float64(s.AmountTotal/100)),
s.ClientReferenceID,
"",
}
if s.PaymentLink.LineItems != nil && s.PaymentLink.LineItems.TotalCount > 0 {
for _, li := range s.PaymentLink.LineItems.Data {
deets[3] = fmt.Sprintf("%s, %s", deets[3], li.Product.ID)
}
} else {
deets[3] = "N/A"
}
table.Append(deets)
}
table.Render()
}```
I've tried s.LineItems. and s.PaymentLink.LineItems.
i'd suggest referring to the documentation to see how the object is structured
have you tried s.line_items
The CheckoutSession object has no such attribute.
๐
It has .LineItems, but it's always nil/empty.
+----------+--------+-----+---+------------+
| CUSTOMER | CHARGE | PID | # | PRODUCT(S) |
+----------+--------+-----+---+------------+
| ignored | $29.00 | | 0 | N/A |
| ignored | $29.00 | | 0 | N/A |
| ignored | $29.00 | | 0 | N/A |
| ignored | $29.00 | | 0 | N/A |
| ignored | $29.00 | | 0 | N/A |
| ignored | $29.00 | | 0 | N/A |
| ignored | $3.00 | | 0 | N/A |
+----------+--------+-----+---+------------+```
# = s.LineItems.TotalCount.
Is it because it's a PaymentLink?
onesec, let me test it out
Awesome, thanks.
I'm trying this: params := &stripe.CheckoutSessionListParams{} params.AddExpand("data.customer") params.AddExpand("data.line_items") params.AddExpand("data.payment_link") params.AddExpand("data.payment_link.line_items")
It is for me.
$ go run .
+----------+--------+--------------------+---+----------------------------------------+
| CUSTOMER | CHARGE | PID | # | PRODUCT(S) |
+----------+--------+--------------------+---+----------------------------------------+
| REDACTED | $29.00 | cus_LaClexI99IwQ3z | 0 | s.LineItems=nil / .TotalCount==0, Alex |
| REDACTED | $29.00 | cus_LaCi98m23pZMxD | 0 | s.LineItems=nil / .TotalCount==0, Alex |
| REDACTED | $29.00 | cus_LaCQwiFPrOOTXx | 0 | s.LineItems=nil / .TotalCount==0, Alex |
| REDACTED | $29.00 | cus_LaAhvfm1HYPZMT | 0 | s.LineItems=nil / .TotalCount==0, Alex |
| REDACTED | $29.00 | cus_LaAdij5RoFrzUE | 0 | s.LineItems=nil / .TotalCount==0, Alex |
| REDACTED | $29.00 | cus_La8Md3PcHVSWMD | 0 | s.LineItems=nil / .TotalCount==0, Alex |
| REDACTED | $3.00 | cus_K0stWrFLr5YFVc | 0 | s.LineItems=nil / .TotalCount==0, Alex |
+----------+--------+--------------------+---+----------------------------------------+
Based on the following code:
params := &stripe.CheckoutSessionListParams{}
params.AddExpand("data.customer")
params.AddExpand("data.line_items")
params.AddExpand("data.payment_link")
params.AddExpand("data.payment_link.line_items")
i := session.List(params)
for i.Next() {
s := i.CheckoutSession()
if s.Customer == nil {
continue
}
deets := []string{
// s.Customer.Email,
"REDACTED",
fmt.Sprintf("$%.2f", float64(s.AmountTotal/100)),
s.Customer.ID,
fmt.Sprintf("%d", s.LineItems.TotalCount),
"",
}
if s.LineItems != nil && s.LineItems.TotalCount > 0 {
for _, li := range s.LineItems.Data {
deets[4] = fmt.Sprintf("%s, %s", deets[3], li.Product.ID)
}
} else {
deets[4] = "s.LineItems=nil / .TotalCount==0, Alex"
}
table.Append(deets)
}```
We're so close, Alex. So, so close. I'm doing something wrong. If we can identify that, we've nailed it.
can you log the Checkout Session id that is returning nil for LineItems
I sure can, one moment...
+--------------------------------------------------------------------+----------+--------------------+--------+----------------------------------------+------------+
| CHECKOUT SESSION ID | CUSTOMER | PID | $$$ | # | PRODUCT(S) |
+--------------------------------------------------------------------+----------+--------------------+--------+----------------------------------------+------------+
| cs_test_a15KNLEvJdWv1Ml5Jq2k2Zt4JpclpDbdRIAuJHpqQlJLjukhKLrFRlnmwh | REDACTED | cus_LaClexI99IwQ3z | $29.00 | s.LineItems=nil / .TotalCount==0, Alex | |
| cs_test_a1gVWv4zf74xvMX6MGgZ3oPHJ1QPxnvLwKspNfOrFwqgcWFrBGihNrNDMF | REDACTED | cus_LaCi98m23pZMxD | $29.00 | s.LineItems=nil / .TotalCount==0, Alex | |
| cs_test_a1sRzxSpUy3TJh8MUavYXuBITNiscHg4Z0foa2dUDC8xkATXPTYsjVwvXP | REDACTED | cus_LaCQwiFPrOOTXx | $29.00 | s.LineItems=nil / .TotalCount==0, Alex | |
| cs_test_a12vz1r6WSHsXY6QEjpDamfif3HGUqMeJuDwVE4ZNWCfPn2RgeMRGRlLoS | REDACTED | cus_LaAhvfm1HYPZMT | $29.00 | s.LineItems=nil / .TotalCount==0, Alex | |
| cs_test_a1CeB639XiiNhv1kFJo4cOR3rfy5mb4rr19yvxVllXEzwVR40udjCE1aCD | REDACTED | cus_LaAdij5RoFrzUE | $29.00 | s.LineItems=nil / .TotalCount==0, Alex | |
| cs_test_a1bpZN8fogA63ruODXJySy37KdGBY1IYk1S0CAYVo1NQL9hLTDZ2IoWN28 | REDACTED | cus_La8Md3PcHVSWMD | $29.00 | s.LineItems=nil / .TotalCount==0, Alex | |
| cs_test_NPgfBinxJkkt3j6U8m78a27nCSy22vA5dQCe3S3AMQjgIRuwV53WJbxX | REDACTED | cus_K0stWrFLr5YFVc | $3.00 | s.LineItems=nil / .TotalCount==0, Alex | |
+--------------------------------------------------------------------+----------+--------------------+--------+----------------------------------------+------------+```
It's messy, sorry
I'm not sure if cs_* IDs are meant to be searchable, but I cannot find these in the UI when using the built-in search function (/)
you can't find them in the Dashboard Search, you need to search using the PaymentIntent id
Right. I can find the PIDs fine. Makes sense.
lets test this against a single Checkout Session. If you retrieve cs_test_a15KNLEvJdWv1Ml5Jq2k2Zt4JpclpDbdRIAuJHpqQlJLjukhKLrFRlnmwh and expand data.line_items, then log s.LineItems -> what do you get?
Sure thing. One moment...
OK, so the issue was I was checking to see if TotalCount was > 0, but it seems it's always 0, which is obviously useful and logical... /s
If I ignore that and, instead, just check the len(s.LineItems.Data) > 0, then it works: $ go run . +---------------------+----------+--------------------+--------+---+------------------------------------------+ | CHECKOUT SESSION ID | CUSTOMER | PID | $$$ | # | PRODUCT(S) | +---------------------+----------+--------------------+--------+---+------------------------------------------+ | IGNORED | REDACTED | cus_LaClexI99IwQ3z | $29.00 | 1 | $29.00, Test Course Access | | IGNORED | REDACTED | cus_LaCi98m23pZMxD | $29.00 | 1 | $29.00, Test Course Access | | IGNORED | REDACTED | cus_LaCQwiFPrOOTXx | $29.00 | 1 | $29.00, Test Course Access | | IGNORED | REDACTED | cus_LaAhvfm1HYPZMT | $29.00 | 1 | $29.00, Test Course Access | | IGNORED | REDACTED | cus_LaAdij5RoFrzUE | $29.00 | 1 | $29.00, Test Course Access | | IGNORED | REDACTED | cus_La8Md3PcHVSWMD | $29.00 | 1 | $29.00, Test Course Access | | IGNORED | REDACTED | cus_K0stWrFLr5YFVc | $3.00 | 1 | $3.00, End to End DevOps Earlier Adopter | +---------------------+----------+--------------------+--------+---+------------------------------------------+
So we're even closer now, thank you... but I cannot look at lineitem.Product.* as .Product is nil and I cannot expand it (I tried data.line_items.data.product, but the API rejected that.)
So now I just need to work out how-to either expand the Product information, or just take the ID and do a straight product lookup using the API.
i'd suggest that you log the data of the object that you've retrieved. Right now the problem is that you're making assumptions but not looking at the actual object structure.
I have the object structure. That's easy to find as I'm looking at the actual code itself...
alright, so if you log s.LineItems.Data - > this should be an array
This is the literal struct{} that will be available to me.
It is, yes. And I can loop over that just fine.
The object for which is: // A line item. type LineItem struct { APIResource // Total discount amount applied. If no discounts were applied, defaults to 0. AmountDiscount int64 `json:"amount_discount"` // Total before any discounts or taxes are applied. AmountSubtotal int64 `json:"amount_subtotal"` // Total tax amount applied. If no tax was applied, defaults to 0. AmountTax int64 `json:"amount_tax"` // Total after discounts and taxes. AmountTotal int64 `json:"amount_total"` // Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase. Must be a [supported currency](https://stripe.com/docs/currencies). Currency Currency `json:"currency"` Deleted bool `json:"deleted"` // An arbitrary string attached to the object. Often useful for displaying to users. Defaults to product name. Description string `json:"description"` // The discounts applied to the line item. Discounts []*LineItemDiscount `json:"discounts"` // Unique identifier for the object. ID string `json:"id"` // String representing the object's type. Objects of the same type share the same value. Object string `json:"object"` // The price used to generate the line item. Price *Price `json:"price"` // The ID of the product for this line item. // // This will always be the same as `price.product`. Product *Product `json:"product"` // The quantity of products being purchased. Quantity int64 `json:"quantity"` // The taxes applied to the line item. Taxes []*LineItemTax `json:"taxes"` }
if you get s.LineItems.Data[0] -> what do you see?
&{APIResource:{LastResponse:<nil>} AmountDiscount:0 AmountSubtotal:2900 AmountTax:0 AmountTotal:2900 Currency:usd Deleted:false Description:Test Course Access Discounts:[] ID:li_1Kt2LaFI0bgZdvZj2qqGIDve Object:item Price:0xc00011f600 Product:<nil> Quantity:1 Taxes:[]}
to get the Product id : s.LineItems.Data[0].Price.Product.ID
line_items.data.product is clearly not going to work
No, but .Product is an object that is not being expanded. It must have some use. However, I can totally work with .Price.Product.ID, for sure.
i don't understand what you mean by Product is an object that is not being expanded, what are you trying to retrieve here?
Product:<nil> == // The ID of the product for this line item. // // This will always be the same as `price.product`. Product *Product `json:"product"
*Product == type Product struct { ... }
I'm just saying, I was trying to access lineitem.Product but it's nil and I cannot expand it in the parameters. It's just unusual that it's nil and I cannot use it directly. But I can totally work with Price.Product as I know a Price is an object in the Stripe API too.
i'd ignore that. It's not listed on our documentation as one of the parameters here : https://stripe.com/docs/api/checkout/sessions/object
I'm not entirely certain right now why we still have that parameter on the Checkout. It could be some legacy reason.
The Product.ID is all I needed, though. Now I can simply "hard code" that and my code and understand that is, indeed, the product that's been bought.
Thanks A LOT Alex.
If there's a customer satisfaction survey or something I can fill out, please tell me how I go about doing that.
we don't have one on Discord but the thought is enough. I'm glad we managed to figure things out ๐