Skip to content

Commit

Permalink
feat(payments): create and fetch root account resources for stripe
Browse files Browse the repository at this point in the history
  • Loading branch information
laouji committed Sep 26, 2024
1 parent 7dd1534 commit fded87d
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ import (
"github.com/formancehq/payments/internal/models"
)

const (
var (
rootAccountReference = "root"
)

// root account reference is internal so we don't pass it to Stripe API clients
func resolveAccount(ref *string) *string {
if *ref == rootAccountReference {
return nil
}
return ref
}

type AccountsState struct {
LastID string `json:"lastID,omitempty"`
InitFinished bool `json:"init_finished"`
LastID string `json:"lastID,omitempty"`
}

func (p *Plugin) fetchNextAccounts(ctx context.Context, req models.FetchNextAccountsRequest) (models.FetchNextAccountsResponse, error) {
Expand All @@ -25,9 +34,25 @@ func (p *Plugin) fetchNextAccounts(ctx context.Context, req models.FetchNextAcco
}
}

var accounts []models.PSPAccount
newState := AccountsState{}
rawAccounts, hasMore, err := p.client.GetAccounts(ctx, &oldState.LastID, PageLimit)
accounts := make([]models.PSPAccount, 0, req.PageSize)
if !oldState.InitFinished {
// create a root account if this is the first time this is being run
accounts = append(accounts, models.PSPAccount{
Name: &rootAccountReference,
Reference: rootAccountReference,
CreatedAt: time.Now().UTC(),
Raw: json.RawMessage("{}"),
Metadata: map[string]string{},
})
oldState.InitFinished = true
}

needed := req.PageSize - len(accounts)

newState := AccountsState{
InitFinished: oldState.InitFinished,
}
rawAccounts, hasMore, err := p.client.GetAccounts(ctx, &oldState.LastID, int64(needed))
if err != nil {
return models.FetchNextAccountsResponse{}, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ var _ = Describe("Stripe Plugin Accounts", func() {
var (
m *client.MockClient

pageSize int
sampleAccounts []*stripesdk.Account
)

BeforeEach(func() {
ctrl := gomock.NewController(GinkgoT())
m = client.NewMockClient(ctrl)
plg.SetClient(m)
pageSize = 20

sampleAccounts = make([]*stripesdk.Account, 0)
for i := 0; i < int(stripe.PageLimit); i++ {
for i := 0; i < pageSize-1; i++ {
sampleAccounts = append(sampleAccounts, &stripesdk.Account{
ID: fmt.Sprintf("some-reference-%d", i),
})
Expand All @@ -44,17 +46,20 @@ var _ = Describe("Stripe Plugin Accounts", func() {
})
It("fetches next accounts", func(ctx SpecContext) {
req := models.FetchNextAccountsRequest{
State: json.RawMessage(`{}`),
State: json.RawMessage(`{}`),
PageSize: pageSize,
}
m.EXPECT().GetAccounts(ctx, gomock.Any(), stripe.PageLimit).Return(
// pageSize passed to client is less when we generate a root account
m.EXPECT().GetAccounts(ctx, gomock.Any(), int64(pageSize-1)).Return(
sampleAccounts,
true,
nil,
)
res, err := plg.FetchNextAccounts(ctx, req)
Expect(err).To(BeNil())
Expect(res.HasMore).To(BeTrue())
Expect(res.Accounts).To(HaveLen(int(stripe.PageLimit)))
Expect(res.Accounts).To(HaveLen(req.PageSize))
Expect(res.Accounts[0].Reference).To(Equal("root"))

var state stripe.AccountsState

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@ func (p *Plugin) fetchNextBalances(ctx context.Context, req models.FetchNextBala
return models.FetchNextBalancesResponse{}, err
}

stripeAccount := from.Reference
if stripeAccount == rootAccountReference {
// special case for root account
stripeAccount = ""
}

balance, err := p.client.GetAccountBalances(ctx, &stripeAccount)
balance, err := p.client.GetAccountBalances(ctx, resolveAccount(&from.Reference))
if err != nil {
return models.FetchNextBalancesResponse{}, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ func (c *client) GetExternalAccounts(
filters.StartingAfter = lastID
}

// return 0 results because this endpoint cannot be used for root account
if accountID == nil {
return []*stripe.BankAccount{}, false, nil
}

itr := c.bankAccountClient.List(&stripe.BankAccountListParams{
Account: accountID,
ListParams: filters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (p *Plugin) fetchNextExternalAccounts(ctx context.Context, req models.Fetch
newState := AccountsState{}
var accounts []models.PSPAccount

rawAccounts, hasMore, err := p.client.GetExternalAccounts(ctx, &from.Reference, &oldState.LastID, PageLimit)
rawAccounts, hasMore, err := p.client.GetExternalAccounts(ctx, resolveAccount(&from.Reference), &oldState.LastID, int64(req.PageSize))
if err != nil {
return models.FetchNextExternalAccountsResponse{}, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var _ = Describe("Stripe Plugin ExternalAccounts", func() {
var (
m *client.MockClient

pageSize int
sampleExternalAccounts []*stripesdk.BankAccount
accRef string
created int64
Expand All @@ -36,10 +37,11 @@ var _ = Describe("Stripe Plugin ExternalAccounts", func() {
m = client.NewMockClient(ctrl)
plg.SetClient(m)

pageSize = 10
accRef = "baseAcc"
created = 1483565364
sampleExternalAccounts = make([]*stripesdk.BankAccount, 0)
for i := 0; i < int(stripe.PageLimit); i++ {
for i := 0; i < pageSize; i++ {
sampleExternalAccounts = append(sampleExternalAccounts, &stripesdk.BankAccount{
ID: fmt.Sprintf("some-reference-%d", i),
Account: &stripesdk.Account{Created: created},
Expand All @@ -51,16 +53,17 @@ var _ = Describe("Stripe Plugin ExternalAccounts", func() {
req := models.FetchNextExternalAccountsRequest{
FromPayload: json.RawMessage(fmt.Sprintf(`{"reference": "%s"}`, accRef)),
State: json.RawMessage(`{}`),
PageSize: pageSize,
}
m.EXPECT().GetExternalAccounts(ctx, &accRef, gomock.Any(), stripe.PageLimit).Return(
m.EXPECT().GetExternalAccounts(ctx, &accRef, gomock.Any(), int64(pageSize)).Return(
sampleExternalAccounts,
true,
nil,
)
res, err := plg.FetchNextExternalAccounts(ctx, req)
Expect(err).To(BeNil())
Expect(res.HasMore).To(BeTrue())
Expect(res.ExternalAccounts).To(HaveLen(int(stripe.PageLimit)))
Expect(res.ExternalAccounts).To(HaveLen(pageSize))

var state stripe.AccountsState

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (p *Plugin) fetchNextPayments(ctx context.Context, req models.FetchNextPaym

var payments []models.PSPPayment
newState := PaymentState{}
rawPayments, hasMore, err := p.client.GetPayments(ctx, &from.Reference, &oldState.LastID, PageLimit)
rawPayments, hasMore, err := p.client.GetPayments(ctx, resolveAccount(&from.Reference), &oldState.LastID, int64(req.PageSize))
if err != nil {
return models.FetchNextPaymentsResponse{}, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ var _ = Describe("Stripe Plugin Payments", func() {

samplePayments []*stripesdk.BalanceTransaction
accRef string
pageSize int
)

BeforeEach(func() {
ctrl := gomock.NewController(GinkgoT())
m = client.NewMockClient(ctrl)
plg.SetClient(m)

pageSize = 15
accRef = "baseAcc"
samplePayments = []*stripesdk.BalanceTransaction{
{
Expand Down Expand Up @@ -189,14 +191,15 @@ var _ = Describe("Stripe Plugin Payments", func() {
req := models.FetchNextPaymentsRequest{
FromPayload: json.RawMessage(fmt.Sprintf(`{"reference": "%s"}`, accRef)),
State: json.RawMessage(`{}`),
PageSize: pageSize,
}
p := []*stripesdk.BalanceTransaction{
{
ID: "someid",
Type: stripesdk.BalanceTransactionTypeAdjustment,
},
}
m.EXPECT().GetPayments(ctx, &accRef, gomock.Any(), stripe.PageLimit).Return(
m.EXPECT().GetPayments(ctx, &accRef, gomock.Any(), int64(pageSize)).Return(
p,
true,
nil,
Expand All @@ -210,6 +213,7 @@ var _ = Describe("Stripe Plugin Payments", func() {
req := models.FetchNextPaymentsRequest{
FromPayload: json.RawMessage(fmt.Sprintf(`{"reference": "%s"}`, accRef)),
State: json.RawMessage(`{}`),
PageSize: pageSize,
}
p := []*stripesdk.BalanceTransaction{
{
Expand All @@ -222,7 +226,7 @@ var _ = Describe("Stripe Plugin Payments", func() {
},
},
}
m.EXPECT().GetPayments(ctx, &accRef, gomock.Any(), stripe.PageLimit).Return(
m.EXPECT().GetPayments(ctx, &accRef, gomock.Any(), int64(pageSize)).Return(
p,
true,
nil,
Expand All @@ -236,8 +240,9 @@ var _ = Describe("Stripe Plugin Payments", func() {
req := models.FetchNextPaymentsRequest{
FromPayload: json.RawMessage(fmt.Sprintf(`{"reference": "%s"}`, accRef)),
State: json.RawMessage(`{}`),
PageSize: pageSize,
}
m.EXPECT().GetPayments(ctx, &accRef, gomock.Any(), stripe.PageLimit).Return(
m.EXPECT().GetPayments(ctx, &accRef, gomock.Any(), int64(pageSize)).Return(
samplePayments,
true,
nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
stripesdk "github.com/stripe/stripe-go/v79"
)

const PageLimit = int64(20)

type Plugin struct {
StripeAPIBackend stripesdk.Backend // override in tests to mock
client client.Client
Expand Down

0 comments on commit fded87d

Please sign in to comment.