storj/satellite/payments/stripecoinpayments/stripemock.go
Kaloyan Raev a20e85824a satellite/payments: add STORJ amount and rate to Stripe TX metadata
Jira: https://storjlabs.atlassian.net/browse/USR-968

We want to keep track of the STORJ amount and exchange rate in the
metadata of Stripe Customer Balance Transaction to be able to generate
reports without the need of requesting CoinPayments for this info.

Change-Id: Ia93af95706cd2312cf688f044874495279fe8fa2
2020-07-22 11:57:21 +03:00

246 lines
6.7 KiB
Go

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package stripecoinpayments
import (
"errors"
"time"
"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/charge"
"github.com/stripe/stripe-go/customerbalancetransaction"
"github.com/stripe/stripe-go/form"
"github.com/stripe/stripe-go/invoice"
"github.com/stripe/stripe-go/invoiceitem"
"github.com/stripe/stripe-go/paymentmethod"
"storj.io/common/uuid"
)
// mockClient singleton of mockStripeClient.
//
// The satellite has a Core part and API part which mostly duplicate each
// other. Each of them have a StripeClient instance. This is not a problem in
// production, because the stripeClient implementation is stateless and calls
// the Web API of the same Stripe backend. But it is a problem in test
// environments as the mockStripeClient client is stateful - the data is stored
// in in-memory maps. Therefore, we need it to be a singleton, so the Core and
// API parts share the same state.
var mockClient StripeClient
// mockStripeClient Stripe client mock.
type mockStripeClient struct {
customers *mockCustomers
paymentMethods *mockPaymentMethods
invoices *mockInvoices
invoiceItems *mockInvoiceItems
customerBalanceTransactions *mockCustomerBalanceTransactions
charges *mockCharges
}
// NewStripeMock creates new Stripe client mock.
func NewStripeMock() StripeClient {
if mockClient == nil {
mockClient = &mockStripeClient{
customers: newMockCustomers(),
paymentMethods: &mockPaymentMethods{},
invoices: &mockInvoices{},
invoiceItems: &mockInvoiceItems{},
customerBalanceTransactions: newMockCustomerBalanceTransactions(),
charges: &mockCharges{},
}
}
return mockClient
}
func (m *mockStripeClient) Customers() StripeCustomers {
return m.customers
}
func (m *mockStripeClient) PaymentMethods() StripePaymentMethods {
return m.paymentMethods
}
func (m *mockStripeClient) Invoices() StripeInvoices {
return m.invoices
}
func (m *mockStripeClient) InvoiceItems() StripeInvoiceItems {
return m.invoiceItems
}
func (m *mockStripeClient) CustomerBalanceTransactions() StripeCustomerBalanceTransactions {
return m.customerBalanceTransactions
}
func (m *mockStripeClient) Charges() StripeCharges {
return m.charges
}
type mockCustomers struct {
customers []*stripe.Customer
}
func newMockCustomers() *mockCustomers {
return &mockCustomers{
customers: make([]*stripe.Customer, 0, 5),
}
}
func (m *mockCustomers) New(params *stripe.CustomerParams) (*stripe.Customer, error) {
uuid, err := uuid.New()
if err != nil {
return nil, err
}
customer := &stripe.Customer{
ID: uuid.String(),
Email: *params.Email,
InvoiceSettings: &stripe.CustomerInvoiceSettings{
DefaultPaymentMethod: &stripe.PaymentMethod{
ID: "pm_card_mastercard",
},
},
}
m.customers = append(m.customers, customer)
return customer, nil
}
func (m *mockCustomers) Get(id string, params *stripe.CustomerParams) (*stripe.Customer, error) {
for _, customer := range m.customers {
if id == customer.ID {
return customer, nil
}
}
return nil, errors.New("customer not found")
}
func (m *mockCustomers) Update(id string, params *stripe.CustomerParams) (*stripe.Customer, error) {
customer, err := m.Get(id, nil)
if err != nil {
return nil, err
}
if params == nil {
return customer, nil
}
if params.Metadata != nil {
customer.Metadata = params.Metadata
}
// TODO update customer with more params as necessary
return customer, nil
}
type mockPaymentMethods struct {
}
func (m *mockPaymentMethods) List(listParams *stripe.PaymentMethodListParams) *paymentmethod.Iter {
values := []interface{}{
&stripe.PaymentMethod{
ID: "pm_card_mastercard",
Card: &stripe.PaymentMethodCard{
ExpMonth: 12,
ExpYear: 2050,
Brand: "Mastercard",
Last4: "4444",
},
},
}
listMeta := stripe.ListMeta{
HasMore: false,
TotalCount: uint32(len(values)),
}
return &paymentmethod.Iter{Iter: stripe.GetIter(nil, func(*stripe.Params, *form.Values) ([]interface{}, stripe.ListMeta, error) {
return values, listMeta, nil
})}
}
func (m *mockPaymentMethods) New(params *stripe.PaymentMethodParams) (*stripe.PaymentMethod, error) {
return nil, nil
}
func (m *mockPaymentMethods) Attach(id string, params *stripe.PaymentMethodAttachParams) (*stripe.PaymentMethod, error) {
return nil, nil
}
func (m *mockPaymentMethods) Detach(id string, params *stripe.PaymentMethodDetachParams) (*stripe.PaymentMethod, error) {
return nil, nil
}
type mockInvoices struct {
}
func (m *mockInvoices) New(params *stripe.InvoiceParams) (*stripe.Invoice, error) {
return nil, nil
}
func (m *mockInvoices) List(listParams *stripe.InvoiceListParams) *invoice.Iter {
return &invoice.Iter{Iter: &stripe.Iter{}}
}
func (m *mockInvoices) FinalizeInvoice(id string, params *stripe.InvoiceFinalizeParams) (*stripe.Invoice, error) {
return nil, nil
}
type mockInvoiceItems struct {
}
func (m *mockInvoiceItems) New(params *stripe.InvoiceItemParams) (*stripe.InvoiceItem, error) {
return nil, nil
}
func (m *mockInvoiceItems) List(listParams *stripe.InvoiceItemListParams) *invoiceitem.Iter {
return &invoiceitem.Iter{Iter: &stripe.Iter{}}
}
type mockCustomerBalanceTransactions struct {
transactions map[string][]*stripe.CustomerBalanceTransaction
}
func newMockCustomerBalanceTransactions() *mockCustomerBalanceTransactions {
return &mockCustomerBalanceTransactions{
transactions: make(map[string][]*stripe.CustomerBalanceTransaction),
}
}
func (m *mockCustomerBalanceTransactions) New(params *stripe.CustomerBalanceTransactionParams) (*stripe.CustomerBalanceTransaction, error) {
tx := &stripe.CustomerBalanceTransaction{
Type: stripe.CustomerBalanceTransactionTypeAdjustment,
Amount: *params.Amount,
Description: *params.Description,
Metadata: params.Metadata,
Created: time.Now().Unix(),
}
m.transactions[*params.Customer] = append(m.transactions[*params.Customer], tx)
return tx, nil
}
func (m *mockCustomerBalanceTransactions) List(listParams *stripe.CustomerBalanceTransactionListParams) *customerbalancetransaction.Iter {
return &customerbalancetransaction.Iter{Iter: stripe.GetIter(listParams, func(p *stripe.Params, b *form.Values) ([]interface{}, stripe.ListMeta, error) {
txs := m.transactions[*listParams.Customer]
ret := make([]interface{}, len(txs))
for i, v := range txs {
ret[i] = v
}
listMeta := stripe.ListMeta{
TotalCount: uint32(len(txs)),
}
return ret, listMeta, nil
})}
}
type mockCharges struct {
}
func (m *mockCharges) List(listParams *stripe.ChargeListParams) *charge.Iter {
return &charge.Iter{Iter: &stripe.Iter{}}
}