From 501816770b68f2d95b0e51019c636b1c1966672d Mon Sep 17 00:00:00 2001 From: Yehor Butko Date: Tue, 15 Oct 2019 14:23:54 +0300 Subject: [PATCH] satellite/payments: receive credit cards (#3249) --- satellite/payments/account.go | 3 + satellite/payments/creditcards.go | 26 ++++++++ .../payments/stripecoinpayments/accounts.go | 56 +++++++++++++++++ .../stripecoinpayments/creditcards.go | 51 ++++++++++++++++ .../payments/stripecoinpayments/service.go | 61 ++++++------------- 5 files changed, 156 insertions(+), 41 deletions(-) create mode 100644 satellite/payments/creditcards.go create mode 100644 satellite/payments/stripecoinpayments/accounts.go create mode 100644 satellite/payments/stripecoinpayments/creditcards.go diff --git a/satellite/payments/account.go b/satellite/payments/account.go index 056f1409a..34c247e72 100644 --- a/satellite/payments/account.go +++ b/satellite/payments/account.go @@ -16,4 +16,7 @@ type Accounts interface { // Balance returns an integer amount in cents that represents the current balance of payment account. Balance(ctx context.Context, userID uuid.UUID) (int64, error) + + // CreditCards exposes all needed functionality to manage account credit cards. + CreditCards() CreditCards } diff --git a/satellite/payments/creditcards.go b/satellite/payments/creditcards.go new file mode 100644 index 000000000..a5de38343 --- /dev/null +++ b/satellite/payments/creditcards.go @@ -0,0 +1,26 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + +package payments + +import ( + "context" + + "github.com/skyrings/skyring-common/tools/uuid" +) + +// CreditCards exposes all needed functionality to manage account credit cards. +type CreditCards interface { + // List returns a list of PaymentMethods for a given Customer. + List(ctx context.Context, userID uuid.UUID) ([]CreditCard, error) +} + +// CreditCard holds all public information about credit card. +type CreditCard struct { + ID string + + ExpMonth int `json:"exp_month"` + ExpYear int `json:"exp_year"` + Brand string `json:"brand"` + Last4 string `json:"last4"` +} diff --git a/satellite/payments/stripecoinpayments/accounts.go b/satellite/payments/stripecoinpayments/accounts.go new file mode 100644 index 000000000..8aa9095d3 --- /dev/null +++ b/satellite/payments/stripecoinpayments/accounts.go @@ -0,0 +1,56 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + +package stripecoinpayments + +import ( + "context" + + "github.com/skyrings/skyring-common/tools/uuid" + "github.com/stripe/stripe-go" + + "storj.io/storj/satellite/payments" +) + +// accounts is an implementation of payments.Accounts. +type accounts struct { + service *Service +} + +// CreditCards exposes all needed functionality to manage account credit cards. +func (accounts *accounts) CreditCards() payments.CreditCards { + return &creditCards{service: accounts.service} +} + +// Setup creates a payment account for the user. +func (accounts *accounts) Setup(ctx context.Context, userID uuid.UUID, email string) (err error) { + defer mon.Task()(&ctx, userID, email)(&err) + + params := &stripe.CustomerParams{ + Email: stripe.String(email), + } + + if _, err := accounts.service.stripeClient.Customers.New(params); err != nil { + return ErrorStripe.Wrap(err) + } + + // TODO: delete customer from stripe, if db insertion fails + return accounts.service.customers.Insert(ctx, userID, email) +} + +// Balance returns an integer amount in cents that represents the current balance of payment account. +func (accounts *accounts) Balance(ctx context.Context, userID uuid.UUID) (_ int64, err error) { + defer mon.Task()(&ctx, userID)(&err) + + customerID, err := accounts.service.customers.GetCustomerID(ctx, userID) + if err != nil { + return 0, err + } + + c, err := accounts.service.stripeClient.Customers.Get(customerID, nil) + if err != nil { + return 0, ErrorStripe.Wrap(err) + } + + return c.Balance, nil +} diff --git a/satellite/payments/stripecoinpayments/creditcards.go b/satellite/payments/stripecoinpayments/creditcards.go new file mode 100644 index 000000000..39393b76b --- /dev/null +++ b/satellite/payments/stripecoinpayments/creditcards.go @@ -0,0 +1,51 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + +package stripecoinpayments + +import ( + "context" + + "github.com/skyrings/skyring-common/tools/uuid" + "github.com/stripe/stripe-go" + + "storj.io/storj/satellite/payments" +) + +// creditCards is an implementation of payments.CreditCards. +type creditCards struct { + service *Service +} + +// List returns a list of PaymentMethods for a given Customer. +func (creditCards *creditCards) List(ctx context.Context, userID uuid.UUID) (cards []payments.CreditCard, err error) { + defer mon.Task()(&ctx, userID)(&err) + + customerID, err := creditCards.service.customers.GetCustomerID(ctx, userID) + if err != nil { + return nil, err + } + + params := &stripe.PaymentMethodListParams{ + Customer: &customerID, + Type: stripe.String(string(stripe.PaymentMethodTypeCard)), + } + + paymentMethodsIterator := creditCards.service.stripeClient.PaymentMethods.List(params) + for paymentMethodsIterator.Next() { + stripeCard := paymentMethodsIterator.PaymentMethod() + + cards = append(cards, payments.CreditCard{ + ExpMonth: int(stripeCard.Card.ExpMonth), + ExpYear: int(stripeCard.Card.ExpYear), + Brand: string(stripeCard.Card.Brand), + Last4: stripeCard.Card.Last4, + }) + } + + if err = paymentMethodsIterator.Err(); err != nil { + return nil, ErrorStripe.Wrap(err) + } + + return cards, nil +} diff --git a/satellite/payments/stripecoinpayments/service.go b/satellite/payments/stripecoinpayments/service.go index f844edcf8..4ca0159b3 100644 --- a/satellite/payments/stripecoinpayments/service.go +++ b/satellite/payments/stripecoinpayments/service.go @@ -4,61 +4,40 @@ package stripecoinpayments import ( - "context" - - "github.com/skyrings/skyring-common/tools/uuid" - "github.com/stripe/stripe-go" - "github.com/stripe/stripe-go/customer" + "github.com/stripe/stripe-go/client" "github.com/zeebo/errs" "gopkg.in/spacemonkeygo/monkit.v2" + + "storj.io/storj/satellite/payments" ) var mon = monkit.Package() -// ErrorStripe is stripe error type +// ErrorStripe is stripe error type. var ErrorStripe = errs.Class("stripe API error") -// Service is an implementation for payment service via Stripe and Coinpayments +// Config stores needed information for payment service initialization +type Config struct { + secretKey string +} + +// Service is an implementation for payment service via Stripe and Coinpayments. type Service struct { - customers CustomersDB + customers CustomersDB + stripeClient *client.API } // NewService creates a Service instance. -func NewService(customers CustomersDB) *Service { +func NewService(config Config, customers CustomersDB) *Service { + stripeClient := client.New(config.secretKey, nil) + return &Service{ - customers: customers, + customers: customers, + stripeClient: stripeClient, } } -// Setup creates a payment account for the user. -func (service *Service) Setup(ctx context.Context, userID uuid.UUID, email string) (err error) { - defer mon.Task()(&ctx, userID, email)(&err) - - params := &stripe.CustomerParams{ - Email: stripe.String(email), - } - - if _, err := customer.New(params); err != nil { - return ErrorStripe.Wrap(err) - } - - // TODO: delete customer from stripe, if db insertion fails - return service.customers.Insert(ctx, userID, email) -} - -// Balance returns an integer amount in cents that represents the current balance of payment account. -func (service *Service) Balance(ctx context.Context, userID uuid.UUID) (_ int64, err error) { - defer mon.Task()(&ctx, userID)(&err) - - customerID, err := service.customers.GetCustomerID(ctx, userID) - if err != nil { - return 0, err - } - - c, err := customer.Get(customerID, nil) - if err != nil { - return 0, ErrorStripe.Wrap(err) - } - - return c.Balance, nil +// Accounts exposes all needed functionality to manage payment accounts. +func (service *Service) Accounts() payments.Accounts { + return &accounts{service: service} }