stripe / stripe-go Goto Github PK
View Code? Open in Web Editor NEWGo library for the Stripe API.
Home Page: https://stripe.com
License: MIT License
Go library for the Stripe API.
Home Page: https://stripe.com
License: MIT License
The Go runtime does not guarantee that pointer swaps are atomic (e.g. many non-x86 processors). In order for these globals to be used, the code needs to use locks or atomic loads and swaps to pull these objects out without racing.
Honestly, it would be preferable to have these as default clients etc. that can't be changed and just make it easier to build out clients that do not use the default HTTP client. Currently, it's very difficult to not use the default HTTP client, etc., because of the complicated use of BackendConfiguration, SupportedBackend, Backend, and even Backends types in this API.
I'm trying to do the following:
According to the documentation (https://stripe.com/docs/api#update_customer) there is an optional default_source
parameter, however don't see it on the customer params: https://github.com/stripe/stripe-go/blob/master/customer.go#L7-L17
Is this named something else? Is there another preferred way of doing this?
UPDATE: It looks like this used to be default_card
, but that this was deprecated and then removed in the API change of 2015-02-18. I'll submit a PR to change DefaultCard to DefaultSource, since it seems like it is not there intentionally.
Given a completely new and clean Stripe account the following code:
params := &stripe.InvoiceListParams{
Customer: "stripeId", // valid customer.ID
}
i := invoice.List(params)
for !i.Stop() {
inv, err := i.Next()
if err != nil {
// handle error
}
}
Bails with the following:
panic: interface conversion: interface is nil, not *stripe.Invoice
The offending line:
github.com/stripe/stripe-go/invoice/client.go:257
This seems to fail for all other iterators as well.
Lost a bit of time to this: rather than setting Customer.Token
to set the token for a card I was creating a CardParams
value and setting its Token
field and scratching my head about why it doesn't work. The CustomerClient.Create
method doesn't look for this, but maybe it should?
{"type":"invalid_request_error","message":"Invalid timestamp: must be an integer Unix timestamp in the future.","param":"trial_end"}
It would be good if:
&CustomerParams{TrialEndNow: true}
Thanks.
after retrieving a plan list, even though len(planList.Values)
returns non-zero length.
Calling Client.Init
with a non-nil httpClient
mutates the global httpClient
. This could cause confusing and unexpected behavior. Per a discussion with @cosn, we're simply going to remove the httpClient
parameter and global getter/setter. To pass a custom httpClient, pass a custom backend created with NewInternalBackend
.
According to Create Recipient Documentation, I should be able to pass a Stripe.js bank account token like so:
Bank: &stripe.BankAccountParams{Token: token}
in the same manner as:
Card: &stripe.CardParams{Token: token},
However, Token
is not a defined field in stripe.BankAccountParams
. Has it simply not been added yet?
We currently support adding an httpClient
since the default doesn't work on GAPS, but it should be called out specifically in the README to make it easier for people to discover.
The test in balance_test.go
should pass when returned balance is legitimately equal to zero.
if target.Available[0].Value == 0 {
t.Errorf("Available amount is not set\n")
}
NewIdempotencyKey is not actually idempotent. If time moves backwards (as it will to every machine running NTP this June when the leap second hits), this code will produce the same key many times. https://github.com/stripe/stripe-go/blob/master/params.go#L58
Instead, just pull 128 bits from crypto/rand.Read. That will be enough randomness to not collide for the lifetime of the universe.
The previous leap second in 2012 did not go well. http://www.wired.com/2012/07/leap-second-bug-wreaks-havoc-with-java-linux/
Is there a way to disable/redirect Stripe logging? Specifically the "Requesting..." message when an API request goes out.
I tried putting this in my own code
log.SetOutput(ioutil.Discard)
but this disables all logging, not just from this library.
I'm trying to save the charge response as an audit log in postgresql as a JSON field but whenever I try to json.Marshal
the object I get an nil pointer reference.
The trace output shows the error as originating here: github.com/stripe/stripe-go/payment_source.go:165
which is the MarshalJSON()
method on the PaymentSource
It seems to be caused because c.Card.Customer
is nil
here.
I've created a work around for it that satisfies my use-case in which in the MarshalJSON
method I check for the c.Card.Customer
object and only assign the Customer if it's set:
func (s *PaymentSource) MarshalJSON() ([]byte, error) {
var target interface{}
switch s.Type {
case PaymentSourceBitcoinReceiver:
target = struct {
Type PaymentSourceType `json:"object"`
*BitcoinReceiver
}{
Type: s.Type,
BitcoinReceiver: s.BitcoinReceiver,
}
case PaymentSourceCard:
var customerID *string
if s.Card.Customer != nil {
customerID = &s.Card.Customer.ID
}
target = struct {
Type PaymentSourceType `json:"object"`
Customer *string `json:"customer"`
*Card
}{
Type: s.Type,
Customer: customerID,
Card: s.Card,
}
case "":
target = s.ID
}
return json.Marshal(target)
}
My code:
sc := &client.API{}
sc.Init(stripeCredentials.TestKey, nil)
chargeParams := &stripe.ChargeParams{
Amount: uint64(order.TotalIncTax * 100),
Currency: currency.GBP,
Desc: "Payment To " + store.Name + " for Your Order #" + strconv.FormatInt(order.ExternalID, 10),
NoCapture: true,
}
chargeParams.Params.IdempotencyKey = stripe.NewIdempotencyKey()
chargeParams.SetSource(&stripe.CardParams{
Number: cardDetails.CardNumber,
Month: cardDetails.ExpiryMonth,
Year: cardDetails.ExpiryYear,
CVC: cardDetails.CVC,
Address1: order.BillingAddress.Line1,
Address2: order.BillingAddress.Line2,
City: order.BillingAddress.City,
State: order.BillingAddress.State,
Zip: order.BillingAddress.PostalCode,
Country: order.BillingAddress.CountryISO,
})
charge, err := sc.Charges.New(chargeParams)
// This method appends the passed value to the transaction AuditLog field which is then json.Marshalled()
// This call only fails if the charge object is passed in. Anything else works
transaction.AddAuditLog(&TransactionAuditLog{
Error: err,
Response: charge,
})
event.GetObjValue("amount_due")
will panic:
interface conversion: interface is float64, not string
Line 69 in e9daefe
The readme mentions go's lack of package versioning. gopkg.in provides such API versioning that may be useful. This may have already been considered and rejected, and the reasons would be interesting to hear. If not, is it worth adding?
This page https://stripe.com/docs/subscriptions claims that:
"If quantity is set to zero, the subscription will act like a free plan, creating invoices but not charging customers."
This in fact works when you call the API with something like CURL.
However, when omitted, the stripe API doc says "quantity optional, default is 1".
In Go zero (0) is the default value for an uninitialized number. As such stripe.SubParams.Quantity being set to zero means "un-initialized". Which gets elided from the request that is made. This means you cannot set a zero quantity subscription from the golang client. Trying to do so makes a subscription of quantity 1 since it falls back to the default.
Which is unfortunate because we have need for such a thing :).
I have a use case where I need to track transactions going across my application. Each request has a unique ID which I attach to the log entry. I propose that instead of using the log
package as it is seen here, that a Logger
variable be added as a package level variable to stripe.go
where the whole package could log to instead. This would allow runtime customization of the logging prefix, and level to a much higher degree then the stripe.LogLevel
variable gives.
Could create a new logger in an init
function as:
var Logger *log.Logger
func init() {
...
Logger = log.New(os.Stderr, "", log.LstdFlags)
...
}
Then customizations could be done with stripe.Logger.SetPrefix
or stripe.Logger.SetFlags
.
It's not really clear what purpose the Params
serve when passing them into the various Get
methods. With Customers.Get()
you can pass nil
for CustomerParams
. But with Subs.Get()
if you pass nil
for SubParams
you get a panic.
It's not obvious from the godoc that you need to create a SubParams
with the Customer
field set to a customer ID. It is a little more clear in the online Stripe docs but I wasn't looking at those. The stripe API docs don't give you a sense of the struct layout, so really you need to look at both.
(I'm finding the Params stuff to be awkward to use, especially with Gets. I think a better general pattern for client APIs is to follow the standard library's pattern in net/http where you have a type per request and set properties on it, making the request via a Do
call. Ie:
req, _ := http.NewRequest("GET", "http://google.com", nil)
req.Header.Set("User-Agent", "awesomesauce")
res, err := http.DefaultClient.Do(req)
For something like getting subscriptions you could add required properties to the request constructor, like:
sub, err := stripe.GetSubscription("sub_4pcfEeZMjXZlGY", "cus_4qlHv0B8e3WVoJ").Do()
Of course, I acknowledge this is all a matter of personal taste.)
Hi,
Currently we currently proxy requests to Stripe (for example, if the web application need to access the logged in customers' Customer
object it hits our API, which hits Stripe).
Unfortunately, when the Customer
object is marshalled, it loses all Source
information, which is somewhat unexpected.
Perhaps PaymentSource
can be transformed into an interface with Type()
,ID()
,Card()
and BitcoinReciever()
methods, and have Card
or BitcoinReciever
implement these methods? (Not backwards compat?)
Or maybe add a custom MarshalJson
function that makes sure PaymentSource
marshals itself appropriately?
Meta currently isn't included in many of the ResourceParams structs ex: CustomerParams. Is use of that discouraged for some reason?
When created a charge using stripe connect a response is returned with a transfer value, this is a string ID not an entire Transfer struct, so the response does not unmarshall the JSON correctly.
Please see corresponding PR for details:
Pull request here
The sub params do not support the special now
value for TrialEnd
. Using time.Now().Unix()
is problematic because depending on server/network lag you could end up with the following error
Error encountered from Stripe: {"type":"invalid_request_error","message":"Invalid timestamp: must be an integer Unix timestamp in the future.","param":"trial_end"}
I think a EndTrial
bool would work nice for this case.
Congrats on releasing this package! It looks comprehensive and clean. I have one initial suggestion, if you're interested:
I find the naming convention and behavior for iterators in this package a little confusing. In particular, does the Next method produce a new value every time it is called, or does it simply access a single stored item between calls to Stop? For example, does the following code print one value twice, or two different values?
i := plan.List(params)
for !i.Stop() {
log.Println(i.Next())
log.Println(i.Next())
}
It is not clear. (Even after reading the documentation it remains unclear, since the comment there says essentially nothing beyond repeating the name of the method.)
In contrast, two standard library types (bufio.Scanner and sql.Rows) have adopted a slightly different convention: the driver method that goes in the for loop condition is called Next
or Step
rather than Stop
, and, importantly, the iterator provides accessor methods for the current item in the sequence.
Using the standard library convention, the documented example for plan.List would look like this:
stripe.Key = "sk_key"
params := &stripe.PlanListParams{}
params.Filters.AddFilter("limit", "", "3")
params.Single = true
i := plan.List(params)
for i.Next() {
plan, err := i.Plan()
if err != nil {
log.Fatal(err)
}
log.Printf("%v ", plan.Name)
}
and my example above would look like either this:
i := plan.List(params)
for i.Next() {
log.Println(i.Plan())
log.Println(i.Plan())
}
or this, depending on the meaning of the API as it currently stands:
i := plan.List(params)
for i.Next() {
log.Println(i.Plan())
i.Next()
log.Println(i.Plan())
}
Either way, it is self-evident.
I suggest that Stripe should consider adopting the standard library's convention, both for the sake of consistency and because it is (IMO) inherently less confusing.
You could go further, and put the error in a separate accessor method (as done by Scanner) but that's a separate issue.
Seems that the github.com/stripe/stripe-go/customer.go
file got committed without go fmt
being run.
@@ -18,7 +18,7 @@
// SetSource adds valid sources to a CustomerParams object,
// returning an error for unsupported sources.
-func (cp *CustomerParams) SetSource(sp interface{}) (error) {
+func (cp *CustomerParams) SetSource(sp interface{}) error {
source, err := SourceParamsFor(sp)
cp.Source = source
return err
I'm working on a 3rd party stripe app. I saw in the webhook docs that a user_id
property will be returned if your using Stripe Connect so you can identify the stripe user the even belongs to.
While developing I was not getting the user_id in the Event instance. I noticed that the Event struct does not include that UserId property event.go#L7, so that JSON field is never decoded.
I made a quick change to event.go
to see if I could fix it, and everything was working as expected:
type Event struct {
ID string `json:"id"`
Live bool `json:"livemode"`
Created int64 `json:"created"`
Data *EventData `json:"data"`
Webhooks uint64 `json:"pending_webhooks"`
Type string `json:"type"`
Req string `json:"request"`
UserId string `json:"user_id"` // The new line
}
If I created a PR to fix this, would it get merged?
Is it possible to use Stripe Connect with this API? If so, can you provide some examples?
Thanks!
I see there's a ErrorCode
and ErrorType
struct definitions, but I don't see them being returned anywhere. For example http://godoc.org/github.com/stripe/stripe-go/sub#New returns builtin error
.
Thanks!
I can load a customer via customer.Get(customerId) directly and see the Email and Desc correctly.
However, if I load all charges for that same customer using charge.List(), and then try to access charge.Customer.Desc and charge.Customer.Email, they are both always blank.
I was expecting charge.Customer to display all attributes from the Customer struct.
In the ruby client, I'm able to pass expand: ['data.customer'] to load the details of the customer. However, I don't see how to do so using ChargeListParams or ListParams.
Several places in the code use token
as a field name or method parameter to refer to a Stripe API key. Given that "token" is already heavily overloaded in Stripe-land, I think it's more confusing than e.g. apiKey
.
The data contained in the Obj
field is always (?) a valid Stripe object, and the various objects support JSON unmarshaling. I'd like a json.RawMessage
field in the EventData
for the object so I could unmarshal it to a real object rather than having to use a map[string]interface{}
. This is what I do with my code currently:
type webhookEvent struct {
ID string `json:"id"`
Type string `json:"type"`
Data struct {
Object json.RawMessage `json:"object"`
PreviousAttributes map[string]interface{} `json:"previous_attributes"`
} `json:"data"`
}
func invoiceCreatedHandler(ev *webhookEvent) error {
var invoice stripe.Invoice
if err := json.Unmarshal(ev.Data.Object, &invoice); err != nil {
return err
}
}
I am not sure how setting http client globally works under app engine, where it is unique per request. Does the request attach to the correct context with concurrent access to the API.
I saw it is handled with passing a context in go oauth2 client.
please see here.
https://github.com/golang/oauth2/blob/master/client_appengine.go
https://github.com/golang/oauth2/blob/master/oauth2.go#L160
This may be evasive as all function needs to take a context as argument.
so, currently, am I expect to set the client before all API calls?
How come?
Hello,
Error encountered from Stripe: {"type":"invalid_request_error","message":"Invalid timestamp: must be an integer Unix timestamp in the future.","param":"trial_end"}
It would be good if Update have new params "TrialEndNow"
Thanks.
This line does not append "/upcoming" to the url for fetching upcoming invoices:
https://github.com/stripe/stripe-go/blob/master/invoice/client.go#L169
https://github.com/stripe/stripe-go/blob/master/fee/client.go#L35
Hi,
The Get method for Fee's is making a POST call to the refund endpoint, which then refunds the fee instead of returning unmodified fee information. Is this a bug or am I misunderstanding the usecase of Get here?
It seems like there should be both a method to Get
the data and one to Refund
(which the Get currently does).
Docs: https://stripe.com/docs/api/go#retrieve_application_fee
Thanks for the help!
Can we add the (undocumented but often-referred-to) billing_cycle_anchor field to SubParams?
This sounded strange as it would be pretty non-idiomatic to panic out of a library like this, so I spot-checked a few places[1] and found that the library is doing the right thing and just returning non-nil errors.
Please fix the documentation to not confuse/scare newcomers like me.
[1]
https://stripe.com/docs/api/go#create_customer
vs
https://github.com/stripe/stripe-go/blob/master/customer/client.go#L68
https://stripe.com/docs/api/go#create_card
vs
https://github.com/stripe/stripe-go/blob/master/sub/client.go#L65
How do I build a custom payment form without requiring my customers to use their card details?
The documentation states that you can use params.Filters.AddFilter("include", "", "total_count")
to fill out metadata, but the include type is forced to be an array.
Documentation should say: params.Filters.AddFilter("include[]", "", "total_count")
Right now there are no examples in GoDoc. We should have a list, create, update, and delete example.
Not sure if this feature is still experimental in the API but it would be awesome to have it included in the Go library.
golint (https://github.com/golang/lint) is a handy tool for ensuring that your code conforms to stylistic best practices, especially when it comes to naming. The guidelines are here: https://code.google.com/p/go-wiki/wiki/CodeReviewComments
In particular, things like Id
, Api
, Url
, and Http
should be ID
, API
, URL
, and HTTP
to match the standard library's conventions. It's frustrating when my own code uses ID
but stripe's uses Id
-- it results in a lot of compile-time errors.
Other than that, stripe-go is pretty golint-clean. There are a few complaints about lack of comments on exported identifiers, but nothing too severe.
While package names (e.g. https://github.com/stripe/stripe-go/blob/master/discounts.go#L1) don't have to match the basename of the path (stripe-go in this case), it's typically the convention. I was surprised to see "stripe" as an identifier in the first example on the blog post before I remembered that this was even possible.
Is it possible to make these match, or was there some reason for this package naming choice?
It looks like the error returned from a new charge is specifically HTTP and other language-level errors, not things like invalid number, card declined. Am I understanding this correctly?
I'd be happy to update the docs, I just wanted to check with you first that my understanding is correct.
A shipping
parameter is now present on the [Create Charge] API but it's not in bindings yet.
According to the API docs, the customer resource responds with a field default_source
which is a string representing the ID of the payment source: https://stripe.com/docs/api#create_customer
The Customer
type has a field DefaultSource
which is a pointer to a PaymentSource
: https://github.com/stripe/stripe-go/blob/master/customer.go#L43
It doesn't seem like the code would be able to unmarshal a string into an actual payment source. Am I missing a step?
According to Stripe API, when retrieving a customer that has been deleted, Stripe returns a subset of that customer's data and an extra property is returned called 'deleted' with a value of 'true'.
Without that being supported in Customer struct it is difficult to handle deleted customers.
Say you are trying to transfer money to a recipient, but your account doesn't have enough funds, and your code looks like:
params := stripe.TransferParams{
Amount: int64(32),
Currency: "usd",
Recipient: user.RecipientId,
Desc: "test account payout",
}
t, err := sc.Transfers.New(¶ms)
if err != nil {
user.TxfrId = t.Id
user.Save()
return emailConfirmation(user.Email, "Your money will be arriving soon")
}
In this case you've now told the user their money was paid out, when in fact no money was transferred. Instead you need the following error logic:
func transferError(t *stripe.Transfer, err error) error {
if err != nil {
return err
}
if t.FailCode != "" {
return fmt.Errorf("transfer_failure: Transfer %s failed with code %s", t.ID, t.FailCode)
}
return nil
}
This was not obvious, and almost led to people not getting paid.
I understand that this is more of an API decision than a library decision, but there's no page for "general questions about API design", and it's particularly an issue in Go when the a) the error return parameter is used everywhere to report errors, and b) most people would categorize "the money will not be transferred" as an error.
Although allowing the http client to be injected does indeed help with appengine usage line 68 of stripe.go is using an explicit Timeout in the http.Client struct literal. Appengine does not allow this. for now I'll be forking this but try using the Transport's deadline instead of the http.Client struct literal's timeout
The documented approach for using this package on App Engine works for the first HTTP request, but never again. Specifically, the documentation says to call
stripe.SetHTTPClient(urlfetch.Client(appengine.NewContext(r)))
That creates and uses an HTTP client which is only valid for the life of the current HTTP request (r
). On the next request, one calls stripe.SetHTTPClient
again. That sets the default HTTP client but doesn't change the API backend, which still references the HTTP client from the first request.
A second HTTP request might call something like customer.New(...)
which calls stripe.GetBackend(stripe.APIBackend)
which returns a backend referring to an HTTP client from the first request. Using that HTTP client gives an error like:
Post https://api.stripe.com/v1/customers: Call error 3: invalid security ticket: 6e2752a700a25c35
A better incantation for App Engine is something like:
stripe.SetBackend(stripe.APIBackend, nil)
stripe.SetHTTPClient(urlfetch.Client(appengine.NewContext(r)))
I'd submit a pull request, but I'm not sure if this should be corrected in the documentation, in SetHTTPClient
or in GetBackend
Somewhat related, using a global HTTP client in App Engine is bound to cause trouble when simultaneous requests want to call Stripe APIs. Both goroutines will contend over stripe.httpClient and at most one goroutine can win. I wish I could recommend a good solution to that problem.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.