Skip to content

Commit 8caad57

Browse files
Add missing parameters to Card and BankAccount services (#2102)
* Add tests * Add generated code
1 parent 9a08760 commit 8caad57

File tree

6 files changed

+414
-62
lines changed

6 files changed

+414
-62
lines changed

bankaccount.go

Lines changed: 105 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package stripe
88

99
import (
1010
"encoding/json"
11+
"fmt"
1112
"github.com/stripe/stripe-go/v82/form"
1213
"strconv"
1314
)
@@ -250,15 +251,15 @@ const (
250251

251252
// Delete a specified external account for a given account.
252253
type BankAccountParams struct {
253-
Params `form:"*"`
254-
Customer *string `form:"-"` // Included in URL
254+
Params `form:"*"`
255255
// Token is a token referencing an external account like one returned from
256256
// Stripe.js.
257-
Token *string `form:"-"` // Included in URL
257+
Token *string `form:"-"` // Included in URL
258+
Customer *string `form:"-"` // Included in URL
258259
// Account is the identifier of the parent account under which bank
259260
// accounts are nested.
260261
Account *string `form:"-"` // Included in URL
261-
// The name of the person or business that owns the bank account.
262+
// The name of the person or business that owns the bank account. This field is required when attaching the bank account to a `Customer` object.
262263
AccountHolderName *string `form:"account_holder_name"`
263264
// The type of entity that holds the account. This can be either `individual` or `company`.
264265
AccountHolderType *string `form:"account_holder_type"`
@@ -282,7 +283,7 @@ type BankAccountParams struct {
282283
Country *string `form:"country"`
283284
// The currency the bank account is in. This must be a country/currency pairing that [Stripe supports](https://stripe.com/docs/payouts).
284285
Currency *string `form:"currency"`
285-
// When set to true, this becomes the default external account for its currency.
286+
// When set to true, or if this is the first external account added in this currency, this account becomes the default external account for its currency.
286287
DefaultForCurrency *bool `form:"default_for_currency"`
287288
// Documents that may be submitted to satisfy various informational requests.
288289
Documents *BankAccountDocumentsParams `form:"documents"`
@@ -314,7 +315,7 @@ type BankAccountParams struct {
314315
// either a source or external account.
315316
//
316317
// It may look like an AppendTo from the form package, but it's not, and is
317-
// only used in the special case where we use `bankaccount.New`. It's needed
318+
// only used in the special case where we create a new BankAccount. It's needed
318319
// because we have some weird encoding logic here that can't be handled by the
319320
// form package (and it's special enough that it wouldn't be desirable to have
320321
// it do so).
@@ -337,7 +338,7 @@ func (p *BankAccountParams) AppendToAsSourceOrExternalAccount(body *form.Values)
337338
sourceType = "external_account"
338339
}
339340

340-
// Use token (if exists) or a dictionary containing a users bank account details.
341+
// Use token (if exists) or a dictionary containing a user's bank account details.
341342
if p.Token != nil {
342343
body.Add(sourceType, StringValue(p.Token))
343344

@@ -419,8 +420,9 @@ func (p *BankAccountListParams) AppendTo(body *form.Values, keyParts []string) {
419420

420421
// Delete a specified external account for a given account.
421422
type BankAccountDeleteParams struct {
422-
Params `form:"*"`
423-
Account *string `form:"-"` // Included in URL
423+
Params `form:"*"`
424+
Customer *string `form:"-"` // Included in URL
425+
Account *string `form:"-"` // Included in URL
424426
}
425427

426428
// One or more documents that support the [Bank account ownership verification](https://support.stripe.com/questions/bank-account-ownership-verification) requirement. Must be a document associated with the bank account that displays the last 4 digits of the account number, either a statement or a check.
@@ -444,8 +446,9 @@ type BankAccountUpdateDocumentsParams struct {
444446
// You can re-enable a disabled bank account by performing an update call without providing any
445447
// arguments or changes.
446448
type BankAccountUpdateParams struct {
447-
Params `form:"*"`
448-
Account *string `form:"-"` // Included in URL
449+
Params `form:"*"`
450+
Customer *string `form:"-"` // Included in URL
451+
Account *string `form:"-"` // Included in URL
449452
// The name of the person or business that owns the bank account.
450453
AccountHolderName *string `form:"account_holder_name"`
451454
// The type of entity that holds the account. This can be either `individual` or `company`.
@@ -494,18 +497,38 @@ func (p *BankAccountUpdateParams) AddMetadata(key string, value string) {
494497
p.Metadata[key] = value
495498
}
496499

500+
// Documents that may be submitted to satisfy various informational requests.
501+
type BankAccountCreateDocumentsBankAccountOwnershipVerificationParams struct {
502+
Files []*string `form:"files"`
503+
}
504+
type BankAccountCreateDocumentsParams struct {
505+
// Documents that may be submitted to satisfy various informational requests.
506+
BankAccountOwnershipVerification *BankAccountCreateDocumentsBankAccountOwnershipVerificationParams `form:"bank_account_ownership_verification"`
507+
}
508+
497509
// Create creates a new bank account
498510
type BankAccountCreateParams struct {
499511
Params `form:"*"`
500512
Account *string `form:"-"` // Included in URL
501513
Customer *string `form:"-"` // Included in URL
502514
Token *string `form:"-"` // Included in URL
515+
// The name of the person or business that owns the bank account. This field is required when attaching the bank account to a `Customer` object.
516+
AccountHolderName *string `form:"account_holder_name"`
517+
// The type of entity that holds the account. This can be either `individual` or `company`.
518+
AccountHolderType *string `form:"account_holder_type"`
503519
// The account number for the bank account, in string form. Must be a checking account.
504520
AccountNumber *string `form:"account_number"`
505521
// The country in which the bank account is located.
506522
Country *string `form:"country"`
507523
// The currency the bank account is in. This must be a country/currency pairing that [Stripe supports](https://stripe.com/docs/payouts).
508524
Currency *string `form:"currency"`
525+
// When set to true, or if this is the first external account added in this currency, this account becomes the default external account for its currency.
526+
DefaultForCurrency *bool `form:"default_for_currency"`
527+
Documents *BankAccountCreateDocumentsParams `form:"documents"`
528+
// Specifies which fields in the response should be expanded.
529+
Expand []*string `form:"expand"`
530+
// Set of [key-value pairs](https://stripe.com/docs/api/metadata) that you can attach to an object. This can be useful for storing additional information about the object in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to `metadata`.
531+
Metadata map[string]string `form:"metadata"`
509532
// The ID of a Payment Method with a `type` of `us_bank_account`. The Payment Method's bank account information will be copied and
510533
// returned as a Bank Account Token. This parameter is exclusive with respect to all other parameters in the `bank_account` hash.
511534
// You must include the top-level `customer` parameter if the Payment Method is attached to a `Customer` object. If the Payment
@@ -518,12 +541,41 @@ type BankAccountCreateParams struct {
518541
RoutingNumber *string `form:"routing_number"`
519542
}
520543

521-
// AppendToAsSourceOrExternalAccount appends the given BankAccountCreateParams as either a source or external account.
522-
func (p *BankAccountCreateParams) AppendToAsSourceOrExternalAccount(body *form.Values) {
544+
// AppendToAsSourceOrExternalAccount appends the given BankAccountCreateParams as
545+
// either a source or external account.
546+
//
547+
// It may look like an AppendTo from the form package, but it's not, and is
548+
// only used in the special case where we create a new BankAccount. It's needed
549+
// because we have some weird encoding logic here that can't be handled by the
550+
// form package (and it's special enough that it wouldn't be desirable to have
551+
// it do so).
552+
//
553+
// This is not a pattern that we want to push forward, and this largely exists
554+
// because the bank accounts endpoint is a little unusual. There is one other
555+
// resource like it, which is cards.
556+
func (p *BankAccountCreateParams) AppendToAsSourceOrExternalAccount(body *form.Values) error {
523557
// Rather than being called in addition to `AppendTo`, this function
524558
// *replaces* `AppendTo`, so we must also make sure to handle the encoding
525559
// of `Params` so metadata and the like is included in the encoded payload.
526560
form.AppendTo(body, p.Params)
561+
if p.Metadata != nil && p.Params.Metadata != nil {
562+
return fmt.Errorf(
563+
"you cannot specify both the (deprecated) .Params.Metadata and .Metadata in `BankAccountCreateParams`")
564+
}
565+
if p.Expand != nil && p.Params.Expand != nil {
566+
return fmt.Errorf(
567+
"you cannot specify both the (deprecated) .Params.Expand and .Expand in `BankAccountCreateParams`")
568+
}
569+
if p.Metadata != nil {
570+
for k, v := range p.Metadata {
571+
body.Add("metadata["+k+"]", v)
572+
}
573+
}
574+
if p.Expand != nil {
575+
for _, v := range p.Expand {
576+
body.Add("expand[]", StringValue(v))
577+
}
578+
}
527579

528580
isCustomer := p.Customer != nil
529581

@@ -534,25 +586,62 @@ func (p *BankAccountCreateParams) AppendToAsSourceOrExternalAccount(body *form.V
534586
sourceType = "external_account"
535587
}
536588

537-
// Use token (if exists) or a dictionary containing a users bank account details.
589+
// Use token (if exists) or a dictionary containing a user's bank account details.
538590
if p.Token != nil {
539591
body.Add(sourceType, StringValue(p.Token))
592+
593+
if p.DefaultForCurrency != nil {
594+
body.Add(
595+
"default_for_currency", strconv.FormatBool(
596+
BoolValue(p.DefaultForCurrency)))
597+
}
540598
} else {
541599
body.Add(sourceType+"[object]", "bank_account")
542600
body.Add(sourceType+"[country]", StringValue(p.Country))
543601
body.Add(sourceType+"[account_number]", StringValue(p.AccountNumber))
544602
body.Add(sourceType+"[currency]", StringValue(p.Currency))
545603

604+
// These are optional and the API will fail if we try to send empty
605+
// values in for them, so make sure to check that they're actually set
606+
// before encoding them.
607+
if p.AccountHolderName != nil {
608+
body.Add(sourceType+"[account_holder_name]", StringValue(p.AccountHolderName))
609+
}
610+
611+
if p.AccountHolderType != nil {
612+
body.Add(sourceType+"[account_holder_type]", StringValue(p.AccountHolderType))
613+
}
614+
546615
if p.RoutingNumber != nil {
547616
body.Add(sourceType+"[routing_number]", StringValue(p.RoutingNumber))
548617
}
618+
619+
if p.DefaultForCurrency != nil {
620+
body.Add(sourceType+"[default_for_currency]", strconv.FormatBool(BoolValue(p.DefaultForCurrency)))
621+
}
622+
}
623+
return nil
624+
}
625+
626+
// AddExpand appends a new field to expand.
627+
func (p *BankAccountCreateParams) AddExpand(f string) {
628+
p.Expand = append(p.Expand, &f)
629+
}
630+
631+
// AddMetadata adds a new key-value pair to the Metadata.
632+
func (p *BankAccountCreateParams) AddMetadata(key string, value string) {
633+
if p.Metadata == nil {
634+
p.Metadata = make(map[string]string)
549635
}
636+
637+
p.Metadata[key] = value
550638
}
551639

552640
// Get returns the details of a bank account.
553641
type BankAccountRetrieveParams struct {
554-
Params `form:"*"`
555-
Account *string `form:"-"` // Included in URL
642+
Params `form:"*"`
643+
Customer *string `form:"-"` // Included in URL
644+
Account *string `form:"-"` // Included in URL
556645
}
557646

558647
// Fields that are `currently_due` and need to be collected again because validation or verification failed.

bankaccount_service.go

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ type v1BankAccountService struct {
2222

2323
// Create creates a new bank account
2424
func (c v1BankAccountService) Create(ctx context.Context, params *BankAccountCreateParams) (*BankAccount, error) {
25+
if params == nil {
26+
return nil, fmt.Errorf("params should not be nil")
27+
}
28+
2529
var path string
26-
if params == nil || (params.Account != nil && params.Customer != nil) || (params.Account == nil && params.Customer == nil) {
30+
if (params.Account != nil && params.Customer != nil) || (params.Account == nil && params.Customer == nil) {
2731
return nil, fmt.Errorf("Invalid bank account params: exactly one of Account or Customer need to be set")
2832
} else if params.Account != nil {
2933
path = FormatURLPath("/v1/accounts/%s/external_accounts", StringValue(params.Account))
@@ -36,7 +40,9 @@ func (c v1BankAccountService) Create(ctx context.Context, params *BankAccountCre
3640
// Note that we call this special append method instead of the standard one
3741
// from the form package. We should not use form's because doing so will
3842
// include some parameters that are undesirable here.
39-
params.AppendToAsSourceOrExternalAccount(body)
43+
if err := params.AppendToAsSourceOrExternalAccount(body); err != nil {
44+
return nil, err
45+
}
4046

4147
// Because bank account creation uses the custom append above, we have to
4248
// make an explicit call using a form and CallRaw instead of the standard
@@ -48,12 +54,18 @@ func (c v1BankAccountService) Create(ctx context.Context, params *BankAccountCre
4854

4955
// Get returns the details of a bank account.
5056
func (c v1BankAccountService) Retrieve(ctx context.Context, id string, params *BankAccountRetrieveParams) (*BankAccount, error) {
51-
if params == nil || params.Account == nil {
52-
return nil, fmt.Errorf("invalid bank account params: Account is required")
57+
if params == nil {
58+
return nil, fmt.Errorf("params should not be nil")
59+
}
60+
var path string
61+
if (params.Account != nil && params.Customer != nil) || (params.Account == nil && params.Customer == nil) {
62+
return nil, fmt.Errorf("Invalid bank account params: exactly one of Account or Customer need to be set")
63+
} else if params.Account != nil {
64+
path = FormatURLPath("/v1/accounts/%s/external_accounts/%s", StringValue(params.Account), id)
65+
} else if params.Customer != nil {
66+
path = FormatURLPath("/v1/customers/%s/sources/%s", StringValue(params.Customer), id)
5367
}
5468
params.Context = ctx
55-
path := FormatURLPath(
56-
"/v1/accounts/%s/external_accounts/%s", StringValue(params.Account), id)
5769
bankaccount := &BankAccount{}
5870
err := c.B.Call(http.MethodGet, path, c.Key, params, bankaccount)
5971
return bankaccount, err
@@ -68,25 +80,37 @@ func (c v1BankAccountService) Retrieve(ctx context.Context, id string, params *B
6880
// You can re-enable a disabled bank account by performing an update call without providing any
6981
// arguments or changes.
7082
func (c v1BankAccountService) Update(ctx context.Context, id string, params *BankAccountUpdateParams) (*BankAccount, error) {
71-
if params == nil || params.Account == nil {
72-
return nil, fmt.Errorf("invalid bank account params: Account is required")
83+
if params == nil {
84+
return nil, fmt.Errorf("params should not be nil")
85+
}
86+
var path string
87+
if (params.Account != nil && params.Customer != nil) || (params.Account == nil && params.Customer == nil) {
88+
return nil, fmt.Errorf("Invalid bank account params: exactly one of Account or Customer need to be set")
89+
} else if params.Account != nil {
90+
path = FormatURLPath("/v1/accounts/%s/external_accounts/%s", StringValue(params.Account), id)
91+
} else if params.Customer != nil {
92+
path = FormatURLPath("/v1/customers/%s/sources/%s", StringValue(params.Customer), id)
7393
}
7494
params.Context = ctx
75-
path := FormatURLPath(
76-
"/v1/accounts/%s/external_accounts/%s", StringValue(params.Account), id)
7795
bankaccount := &BankAccount{}
7896
err := c.B.Call(http.MethodPost, path, c.Key, params, bankaccount)
7997
return bankaccount, err
8098
}
8199

82100
// Delete a specified external account for a given account.
83101
func (c v1BankAccountService) Delete(ctx context.Context, id string, params *BankAccountDeleteParams) (*BankAccount, error) {
84-
if params == nil || params.Account == nil {
85-
return nil, fmt.Errorf("invalid bank account params: Account is required")
102+
if params == nil {
103+
return nil, fmt.Errorf("params should not be nil")
104+
}
105+
var path string
106+
if (params.Account != nil && params.Customer != nil) || (params.Account == nil && params.Customer == nil) {
107+
return nil, fmt.Errorf("Invalid bank account params: exactly one of Account or Customer need to be set")
108+
} else if params.Account != nil {
109+
path = FormatURLPath("/v1/accounts/%s/external_accounts/%s", StringValue(params.Account), id)
110+
} else if params.Customer != nil {
111+
path = FormatURLPath("/v1/customers/%s/sources/%s", StringValue(params.Customer), id)
86112
}
87113
params.Context = ctx
88-
path := FormatURLPath(
89-
"/v1/accounts/%s/external_accounts/%s", StringValue(params.Account), id)
90114
bankaccount := &BankAccount{}
91115
err := c.B.Call(http.MethodDelete, path, c.Key, params, bankaccount)
92116
return bankaccount, err

bankaccount_service_test.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,29 @@ func TestBankAccountDelete_ByAccount(t *testing.T) {
1818
assert.NotNil(t, bankAccount)
1919
}
2020

21+
func TestBankAccountDelete_ByCustomer(t *testing.T) {
22+
sc := stripe.NewClient(TestAPIKey)
23+
bankAccount, err := sc.V1BankAccounts.Delete(context.TODO(), "ba_123", &stripe.BankAccountDeleteParams{
24+
Customer: stripe.String("cus_123"),
25+
})
26+
assert.Nil(t, err)
27+
assert.NotNil(t, bankAccount)
28+
}
29+
2130
func TestBankAccountRetrieve_ByAccount(t *testing.T) {
2231
sc := stripe.NewClient(TestAPIKey)
2332
bankAccount, err := sc.V1BankAccounts.Retrieve(context.TODO(), "ba_123", &stripe.BankAccountRetrieveParams{Account: stripe.String("acct_123")})
2433
assert.Nil(t, err)
2534
assert.NotNil(t, bankAccount)
2635
}
2736

37+
func TestBankAccountRetrieve_ByCustomer(t *testing.T) {
38+
sc := stripe.NewClient(TestAPIKey)
39+
bankAccount, err := sc.V1BankAccounts.Retrieve(context.TODO(), "ba_123", &stripe.BankAccountRetrieveParams{Customer: stripe.String("cus_123")})
40+
assert.Nil(t, err)
41+
assert.NotNil(t, bankAccount)
42+
}
43+
2844
func TestBankAccountList_ByAccount(t *testing.T) {
2945
sc := stripe.NewClient(TestAPIKey)
3046
i := sc.V1BankAccounts.List(context.TODO(), &stripe.BankAccountListParams{Account: stripe.String("acct_123")})
@@ -48,8 +64,12 @@ func TestBankAccountList_ByCustomer(t *testing.T) {
4864
func TestBankAccountCreate_ByAccount(t *testing.T) {
4965
sc := stripe.NewClient(TestAPIKey)
5066
bankAccount, err := sc.V1BankAccounts.Create(context.TODO(), &stripe.BankAccountCreateParams{
51-
Account: stripe.String("acct_123"),
52-
Token: stripe.String("tok_123"),
67+
Account: stripe.String("acct_123"),
68+
Token: stripe.String("tok_123"),
69+
DefaultForCurrency: stripe.Bool(false),
70+
Metadata: map[string]string{
71+
"key": "value",
72+
},
5373
})
5474
assert.Nil(t, err)
5575
assert.NotNil(t, bankAccount)
@@ -60,6 +80,19 @@ func TestBankAccountCreate_ByCustomer(t *testing.T) {
6080
bankAccount, err := sc.V1BankAccounts.Create(context.TODO(), &stripe.BankAccountCreateParams{
6181
Customer: stripe.String("cus_123"),
6282
Token: stripe.String("tok_123"),
83+
Metadata: map[string]string{
84+
"key": "value",
85+
},
86+
})
87+
assert.Nil(t, err)
88+
assert.NotNil(t, bankAccount)
89+
}
90+
91+
func TestBankAccountUpdate_ByCustomer(t *testing.T) {
92+
sc := stripe.NewClient(TestAPIKey)
93+
bankAccount, err := sc.V1BankAccounts.Update(context.TODO(), "ba_123", &stripe.BankAccountUpdateParams{
94+
Customer: stripe.String("cus_123"),
95+
AccountHolderName: stripe.String("Jenny Rosen"),
6396
})
6497
assert.Nil(t, err)
6598
assert.NotNil(t, bankAccount)
@@ -68,8 +101,8 @@ func TestBankAccountCreate_ByCustomer(t *testing.T) {
68101
func TestBankAccountUpdate_ByAccount(t *testing.T) {
69102
sc := stripe.NewClient(TestAPIKey)
70103
bankAccount, err := sc.V1BankAccounts.Update(context.TODO(), "ba_123", &stripe.BankAccountUpdateParams{
71-
Account: stripe.String("acct_123"),
72-
DefaultForCurrency: stripe.Bool(true),
104+
Account: stripe.String("acct_123"),
105+
AccountHolderName: stripe.String("Jenny Rosen"),
73106
})
74107
assert.Nil(t, err)
75108
assert.NotNil(t, bankAccount)

0 commit comments

Comments
 (0)