Skip to content

Migration guide for Stripe Client

Michael Broshi edited this page Jul 21, 2025 · 8 revisions

High-level description of changes

Starting with v82.1, the new stripe.Client type is replacing client.API to provide a more ergonomic, consistent, and less error-prone experience. You create the former using stripe.NewClient(stripeKey). It’s almost a drop-in replacement, except for the differences listed below.

  1. Service method names now align with Stripe API docs. The stripe.Client uses Create, Retrieve, Update, and Delete (instead of New, Get, Update, and Del).
  2. The first argument of each service method is a context.Context.
  3. Parameter objects are now method-specific. For example, CustomerCreateParams and CustomerDeleteParams instead of simply CustomerParams. This allows us to put the right fields in the right methods at compile time.
  4. Services are all version-namespaced for symmetry. E.g. stripeClient.V1Accounts and stripeClient.V2Accounts.
  5. List methods return an iter.Seq2, so they can be ranged over without explicit calls to Next, Current, and Err.

Details of migration

Creating a client

The new stripe.Client is almost, but not quite, a drop-in replacement for client.API. You create a new client using stripe.NewClient(apiKey). You can replace client.API construction with stripe.Client using this regex:

/client.New\((.*), nil\)/stripe.NewClient($1)/

Before

sc := client.New(env().StripeKey, nil)

After

sc := stripe.NewClient(env().StripeKey)

If you are passing in your own stripe.Backends into client.New, you pass them into the stripe.Client as follows

sc := stripe.NewClient(stripeKey, stripe.WithBackends(backends))

CRUD methods

CRUD methods are now called Create, Retrieve, Update, and Delete

Old New
params := &stripe.CustomerParams{...}
params.Context = context.TODO() 
sc.Customers.New(params)
params := &stripe.CustomerCreateParams{...} 
sc.V1Customers.Create(context.TODO(), params)
params := &stripe.CustomerParams{...}
params.Context = context.TODO() 
sc.Customers.Get(params)
params := &stripe.CustomerRetrieveParams{...} 
sc.V1Customers.Retrieve(context.TODO(), params)
params := &stripe.CustomerParams{...}
params.Context = context.TODO() 
sc.Customers.Update(params)
params := &stripe.CustomerUpdateParams{...} 
sc.V1Customers.Update(context.TODO(), params)
params := &stripe.CustomerParams{...}
params.Context = context.TODO() 
sc.Customers.Del(params)
params := &stripe.CustomerDeleteParams{...} 
sc.V1Customers.Delete(context.TODO(), params)

List methods

OLD

i := sc.Customers.List(&stripe.CustomerListParams{})
for i.Next() {
	cust := i.Customer()
	// handle cust
}
if err := i.Err(); err != nil {
	// handle err
}

NEW

params := &stripe.CustomerListParams{}
for cust, err := range sc.V1Customers.List(context.TODO(), params) {
	// handle err
	// handle cust
}

Method-specific Params

All params are now method-specific. This prevents runtime errors where you pass the wrong params to the Stripe API.
Old

params := &stripe.CustomerParams{Name: stripe.String("Jenny Rosen")}
params.Context = context.TODO()
sc.Customers.Del("cus_123", params) // ❌ -- runtime error from Stripe: Name param not allowed

The general pattern is:

  • Old: {resource_name}{subresource_names?}Params
  • New: {resource_name}{method_name}{subresource_names?}Params

Old

params := &stripe.AppsSecretParams{
	Name:    stripe.String("sec_123"),
	Payload: stripe.String("very secret string"),
	Scope: &stripe.AppsSecretScopeParams{
		Type: stripe.String(stripe.AppsSecretScopeTypeAccount),
	},
}

New

params := &stripe.AppsSecretCreateParams{
	Name:    stripe.String("sec_123"),
	Payload: stripe.String("very secret string"),
	Scope: &stripe.AppsSecretCreateScopeParams{
		Type: stripe.String(stripe.AppsSecretScopeTypeAccount),
	},
}

Migrating from global configuration

In the global configuration pattern, instead of making calls to Stripe through a client, you set a global stripe.APIKey (as well as possibly other globally-configured options) and use package-level functions. For example:

stripe.APIKey = "sk_test_123"

cust, err := customer.New(params) // uses the global stripe.APIKey

Instead, to use stripe.Client, the above code would be written as:

sc := stripe.NewClient("sk_test_123")

cust, err := sc.V1Customers.Create(context.TODO(), params)

Each stripe.Client can be configured with a different API key, a different HTTP client, a different backend, etc., does not depend on global state, and is easy to mock/configure for testing. The global configuration pattern is not supported by new V2 APIs, and will eventually be deprecated.

The migration path is similar to that from client.API to stripe.Client. Here is how the CRUD methods change:

Old New
params := &stripe.CustomerParams{...}
params.Context = context.TODO() 
customer.New(params)
params := &stripe.CustomerCreateParams{...} 
sc.V1Customers.Create(context.TODO(), params)
params := &stripe.CustomerParams{...}
params.Context = context.TODO() 
customer.Get(params)
params := &stripe.CustomerRetrieveParams{...} 
sc.V1Customers.Retrieve(context.TODO(), params)
params := &stripe.CustomerParams{...}
params.Context = context.TODO() 
customer.Update(params)
params := &stripe.CustomerUpdateParams{...} 
sc.V1Customers.Update(context.TODO(), params)
params := &stripe.CustomerParams{...}
params.Context = context.TODO() 
customer.Del(params)
params := &stripe.CustomerDeleteParams{...} 
sc.V1Customers.Delete(context.TODO(), params)

And the list methods change as follows:

OLD

i := customer.List(&stripe.CustomerListParams{})
for i.Next() {
	cust := i.Customer()
	// handle cust
}
if err := i.Err(); err != nil {
	// handle err
}

NEW

params := &stripe.CustomerListParams{}
for cust, err := range sc.V1Customers.List(context.TODO(), params) {
	// handle err
	// handle cust
}
Clone this wiki locally