satellite/console: add credit card charges to billing history

Change-Id: I82a08c42c01086dc7fb9508da5c6c0baa2438124
This commit is contained in:
Yaroslav 2020-01-03 16:21:05 +02:00
parent 325790703f
commit 389567fc9e
8 changed files with 115 additions and 18 deletions

View File

@ -28,4 +28,6 @@ const (
Invoice BillingHistoryItemType = 0 Invoice BillingHistoryItemType = 0
// Transaction is a Coinpayments transaction billing item. // Transaction is a Coinpayments transaction billing item.
Transaction BillingHistoryItemType = 1 Transaction BillingHistoryItemType = 1
// Charge is a credit card charge billing item.
Charge BillingHistoryItemType = 2
) )

View File

@ -6,6 +6,7 @@ package console
import ( import (
"context" "context"
"crypto/subtle" "crypto/subtle"
"fmt"
"sort" "sort"
"time" "time"
@ -213,7 +214,7 @@ func (payments PaymentsService) BillingHistory(ctx context.Context) (billingHist
invoices, err := payments.service.accounts.Invoices().List(ctx, auth.User.ID) invoices, err := payments.service.accounts.Invoices().List(ctx, auth.User.ID)
if err != nil { if err != nil {
return nil, err return nil, Error.Wrap(err)
} }
// TODO: add transactions, etc in future // TODO: add transactions, etc in future
@ -232,12 +233,11 @@ func (payments PaymentsService) BillingHistory(ctx context.Context) (billingHist
txsInfos, err := payments.service.accounts.StorjTokens().ListTransactionInfos(ctx, auth.User.ID) txsInfos, err := payments.service.accounts.StorjTokens().ListTransactionInfos(ctx, auth.User.ID)
if err != nil { if err != nil {
return nil, err return nil, Error.Wrap(err)
} }
for _, info := range txsInfos { for _, info := range txsInfos {
billingHistory = append(billingHistory, billingHistory = append(billingHistory, &BillingHistoryItem{
&BillingHistoryItem{
ID: info.ID.String(), ID: info.ID.String(),
Description: "STORJ Token Deposit", Description: "STORJ Token Deposit",
Amount: info.AmountCents, Amount: info.AmountCents,
@ -247,8 +247,24 @@ func (payments PaymentsService) BillingHistory(ctx context.Context) (billingHist
Start: info.CreatedAt, Start: info.CreatedAt,
End: info.ExpiresAt, End: info.ExpiresAt,
Type: Transaction, Type: Transaction,
}, })
) }
charges, err := payments.service.accounts.Charges(ctx, auth.User.ID)
if err != nil {
return nil, Error.Wrap(err)
}
for _, charge := range charges {
desc := fmt.Sprintf("Payment(%s %s)", charge.CardInfo.Brand, charge.CardInfo.LastFour)
billingHistory = append(billingHistory, &BillingHistoryItem{
ID: charge.ID,
Description: desc,
Amount: charge.Amount,
Start: charge.CreatedAt,
Type: Charge,
})
} }
sort.SliceStable(billingHistory, sort.SliceStable(billingHistory,

View File

@ -25,6 +25,9 @@ type Accounts interface {
// ProjectCharges returns how much money current user will be charged for each project. // ProjectCharges returns how much money current user will be charged for each project.
ProjectCharges(ctx context.Context, userID uuid.UUID) ([]ProjectCharge, error) ProjectCharges(ctx context.Context, userID uuid.UUID) ([]ProjectCharge, error)
// Charges returns list of all credit card charges related to account.
Charges(ctx context.Context, userID uuid.UUID) ([]Charge, error)
// Coupons return list of all coupons of specified payment account. // Coupons return list of all coupons of specified payment account.
Coupons(ctx context.Context, userID uuid.UUID) ([]Coupon, error) Coupons(ctx context.Context, userID uuid.UUID) ([]Coupon, error)

View File

@ -0,0 +1,21 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package payments
import "time"
// CardInfo holds information about credit card used for charge.
type CardInfo struct {
ID string `json:"id"`
Brand string `json:"brand"`
LastFour string `json:"lastFour"`
}
// Charge contains charge details.
type Charge struct {
ID string `json:"id"`
Amount int64 `json:"amount"`
CardInfo CardInfo `json:"card"`
CreatedAt time.Time `json:"createdAt"`
}

View File

@ -82,6 +82,12 @@ func (accounts *accounts) ProjectCharges(ctx context.Context, userID uuid.UUID)
return []payments.ProjectCharge{}, nil return []payments.ProjectCharge{}, nil
} }
// Charges returns empty charges list.
func (accounts accounts) Charges(ctx context.Context, userID uuid.UUID) (_ []payments.Charge, err error) {
defer mon.Task()(&ctx, userID)(&err)
return []payments.Charge{}, nil
}
// Coupons return list of all coupons of specified payment account. // Coupons return list of all coupons of specified payment account.
func (accounts *accounts) Coupons(ctx context.Context, userID uuid.UUID) (coupons []payments.Coupon, err error) { func (accounts *accounts) Coupons(ctx context.Context, userID uuid.UUID) (coupons []payments.Coupon, err error) {
defer mon.Task()(&ctx, userID)(&err) defer mon.Task()(&ctx, userID)(&err)

View File

@ -117,6 +117,53 @@ func (accounts *accounts) ProjectCharges(ctx context.Context, userID uuid.UUID)
return charges, nil return charges, nil
} }
// Charges returns list of all credit card charges related to account.
func (accounts *accounts) Charges(ctx context.Context, userID uuid.UUID) (_ []payments.Charge, err error) {
defer mon.Task()(&ctx, userID)(&err)
customerID, err := accounts.service.db.Customers().GetCustomerID(ctx, userID)
if err != nil {
return nil, Error.Wrap(err)
}
params := &stripe.ChargeListParams{
Customer: stripe.String(customerID),
}
params.Filters.AddFilter("limit", "", "100")
iter := accounts.service.stripeClient.Charges.List(params)
var charges []payments.Charge
for iter.Next() {
charge := iter.Charge()
// ignore all non credit card charges
if charge.PaymentMethodDetails.Type != stripe.ChargePaymentMethodDetailsTypeCard {
continue
}
if charge.PaymentMethodDetails.Card == nil {
continue
}
charges = append(charges, payments.Charge{
ID: charge.ID,
Amount: charge.Amount,
CardInfo: payments.CardInfo{
ID: charge.PaymentMethod,
Brand: string(charge.PaymentMethodDetails.Card.Brand),
LastFour: charge.PaymentMethodDetails.Card.Last4,
},
CreatedAt: time.Unix(charge.Created, 0).UTC(),
})
}
if err = iter.Err(); err != nil {
return nil, Error.Wrap(err)
}
return charges, nil
}
// Coupons return list of all coupons of specified payment account. // Coupons return list of all coupons of specified payment account.
func (accounts *accounts) Coupons(ctx context.Context, userID uuid.UUID) (coupons []payments.Coupon, err error) { func (accounts *accounts) Coupons(ctx context.Context, userID uuid.UUID) (coupons []payments.Coupon, err error) {
defer mon.Task()(&ctx, userID)(&err) defer mon.Task()(&ctx, userID)(&err)

View File

@ -45,11 +45,11 @@ export default class BillingHistoryDate extends Vue {
} }
public get date(): string { public get date(): string {
if (this.type === BillingHistoryItemType.Transaction) { if (this.type === BillingHistoryItemType.Invoice) {
return this.start.toLocaleDateString(); return `${this.start.toLocaleDateString()} - ${this.expiration.toLocaleDateString()}`;
} }
return `${this.start.toLocaleDateString()} - ${this.expiration.toLocaleDateString()}`; return this.start.toLocaleDateString();
} }
public get seconds(): number { public get seconds(): number {

View File

@ -138,6 +138,8 @@ export enum BillingHistoryItemType {
Invoice = 0, Invoice = 0,
// Transaction is a Coinpayments transaction billing item. // Transaction is a Coinpayments transaction billing item.
Transaction = 1, Transaction = 1,
// Charge is a credit card charge billing item.
Charge = 2,
} }
// TokenDeposit holds public information about token deposit // TokenDeposit holds public information about token deposit