a20e85824a
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
246 lines
6.7 KiB
Go
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{}}
|
|
}
|