satellite/payments: invoice creation (#3468)

This commit is contained in:
Yaroslav Vorobiov 2019-11-05 15:16:02 +02:00 committed by Yehor Butko
parent 9c59efd33d
commit 35edc2bcc3
36 changed files with 2806 additions and 110 deletions

View File

@ -12,6 +12,7 @@ import (
"os"
"strconv"
"strings"
"time"
prompt "github.com/segmentio/go-prompt"
"github.com/spf13/cobra"
@ -79,15 +80,36 @@ var (
Args: cobra.MinimumNArgs(4),
RunE: SegmentHealth,
}
paymentsCmd = &cobra.Command{
Use: "payments",
Short: "commands for payments",
}
prepareInvoiceRecordsCmd = &cobra.Command{
Use: "prepare-invoice-records <period>",
Short: "Prepares invoice project records that will be used during invoice line items creation",
Args: cobra.MinimumNArgs(1),
RunE: prepareInvoiceRecords,
}
createInvoiceItemsCmd = &cobra.Command{
Use: "create-invoice-items",
Short: "Creates stripe invoice line items for not consumed project records",
RunE: createInvoiceItems,
}
createInvoicesCmd = &cobra.Command{
Use: "create-invoices",
Short: "Creates stripe invoices for all stripe customers known to satellite",
RunE: createInvoices,
}
)
// Inspector gives access to overlay.
type Inspector struct {
conn *rpc.Conn
identity *identity.FullIdentity
overlayclient rpc.OverlayInspectorClient
irrdbclient rpc.IrreparableInspectorClient
healthclient rpc.HealthInspectorClient
conn *rpc.Conn
identity *identity.FullIdentity
overlayclient rpc.OverlayInspectorClient
irrdbclient rpc.IrreparableInspectorClient
healthclient rpc.HealthInspectorClient
paymentsClient rpc.PaymentsClient
}
// NewInspector creates a new gRPC inspector client for access to overlay.
@ -108,11 +130,12 @@ func NewInspector(address, path string) (*Inspector, error) {
}
return &Inspector{
conn: conn,
identity: id,
overlayclient: conn.OverlayInspectorClient(),
irrdbclient: conn.IrreparableInspectorClient(),
healthclient: conn.HealthInspectorClient(),
conn: conn,
identity: id,
overlayclient: conn.OverlayInspectorClient(),
irrdbclient: conn.IrreparableInspectorClient(),
healthclient: conn.HealthInspectorClient(),
paymentsClient: conn.PaymentsClient(),
}, nil
}
@ -423,14 +446,104 @@ func sortSegments(segments []*pb.IrreparableSegment) map[string][]*pb.Irreparabl
return objects
}
func prepareInvoiceRecords(cmd *cobra.Command, args []string) error {
i, err := NewInspector(*Addr, *IdentityPath)
if err != nil {
return ErrInspectorDial.Wrap(err)
}
defer func() { err = errs.Combine(err, i.Close()) }()
period, err := parseDateString(args[0])
if err != nil {
return ErrArgs.New("invalid period specified: %v", err)
}
_, err = i.paymentsClient.PrepareInvoiceRecords(context.Background(),
&pb.PrepareInvoiceRecordsRequest{
Period: period,
},
)
if err != nil {
return err
}
fmt.Println("successfully created invoice project records")
return nil
}
func createInvoiceItems(cmd *cobra.Command, args []string) error {
i, err := NewInspector(*Addr, *IdentityPath)
if err != nil {
return ErrInspectorDial.Wrap(err)
}
defer func() { err = errs.Combine(err, i.Close()) }()
_, err = i.paymentsClient.ApplyInvoiceRecords(context.Background(), &pb.ApplyInvoiceRecordsRequest{})
if err != nil {
return err
}
fmt.Println("successfully created invoice line items")
return nil
}
func createInvoices(cmd *cobra.Command, args []string) error {
i, err := NewInspector(*Addr, *IdentityPath)
if err != nil {
return ErrInspectorDial.Wrap(err)
}
defer func() { err = errs.Combine(err, i.Close()) }()
_, err = i.paymentsClient.CreateInvoices(context.Background(), &pb.CreateInvoicesRequest{})
if err != nil {
return err
}
fmt.Println("successfully created invoices")
return nil
}
// parseDateString parses provided date string and returns corresponding time.Time.
func parseDateString(s string) (time.Time, error) {
values := strings.Split(s, "/")
if len(values) != 2 {
return time.Time{}, errs.New("invalid date format %s, use mm/yyyy", s)
}
month, err := strconv.ParseInt(values[0], 10, 64)
if err != nil {
return time.Time{}, errs.New("can not parse month: %v", err)
}
year, err := strconv.ParseInt(values[1], 10, 64)
if err != nil {
return time.Time{}, errs.New("can not parse year: %v", err)
}
date := time.Date(int(year), time.Month(month), 1, 0, 0, 0, 0, time.UTC)
if date.Year() != int(year) || date.Month() != time.Month(month) || date.Day() != 1 {
return date, errs.New("dates mismatch have %s result %s", s, date)
}
return date, nil
}
func init() {
rootCmd.AddCommand(statsCmd)
rootCmd.AddCommand(irreparableCmd)
rootCmd.AddCommand(healthCmd)
rootCmd.AddCommand(paymentsCmd)
healthCmd.AddCommand(objectHealthCmd)
healthCmd.AddCommand(segmentHealthCmd)
paymentsCmd.AddCommand(prepareInvoiceRecordsCmd)
paymentsCmd.AddCommand(createInvoiceItemsCmd)
paymentsCmd.AddCommand(createInvoicesCmd)
objectHealthCmd.Flags().StringVar(&CSVPath, "csv-path", "stdout", "csv path where command output is written")
irreparableCmd.Flags().Int32Var(&irreparableLimit, "limit", 50, "max number of results per page")

528
pkg/pb/payments.pb.go Normal file
View File

@ -0,0 +1,528 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: payments.proto
package pb
import (
context "context"
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
_ "github.com/golang/protobuf/ptypes/timestamp"
grpc "google.golang.org/grpc"
math "math"
drpc "storj.io/drpc"
time "time"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
var _ = time.Kitchen
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type PrepareInvoiceRecordsRequest struct {
Period time.Time `protobuf:"bytes,1,opt,name=period,proto3,stdtime" json:"period"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PrepareInvoiceRecordsRequest) Reset() { *m = PrepareInvoiceRecordsRequest{} }
func (m *PrepareInvoiceRecordsRequest) String() string { return proto.CompactTextString(m) }
func (*PrepareInvoiceRecordsRequest) ProtoMessage() {}
func (*PrepareInvoiceRecordsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_a9566e6e864d2854, []int{0}
}
func (m *PrepareInvoiceRecordsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PrepareInvoiceRecordsRequest.Unmarshal(m, b)
}
func (m *PrepareInvoiceRecordsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PrepareInvoiceRecordsRequest.Marshal(b, m, deterministic)
}
func (m *PrepareInvoiceRecordsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PrepareInvoiceRecordsRequest.Merge(m, src)
}
func (m *PrepareInvoiceRecordsRequest) XXX_Size() int {
return xxx_messageInfo_PrepareInvoiceRecordsRequest.Size(m)
}
func (m *PrepareInvoiceRecordsRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PrepareInvoiceRecordsRequest.DiscardUnknown(m)
}
var xxx_messageInfo_PrepareInvoiceRecordsRequest proto.InternalMessageInfo
func (m *PrepareInvoiceRecordsRequest) GetPeriod() time.Time {
if m != nil {
return m.Period
}
return time.Time{}
}
type PrepareInvoiceRecordsResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PrepareInvoiceRecordsResponse) Reset() { *m = PrepareInvoiceRecordsResponse{} }
func (m *PrepareInvoiceRecordsResponse) String() string { return proto.CompactTextString(m) }
func (*PrepareInvoiceRecordsResponse) ProtoMessage() {}
func (*PrepareInvoiceRecordsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_a9566e6e864d2854, []int{1}
}
func (m *PrepareInvoiceRecordsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PrepareInvoiceRecordsResponse.Unmarshal(m, b)
}
func (m *PrepareInvoiceRecordsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PrepareInvoiceRecordsResponse.Marshal(b, m, deterministic)
}
func (m *PrepareInvoiceRecordsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_PrepareInvoiceRecordsResponse.Merge(m, src)
}
func (m *PrepareInvoiceRecordsResponse) XXX_Size() int {
return xxx_messageInfo_PrepareInvoiceRecordsResponse.Size(m)
}
func (m *PrepareInvoiceRecordsResponse) XXX_DiscardUnknown() {
xxx_messageInfo_PrepareInvoiceRecordsResponse.DiscardUnknown(m)
}
var xxx_messageInfo_PrepareInvoiceRecordsResponse proto.InternalMessageInfo
type ApplyInvoiceRecordsRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ApplyInvoiceRecordsRequest) Reset() { *m = ApplyInvoiceRecordsRequest{} }
func (m *ApplyInvoiceRecordsRequest) String() string { return proto.CompactTextString(m) }
func (*ApplyInvoiceRecordsRequest) ProtoMessage() {}
func (*ApplyInvoiceRecordsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_a9566e6e864d2854, []int{2}
}
func (m *ApplyInvoiceRecordsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ApplyInvoiceRecordsRequest.Unmarshal(m, b)
}
func (m *ApplyInvoiceRecordsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ApplyInvoiceRecordsRequest.Marshal(b, m, deterministic)
}
func (m *ApplyInvoiceRecordsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ApplyInvoiceRecordsRequest.Merge(m, src)
}
func (m *ApplyInvoiceRecordsRequest) XXX_Size() int {
return xxx_messageInfo_ApplyInvoiceRecordsRequest.Size(m)
}
func (m *ApplyInvoiceRecordsRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ApplyInvoiceRecordsRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ApplyInvoiceRecordsRequest proto.InternalMessageInfo
type ApplyInvoiceRecordsResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ApplyInvoiceRecordsResponse) Reset() { *m = ApplyInvoiceRecordsResponse{} }
func (m *ApplyInvoiceRecordsResponse) String() string { return proto.CompactTextString(m) }
func (*ApplyInvoiceRecordsResponse) ProtoMessage() {}
func (*ApplyInvoiceRecordsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_a9566e6e864d2854, []int{3}
}
func (m *ApplyInvoiceRecordsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ApplyInvoiceRecordsResponse.Unmarshal(m, b)
}
func (m *ApplyInvoiceRecordsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ApplyInvoiceRecordsResponse.Marshal(b, m, deterministic)
}
func (m *ApplyInvoiceRecordsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ApplyInvoiceRecordsResponse.Merge(m, src)
}
func (m *ApplyInvoiceRecordsResponse) XXX_Size() int {
return xxx_messageInfo_ApplyInvoiceRecordsResponse.Size(m)
}
func (m *ApplyInvoiceRecordsResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ApplyInvoiceRecordsResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ApplyInvoiceRecordsResponse proto.InternalMessageInfo
type CreateInvoicesRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CreateInvoicesRequest) Reset() { *m = CreateInvoicesRequest{} }
func (m *CreateInvoicesRequest) String() string { return proto.CompactTextString(m) }
func (*CreateInvoicesRequest) ProtoMessage() {}
func (*CreateInvoicesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_a9566e6e864d2854, []int{4}
}
func (m *CreateInvoicesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CreateInvoicesRequest.Unmarshal(m, b)
}
func (m *CreateInvoicesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CreateInvoicesRequest.Marshal(b, m, deterministic)
}
func (m *CreateInvoicesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CreateInvoicesRequest.Merge(m, src)
}
func (m *CreateInvoicesRequest) XXX_Size() int {
return xxx_messageInfo_CreateInvoicesRequest.Size(m)
}
func (m *CreateInvoicesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CreateInvoicesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CreateInvoicesRequest proto.InternalMessageInfo
type CreateInvoicesResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CreateInvoicesResponse) Reset() { *m = CreateInvoicesResponse{} }
func (m *CreateInvoicesResponse) String() string { return proto.CompactTextString(m) }
func (*CreateInvoicesResponse) ProtoMessage() {}
func (*CreateInvoicesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_a9566e6e864d2854, []int{5}
}
func (m *CreateInvoicesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CreateInvoicesResponse.Unmarshal(m, b)
}
func (m *CreateInvoicesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CreateInvoicesResponse.Marshal(b, m, deterministic)
}
func (m *CreateInvoicesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CreateInvoicesResponse.Merge(m, src)
}
func (m *CreateInvoicesResponse) XXX_Size() int {
return xxx_messageInfo_CreateInvoicesResponse.Size(m)
}
func (m *CreateInvoicesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CreateInvoicesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CreateInvoicesResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*PrepareInvoiceRecordsRequest)(nil), "nodestats.PrepareInvoiceRecordsRequest")
proto.RegisterType((*PrepareInvoiceRecordsResponse)(nil), "nodestats.PrepareInvoiceRecordsResponse")
proto.RegisterType((*ApplyInvoiceRecordsRequest)(nil), "nodestats.ApplyInvoiceRecordsRequest")
proto.RegisterType((*ApplyInvoiceRecordsResponse)(nil), "nodestats.ApplyInvoiceRecordsResponse")
proto.RegisterType((*CreateInvoicesRequest)(nil), "nodestats.CreateInvoicesRequest")
proto.RegisterType((*CreateInvoicesResponse)(nil), "nodestats.CreateInvoicesResponse")
}
func init() { proto.RegisterFile("payments.proto", fileDescriptor_a9566e6e864d2854) }
var fileDescriptor_a9566e6e864d2854 = []byte{
// 288 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2b, 0x48, 0xac, 0xcc,
0x4d, 0xcd, 0x2b, 0x29, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xcc, 0xcb, 0x4f, 0x49,
0x2d, 0x2e, 0x49, 0x2c, 0x29, 0x96, 0xe2, 0x4a, 0xcf, 0x4f, 0xcf, 0x87, 0x08, 0x4b, 0xc9, 0xa7,
0xe7, 0xe7, 0xa7, 0xe7, 0xa4, 0xea, 0x83, 0x79, 0x49, 0xa5, 0x69, 0xfa, 0x25, 0x99, 0xb9, 0x20,
0x65, 0xb9, 0x05, 0x10, 0x05, 0x4a, 0x31, 0x5c, 0x32, 0x01, 0x45, 0xa9, 0x05, 0x89, 0x45, 0xa9,
0x9e, 0x79, 0x65, 0xf9, 0x99, 0xc9, 0xa9, 0x41, 0xa9, 0xc9, 0xf9, 0x45, 0x29, 0xc5, 0x41, 0xa9,
0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x36, 0x5c, 0x6c, 0x05, 0xa9, 0x45, 0x99, 0xf9, 0x29, 0x12,
0x8c, 0x0a, 0x8c, 0x1a, 0xdc, 0x46, 0x52, 0x7a, 0x10, 0x13, 0xf5, 0x60, 0x26, 0xea, 0x85, 0xc0,
0x4c, 0x74, 0xe2, 0x38, 0x71, 0x4f, 0x9e, 0x61, 0xc2, 0x7d, 0x79, 0xc6, 0x20, 0xa8, 0x1e, 0x25,
0x79, 0x2e, 0x59, 0x1c, 0xa6, 0x17, 0x17, 0xe4, 0xe7, 0x15, 0xa7, 0x2a, 0xc9, 0x70, 0x49, 0x39,
0x16, 0x14, 0xe4, 0x54, 0x62, 0xb5, 0x5c, 0x49, 0x96, 0x4b, 0x1a, 0xab, 0x2c, 0x54, 0xb3, 0x38,
0x97, 0xa8, 0x73, 0x51, 0x6a, 0x62, 0x09, 0xcc, 0x70, 0xb8, 0x3e, 0x09, 0x2e, 0x31, 0x74, 0x09,
0x88, 0x16, 0xa3, 0xcd, 0x4c, 0x5c, 0x1c, 0x01, 0xd0, 0x90, 0x13, 0xca, 0xe2, 0x12, 0xc5, 0xea,
0x3a, 0x21, 0x75, 0x3d, 0x78, 0x68, 0xea, 0xe1, 0x0b, 0x1d, 0x29, 0x0d, 0xc2, 0x0a, 0x21, 0x16,
0x0b, 0xa5, 0x70, 0x09, 0x63, 0xf1, 0x8a, 0x90, 0x2a, 0x92, 0x01, 0xb8, 0x03, 0x42, 0x4a, 0x8d,
0x90, 0x32, 0xa8, 0x2d, 0xa1, 0x5c, 0x7c, 0xa8, 0x1e, 0x17, 0x52, 0x40, 0xd2, 0x89, 0x35, 0xb0,
0xa4, 0x14, 0xf1, 0xa8, 0x80, 0x18, 0xeb, 0xc4, 0x12, 0xc5, 0x54, 0x90, 0x94, 0xc4, 0x06, 0x8e,
0x72, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x99, 0xc9, 0xeb, 0xe7, 0x7b, 0x02, 0x00, 0x00,
}
type DRPCPaymentsClient interface {
DRPCConn() drpc.Conn
PrepareInvoiceRecords(ctx context.Context, in *PrepareInvoiceRecordsRequest) (*PrepareInvoiceRecordsResponse, error)
ApplyInvoiceRecords(ctx context.Context, in *ApplyInvoiceRecordsRequest) (*ApplyInvoiceRecordsResponse, error)
CreateInvoices(ctx context.Context, in *CreateInvoicesRequest) (*CreateInvoicesResponse, error)
}
type drpcPaymentsClient struct {
cc drpc.Conn
}
func NewDRPCPaymentsClient(cc drpc.Conn) DRPCPaymentsClient {
return &drpcPaymentsClient{cc}
}
func (c *drpcPaymentsClient) DRPCConn() drpc.Conn { return c.cc }
func (c *drpcPaymentsClient) PrepareInvoiceRecords(ctx context.Context, in *PrepareInvoiceRecordsRequest) (*PrepareInvoiceRecordsResponse, error) {
out := new(PrepareInvoiceRecordsResponse)
err := c.cc.Invoke(ctx, "/nodestats.Payments/PrepareInvoiceRecords", in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcPaymentsClient) ApplyInvoiceRecords(ctx context.Context, in *ApplyInvoiceRecordsRequest) (*ApplyInvoiceRecordsResponse, error) {
out := new(ApplyInvoiceRecordsResponse)
err := c.cc.Invoke(ctx, "/nodestats.Payments/ApplyInvoiceRecords", in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcPaymentsClient) CreateInvoices(ctx context.Context, in *CreateInvoicesRequest) (*CreateInvoicesResponse, error) {
out := new(CreateInvoicesResponse)
err := c.cc.Invoke(ctx, "/nodestats.Payments/CreateInvoices", in, out)
if err != nil {
return nil, err
}
return out, nil
}
type DRPCPaymentsServer interface {
PrepareInvoiceRecords(context.Context, *PrepareInvoiceRecordsRequest) (*PrepareInvoiceRecordsResponse, error)
ApplyInvoiceRecords(context.Context, *ApplyInvoiceRecordsRequest) (*ApplyInvoiceRecordsResponse, error)
CreateInvoices(context.Context, *CreateInvoicesRequest) (*CreateInvoicesResponse, error)
}
type DRPCPaymentsDescription struct{}
func (DRPCPaymentsDescription) NumMethods() int { return 3 }
func (DRPCPaymentsDescription) Method(n int) (string, drpc.Handler, interface{}, bool) {
switch n {
case 0:
return "/nodestats.Payments/PrepareInvoiceRecords",
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCPaymentsServer).
PrepareInvoiceRecords(
ctx,
in1.(*PrepareInvoiceRecordsRequest),
)
}, DRPCPaymentsServer.PrepareInvoiceRecords, true
case 1:
return "/nodestats.Payments/ApplyInvoiceRecords",
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCPaymentsServer).
ApplyInvoiceRecords(
ctx,
in1.(*ApplyInvoiceRecordsRequest),
)
}, DRPCPaymentsServer.ApplyInvoiceRecords, true
case 2:
return "/nodestats.Payments/CreateInvoices",
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCPaymentsServer).
CreateInvoices(
ctx,
in1.(*CreateInvoicesRequest),
)
}, DRPCPaymentsServer.CreateInvoices, true
default:
return "", nil, nil, false
}
}
func DRPCRegisterPayments(srv drpc.Server, impl DRPCPaymentsServer) {
srv.Register(impl, DRPCPaymentsDescription{})
}
type DRPCPayments_PrepareInvoiceRecordsStream interface {
drpc.Stream
SendAndClose(*PrepareInvoiceRecordsResponse) error
}
type drpcPaymentsPrepareInvoiceRecordsStream struct {
drpc.Stream
}
func (x *drpcPaymentsPrepareInvoiceRecordsStream) SendAndClose(m *PrepareInvoiceRecordsResponse) error {
if err := x.MsgSend(m); err != nil {
return err
}
return x.CloseSend()
}
type DRPCPayments_ApplyInvoiceRecordsStream interface {
drpc.Stream
SendAndClose(*ApplyInvoiceRecordsResponse) error
}
type drpcPaymentsApplyInvoiceRecordsStream struct {
drpc.Stream
}
func (x *drpcPaymentsApplyInvoiceRecordsStream) SendAndClose(m *ApplyInvoiceRecordsResponse) error {
if err := x.MsgSend(m); err != nil {
return err
}
return x.CloseSend()
}
type DRPCPayments_CreateInvoicesStream interface {
drpc.Stream
SendAndClose(*CreateInvoicesResponse) error
}
type drpcPaymentsCreateInvoicesStream struct {
drpc.Stream
}
func (x *drpcPaymentsCreateInvoicesStream) SendAndClose(m *CreateInvoicesResponse) error {
if err := x.MsgSend(m); err != nil {
return err
}
return x.CloseSend()
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// PaymentsClient is the client API for Payments service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type PaymentsClient interface {
PrepareInvoiceRecords(ctx context.Context, in *PrepareInvoiceRecordsRequest, opts ...grpc.CallOption) (*PrepareInvoiceRecordsResponse, error)
ApplyInvoiceRecords(ctx context.Context, in *ApplyInvoiceRecordsRequest, opts ...grpc.CallOption) (*ApplyInvoiceRecordsResponse, error)
CreateInvoices(ctx context.Context, in *CreateInvoicesRequest, opts ...grpc.CallOption) (*CreateInvoicesResponse, error)
}
type paymentsClient struct {
cc *grpc.ClientConn
}
func NewPaymentsClient(cc *grpc.ClientConn) PaymentsClient {
return &paymentsClient{cc}
}
func (c *paymentsClient) PrepareInvoiceRecords(ctx context.Context, in *PrepareInvoiceRecordsRequest, opts ...grpc.CallOption) (*PrepareInvoiceRecordsResponse, error) {
out := new(PrepareInvoiceRecordsResponse)
err := c.cc.Invoke(ctx, "/nodestats.Payments/PrepareInvoiceRecords", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *paymentsClient) ApplyInvoiceRecords(ctx context.Context, in *ApplyInvoiceRecordsRequest, opts ...grpc.CallOption) (*ApplyInvoiceRecordsResponse, error) {
out := new(ApplyInvoiceRecordsResponse)
err := c.cc.Invoke(ctx, "/nodestats.Payments/ApplyInvoiceRecords", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *paymentsClient) CreateInvoices(ctx context.Context, in *CreateInvoicesRequest, opts ...grpc.CallOption) (*CreateInvoicesResponse, error) {
out := new(CreateInvoicesResponse)
err := c.cc.Invoke(ctx, "/nodestats.Payments/CreateInvoices", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// PaymentsServer is the server API for Payments service.
type PaymentsServer interface {
PrepareInvoiceRecords(context.Context, *PrepareInvoiceRecordsRequest) (*PrepareInvoiceRecordsResponse, error)
ApplyInvoiceRecords(context.Context, *ApplyInvoiceRecordsRequest) (*ApplyInvoiceRecordsResponse, error)
CreateInvoices(context.Context, *CreateInvoicesRequest) (*CreateInvoicesResponse, error)
}
func RegisterPaymentsServer(s *grpc.Server, srv PaymentsServer) {
s.RegisterService(&_Payments_serviceDesc, srv)
}
func _Payments_PrepareInvoiceRecords_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PrepareInvoiceRecordsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PaymentsServer).PrepareInvoiceRecords(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/nodestats.Payments/PrepareInvoiceRecords",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PaymentsServer).PrepareInvoiceRecords(ctx, req.(*PrepareInvoiceRecordsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Payments_ApplyInvoiceRecords_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ApplyInvoiceRecordsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PaymentsServer).ApplyInvoiceRecords(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/nodestats.Payments/ApplyInvoiceRecords",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PaymentsServer).ApplyInvoiceRecords(ctx, req.(*ApplyInvoiceRecordsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Payments_CreateInvoices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateInvoicesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PaymentsServer).CreateInvoices(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/nodestats.Payments/CreateInvoices",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PaymentsServer).CreateInvoices(ctx, req.(*CreateInvoicesRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Payments_serviceDesc = grpc.ServiceDesc{
ServiceName: "nodestats.Payments",
HandlerType: (*PaymentsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "PrepareInvoiceRecords",
Handler: _Payments_PrepareInvoiceRecords_Handler,
},
{
MethodName: "ApplyInvoiceRecords",
Handler: _Payments_ApplyInvoiceRecords_Handler,
},
{
MethodName: "CreateInvoices",
Handler: _Payments_CreateInvoices_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "payments.proto",
}

28
pkg/pb/payments.proto Normal file
View File

@ -0,0 +1,28 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
syntax = "proto3";
option go_package = "pb";
package nodestats;
import "gogo.proto";
import "google/protobuf/timestamp.proto";
service Payments {
rpc PrepareInvoiceRecords(PrepareInvoiceRecordsRequest) returns (PrepareInvoiceRecordsResponse);
rpc ApplyInvoiceRecords(ApplyInvoiceRecordsRequest) returns (ApplyInvoiceRecordsResponse);
rpc CreateInvoices(CreateInvoicesRequest) returns (CreateInvoicesResponse);
}
message PrepareInvoiceRecordsRequest {
google.protobuf.Timestamp period = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}
message PrepareInvoiceRecordsResponse {}
message ApplyInvoiceRecordsRequest {}
message ApplyInvoiceRecordsResponse {}
message CreateInvoicesRequest {}
message CreateInvoicesResponse {}

View File

@ -44,6 +44,9 @@ type (
// OverlayInspectorClient is an alias to the drpc client interface
OverlayInspectorClient = pb.DRPCOverlayInspectorClient
// PaymentsClient is an alias to the drpc client interface
PaymentsClient = pb.DRPCPaymentsClient
// PieceStoreInspectorClient is an alias to the drpc client interface
PieceStoreInspectorClient = pb.DRPCPieceStoreInspectorClient
@ -157,6 +160,16 @@ func (c *Conn) OverlayInspectorClient() OverlayInspectorClient {
return NewOverlayInspectorClient(c.raw)
}
// NewPaymentsClient returns the drpc version of a PaymentsClient
func NewPaymentsClient(rc *RawConn) PaymentsClient {
return pb.NewDRPCPaymentsClient(rc)
}
// PaymentsClient returns a PaymentsClient for this connection
func (c *Conn) PaymentsClient() PaymentsClient {
return NewPaymentsClient(c.raw)
}
// NewPieceStoreInspectorClient returns the drpc version of a PieceStoreInspectorClient
func NewPieceStoreInspectorClient(rc *RawConn) PieceStoreInspectorClient {
return pb.NewDRPCPieceStoreInspectorClient(rc)

View File

@ -45,6 +45,9 @@ type (
// OverlayInspectorClient is an alias to the grpc client interface
OverlayInspectorClient = pb.OverlayInspectorClient
// PaymentsClient is an alias to the grpc client interface
PaymentsClient = pb.PaymentsClient
// PieceStoreInspectorClient is an alias to the grpc client interface
PieceStoreInspectorClient = pb.PieceStoreInspectorClient
@ -158,6 +161,16 @@ func (c *Conn) OverlayInspectorClient() OverlayInspectorClient {
return NewOverlayInspectorClient(c.raw)
}
// NewPaymentsClient returns the grpc version of a PaymentsClient
func NewPaymentsClient(rc *RawConn) PaymentsClient {
return pb.NewPaymentsClient(rc)
}
// PaymentsClient returns a PaymentsClient for this connection
func (c *Conn) PaymentsClient() PaymentsClient {
return NewPaymentsClient(c.raw)
}
// NewPieceStoreInspectorClient returns the grpc version of a PieceStoreInspectorClient
func NewPieceStoreInspectorClient(rc *RawConn) PieceStoreInspectorClient {
return pb.NewPieceStoreInspectorClient(rc)

View File

@ -5329,6 +5329,87 @@
]
}
},
{
"protopath": "pkg:/:pb:/:payments.proto",
"def": {
"messages": [
{
"name": "PrepareInvoiceRecordsRequest",
"fields": [
{
"id": 1,
"name": "period",
"type": "google.protobuf.Timestamp",
"options": [
{
"name": "(gogoproto.stdtime)",
"value": "true"
},
{
"name": "(gogoproto.nullable)",
"value": "false"
}
]
}
]
},
{
"name": "PrepareInvoiceRecordsResponse"
},
{
"name": "ApplyInvoiceRecordsRequest"
},
{
"name": "ApplyInvoiceRecordsResponse"
},
{
"name": "CreateInvoicesRequest"
},
{
"name": "CreateInvoicesResponse"
}
],
"services": [
{
"name": "Payments",
"rpcs": [
{
"name": "PrepareInvoiceRecords",
"in_type": "PrepareInvoiceRecordsRequest",
"out_type": "PrepareInvoiceRecordsResponse"
},
{
"name": "ApplyInvoiceRecords",
"in_type": "ApplyInvoiceRecordsRequest",
"out_type": "ApplyInvoiceRecordsResponse"
},
{
"name": "CreateInvoices",
"in_type": "CreateInvoicesRequest",
"out_type": "CreateInvoicesResponse"
}
]
}
],
"imports": [
{
"path": "gogo.proto"
},
{
"path": "google/protobuf/timestamp.proto"
}
],
"package": {
"name": "nodestats"
},
"options": [
{
"name": "go_package",
"value": "pb"
}
]
}
},
{
"protopath": "pkg:/:pb:/:piecestore2.proto",
"def": {

View File

@ -109,8 +109,8 @@ type API struct {
}
Payments struct {
Accounts payments.Accounts
Clearing payments.Clearing
Accounts payments.Accounts
Inspector *stripecoinpayments.Endpoint
}
Console struct {
@ -375,15 +375,14 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB, pointerDB metai
service := stripecoinpayments.NewService(
peer.Log.Named("stripecoinpayments service"),
config.StripeCoinPayments,
peer.DB.Customers(),
peer.DB.CoinpaymentsTransactions())
peer.DB.StripeCoinPayments(),
peer.DB.Console().Projects())
peer.Payments.Accounts = service.Accounts()
peer.Payments.Clearing = stripecoinpayments.NewChore(
peer.Log.Named("stripecoinpayments clearing loop"),
service,
config.StripeCoinPayments.TransactionUpdateInterval,
config.StripeCoinPayments.AccountBalanceUpdateInterval)
peer.Payments.Inspector = stripecoinpayments.NewEndpoint(service)
pb.RegisterPaymentsServer(peer.Server.PrivateGRPC(), peer.Payments.Inspector)
pb.DRPCRegisterPayments(peer.Server.PrivateDRPC(), peer.Payments.Inspector)
}
}

View File

@ -61,7 +61,7 @@ func TestGrapqhlMutation(t *testing.T) {
)
paymentsConfig := stripecoinpayments.Config{}
payments := stripecoinpayments.NewService(log, paymentsConfig, db.Customers(), db.CoinpaymentsTransactions())
payments := stripecoinpayments.NewService(log, paymentsConfig, db.StripeCoinPayments(), db.Console().Projects())
service, err := console.NewService(
log,

View File

@ -43,7 +43,7 @@ func TestGraphqlQuery(t *testing.T) {
)
paymentsConfig := stripecoinpayments.Config{}
payments := stripecoinpayments.NewService(log, paymentsConfig, db.Customers(), db.CoinpaymentsTransactions())
payments := stripecoinpayments.NewService(log, paymentsConfig, db.StripeCoinPayments(), db.Console().Projects())
service, err := console.NewService(
log,

View File

@ -28,6 +28,8 @@ type Projects interface {
Delete(ctx context.Context, id uuid.UUID) error
// Update is a method for updating project entity.
Update(ctx context.Context, project *Project) error
// List returns paginated projects, created before provided timestamp.
List(ctx context.Context, offset int64, limit int, before time.Time) (ProjectsPage, error)
}
// Project is a database object that describes Project entity
@ -50,3 +52,12 @@ type ProjectInfo struct {
CreatedAt time.Time `json:"createdAt"`
}
// ProjectsPage returns paginated projects,
// providing next offset if there are more projects
// to retrieve.
type ProjectsPage struct {
Projects []Project
Next bool
NextOffset int64
}

View File

@ -100,7 +100,7 @@ type Core struct {
Payments struct {
Accounts payments.Accounts
Clearing payments.Clearing
Chore *stripecoinpayments.Chore
}
GracefulExit struct {
@ -299,11 +299,12 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, pointerDB metainfo
service := stripecoinpayments.NewService(
peer.Log.Named("stripecoinpayments service"),
config.StripeCoinPayments,
peer.DB.Customers(),
peer.DB.CoinpaymentsTransactions())
peer.DB.StripeCoinPayments(),
peer.DB.Console().Projects())
peer.Payments.Accounts = service.Accounts()
peer.Payments.Clearing = stripecoinpayments.NewChore(
peer.Payments.Chore = stripecoinpayments.NewChore(
peer.Log.Named("stripecoinpayments clearing loop"),
service,
config.StripeCoinPayments.TransactionUpdateInterval,

View File

@ -1,15 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package payments
import "context"
// Clearing runs process of reconciling transactions deposits,
// customer balance, invoices and usages.
type Clearing interface {
// Run runs payments clearing loop.
Run(ctx context.Context) error
// Closes closes payments clearing loop.
Close() error
}

View File

@ -32,7 +32,7 @@ func (accounts *accounts) Invoices() payments.Invoices {
func (accounts *accounts) Setup(ctx context.Context, userID uuid.UUID, email string) (err error) {
defer mon.Task()(&ctx, userID, email)(&err)
_, err = accounts.service.customers.GetCustomerID(ctx, userID)
_, err = accounts.service.db.Customers().GetCustomerID(ctx, userID)
if err == nil {
return nil
}
@ -47,14 +47,14 @@ func (accounts *accounts) Setup(ctx context.Context, userID uuid.UUID, email str
}
// TODO: delete customer from stripe, if db insertion fails
return Error.Wrap(accounts.service.customers.Insert(ctx, userID, customer.ID))
return Error.Wrap(accounts.service.db.Customers().Insert(ctx, userID, customer.ID))
}
// 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)
customerID, err := accounts.service.db.Customers().GetCustomerID(ctx, userID)
if err != nil {
return 0, Error.Wrap(err)
}

View File

@ -21,7 +21,7 @@ type creditCards struct {
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)
customerID, err := creditCards.service.db.Customers().GetCustomerID(ctx, userID)
if err != nil {
return nil, Error.Wrap(err)
}
@ -66,7 +66,7 @@ func (creditCards *creditCards) List(ctx context.Context, userID uuid.UUID) (car
func (creditCards *creditCards) Add(ctx context.Context, userID uuid.UUID, cardToken string) (err error) {
defer mon.Task()(&ctx, userID, cardToken)(&err)
customerID, err := creditCards.service.customers.GetCustomerID(ctx, userID)
customerID, err := creditCards.service.db.Customers().GetCustomerID(ctx, userID)
if err != nil {
return payments.ErrAccountNotSetup.Wrap(err)
}
@ -107,7 +107,7 @@ func (creditCards *creditCards) Add(ctx context.Context, userID uuid.UUID, cardT
func (creditCards *creditCards) MakeDefault(ctx context.Context, userID uuid.UUID, cardID string) (err error) {
defer mon.Task()(&ctx, userID, cardID)(&err)
customerID, err := creditCards.service.customers.GetCustomerID(ctx, userID)
customerID, err := creditCards.service.db.Customers().GetCustomerID(ctx, userID)
if err != nil {
return payments.ErrAccountNotSetup.Wrap(err)
}

View File

@ -5,17 +5,37 @@ package stripecoinpayments
import (
"context"
"time"
"github.com/skyrings/skyring-common/tools/uuid"
)
// ErrNoCustomer is error class defining that there is no customer for user.
var ErrNoCustomer = Error.New("customer doesn't exist")
// CustomersDB is interface for working with stripe customers table.
//
// architecture: Database
type CustomersDB interface {
// Insert inserts a stripe customer into the database.
Insert(ctx context.Context, userID uuid.UUID, customerID string) error
// GetCustomerID return stripe customers id.
GetCustomerID(ctx context.Context, userID uuid.UUID) (string, error)
// List returns page with customers ids created before specified date.
List(ctx context.Context, offset int64, limit int, before time.Time) (CustomersPage, error)
}
// Customer holds customer id and user id.
type Customer struct {
ID string
UserID uuid.UUID
}
// CustomersPage holds customers and
// indicates if there is more data available
// and provides next offset.
type CustomersPage struct {
Customers []Customer
Next bool
NextOffset int64
}

View File

@ -4,7 +4,9 @@
package stripecoinpayments_test
import (
"strconv"
"testing"
"time"
"github.com/skyrings/skyring-common/tools/uuid"
"github.com/stretchr/testify/assert"
@ -12,6 +14,7 @@ import (
"storj.io/storj/internal/testcontext"
"storj.io/storj/satellite"
"storj.io/storj/satellite/payments/stripecoinpayments"
"storj.io/storj/satellite/satellitedb/satellitedbtest"
)
@ -20,7 +23,7 @@ func TestCustomersRepository(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
customers := db.Customers()
customers := db.StripeCoinPayments().Customers()
customerID := "customerID"
userID, err := uuid.New()
@ -43,3 +46,48 @@ func TestCustomersRepository(t *testing.T) {
})
})
}
func TestCustomersRepositoryList(t *testing.T) {
satellitedbtest.Run(t, func(t *testing.T, db satellite.DB) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
customersDB := db.StripeCoinPayments().Customers()
const custLen = 5
var customers []stripecoinpayments.Customer
for i := 0; i < custLen; i++ {
userID, err := uuid.New()
require.NoError(t, err)
cus := stripecoinpayments.Customer{
ID: "customerID" + strconv.Itoa(i),
UserID: *userID,
}
err = customersDB.Insert(ctx, cus.UserID, cus.ID)
require.NoError(t, err)
customers = append(customers, cus)
}
page, err := customersDB.List(ctx, 0, custLen, time.Now())
require.NoError(t, err)
require.Equal(t, custLen, len(page.Customers))
assert.False(t, page.Next)
assert.Equal(t, int64(0), page.NextOffset)
for _, cus1 := range page.Customers {
for _, cus2 := range customers {
if cus1.ID != cus2.ID {
continue
}
assert.Equal(t, cus2.ID, cus1.ID)
assert.Equal(t, cus2.UserID, cus1.UserID)
}
}
})
}

View File

@ -0,0 +1,16 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package stripecoinpayments
// DB is stripecoinpayments DB interface.
//
// architecture: Database
type DB interface {
// Customers is getter for customers db.
Customers() CustomersDB
// Transactions is getter for transactions db.
Transactions() TransactionsDB
// ProjectRecords is getter for invoice project records db.
ProjectRecords() ProjectRecordsDB
}

View File

@ -0,0 +1,57 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package stripecoinpayments
import (
"context"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/rpc/rpcstatus"
)
// Endpoint is stripecoinpayments private RPC server payments endpoint.
type Endpoint struct {
service *Service
}
// NewEndpoint creates new endpoint.
func NewEndpoint(service *Service) *Endpoint {
return &Endpoint{service: service}
}
// PrepareInvoiceRecords creates project invoice records for all satellite projects.
func (endpoint *Endpoint) PrepareInvoiceRecords(ctx context.Context, req *pb.PrepareInvoiceRecordsRequest) (_ *pb.PrepareInvoiceRecordsResponse, err error) {
defer mon.Task()(&ctx)(&err)
err = endpoint.service.PrepareInvoiceProjectRecords(ctx, req.Period)
if err != nil {
return nil, rpcstatus.Error(rpcstatus.Internal, err.Error())
}
return &pb.PrepareInvoiceRecordsResponse{}, nil
}
// ApplyInvoiceRecords creates stripe line items for all unapplied invoice project records.
func (endpoint *Endpoint) ApplyInvoiceRecords(ctx context.Context, req *pb.ApplyInvoiceRecordsRequest) (_ *pb.ApplyInvoiceRecordsResponse, err error) {
defer mon.Task()(&ctx)(&err)
err = endpoint.service.InvoiceApplyProjectRecords(ctx)
if err != nil {
return nil, rpcstatus.Error(rpcstatus.Internal, err.Error())
}
return &pb.ApplyInvoiceRecordsResponse{}, nil
}
// CreateInvoices creates invoice for all user accounts on the satellite.
func (endpoint *Endpoint) CreateInvoices(ctx context.Context, req *pb.CreateInvoicesRequest) (_ *pb.CreateInvoicesResponse, err error) {
defer mon.Task()(&ctx)(&err)
err = endpoint.service.CreateInvoices(ctx)
if err != nil {
return nil, rpcstatus.Error(rpcstatus.Internal, err.Error())
}
return &pb.CreateInvoicesResponse{}, nil
}

View File

@ -14,6 +14,8 @@ import (
)
// invoices is an implementation of payments.Invoices.
//
// architecture: Database
type invoices struct {
service *Service
}
@ -22,7 +24,7 @@ type invoices struct {
func (invoices *invoices) List(ctx context.Context, userID uuid.UUID) (invoicesList []payments.Invoice, err error) {
defer mon.Task()(&ctx, userID)(&err)
customerID, err := invoices.service.customers.GetCustomerID(ctx, userID)
customerID, err := invoices.service.db.Customers().GetCustomerID(ctx, userID)
if err != nil {
return nil, Error.Wrap(err)
}

View File

@ -0,0 +1,57 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package stripecoinpayments
import (
"context"
"time"
"github.com/skyrings/skyring-common/tools/uuid"
)
// ErrProjectRecordExists is error class defining that such project record already exists.
var ErrProjectRecordExists = Error.New("invoice project record already exists")
// ProjectRecordsDB is interface for working with invoice project records.
//
// architecture: Database
type ProjectRecordsDB interface {
// Create creates new invoice project record in the DB.
Create(ctx context.Context, records []CreateProjectRecord, start, end time.Time) error
// Check checks if invoice project record for specified project and billing period exists.
Check(ctx context.Context, projectID uuid.UUID, start, end time.Time) error
// Consume consumes invoice project record.
Consume(ctx context.Context, id uuid.UUID) error
// ListUnapplied returns project records page with unapplied project records.
ListUnapplied(ctx context.Context, offset int64, limit int, before time.Time) (ProjectRecordsPage, error)
}
// CreateProjectRecord holds info needed for creation new invoice
// project record.
type CreateProjectRecord struct {
ProjectID uuid.UUID
Storage float64
Egress int64
Objects int64
}
// ProjectRecord holds project usage particular for billing period.
type ProjectRecord struct {
ID uuid.UUID
ProjectID uuid.UUID
Storage float64
Egress int64
Objects int64
PeriodStart time.Time
PeriodEnd time.Time
}
// ProjectRecordsPage holds project records and
// indicates if there is more data available
// and provides next offset.
type ProjectRecordsPage struct {
Records []ProjectRecord
Next bool
NextOffset int64
}

View File

@ -0,0 +1,127 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package stripecoinpayments_test
import (
"testing"
"time"
"github.com/skyrings/skyring-common/tools/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"storj.io/storj/internal/testcontext"
"storj.io/storj/satellite"
"storj.io/storj/satellite/payments/stripecoinpayments"
"storj.io/storj/satellite/satellitedb/satellitedbtest"
)
func TestProjectRecords(t *testing.T) {
satellitedbtest.Run(t, func(t *testing.T, db satellite.DB) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
utc := time.Now().UTC()
prjID, err := uuid.New()
require.NoError(t, err)
start := time.Date(utc.Year(), utc.Month(), 1, 0, 0, 0, 0, time.UTC)
end := time.Date(utc.Year(), utc.Month()+1, 1, 0, 0, 0, 0, time.UTC)
projectRecordsDB := db.StripeCoinPayments().ProjectRecords()
t.Run("create", func(t *testing.T) {
err = projectRecordsDB.Create(ctx,
[]stripecoinpayments.CreateProjectRecord{
{
ProjectID: *prjID,
Storage: 1,
Egress: 2,
Objects: 3,
},
},
start, end,
)
require.NoError(t, err)
})
t.Run("check", func(t *testing.T) {
err = projectRecordsDB.Check(ctx, *prjID, start, end)
require.Error(t, err)
assert.Equal(t, stripecoinpayments.ErrProjectRecordExists, err)
})
page, err := projectRecordsDB.ListUnapplied(ctx, 0, 1, time.Now())
require.NoError(t, err)
require.Equal(t, 1, len(page.Records))
t.Run("consume", func(t *testing.T) {
err = projectRecordsDB.Consume(ctx, page.Records[0].ID)
require.NoError(t, err)
})
page, err = projectRecordsDB.ListUnapplied(ctx, 0, 1, time.Now())
require.NoError(t, err)
require.Equal(t, 0, len(page.Records))
})
}
func TestProjectRecordsList(t *testing.T) {
satellitedbtest.Run(t, func(t *testing.T, db satellite.DB) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
utc := time.Now().UTC()
start := time.Date(utc.Year(), utc.Month(), 1, 0, 0, 0, 0, time.UTC)
end := time.Date(utc.Year(), utc.Month()+1, 1, 0, 0, 0, 0, time.UTC)
projectRecordsDB := db.StripeCoinPayments().ProjectRecords()
const recordsLen = 5
var createProjectRecords []stripecoinpayments.CreateProjectRecord
for i := 0; i < recordsLen; i++ {
projID, err := uuid.New()
require.NoError(t, err)
createProjectRecords = append(createProjectRecords,
stripecoinpayments.CreateProjectRecord{
ProjectID: *projID,
Storage: float64(i) + 1,
Egress: int64(i) + 2,
Objects: int64(i) + 3,
},
)
}
err := projectRecordsDB.Create(ctx, createProjectRecords, start, end)
require.NoError(t, err)
page, err := projectRecordsDB.ListUnapplied(ctx, 0, recordsLen, time.Now())
require.NoError(t, err)
require.Equal(t, recordsLen, len(page.Records))
assert.False(t, page.Next)
assert.Equal(t, int64(0), page.NextOffset)
for _, record := range page.Records {
for _, createRecord := range createProjectRecords {
if record.ProjectID != createRecord.ProjectID {
continue
}
assert.NotNil(t, record.ID)
assert.Equal(t, 16, len(record.ID))
assert.Equal(t, createRecord.ProjectID, record.ProjectID)
assert.Equal(t, createRecord.Storage, record.Storage)
assert.Equal(t, createRecord.Egress, record.Egress)
assert.Equal(t, createRecord.Objects, record.Objects)
assert.Equal(t, start, record.PeriodStart.UTC())
assert.Equal(t, end, record.PeriodEnd.UTC())
}
}
})
}

View File

@ -5,14 +5,16 @@ package stripecoinpayments
import (
"context"
"fmt"
"time"
"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/client"
"github.com/zeebo/errs"
"go.uber.org/zap"
monkit "gopkg.in/spacemonkeygo/monkit.v2"
"gopkg.in/spacemonkeygo/monkit.v2"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/payments"
"storj.io/storj/satellite/payments/coinpayments"
)
@ -37,15 +39,15 @@ type Config struct {
//
// architecture: Service
type Service struct {
log *zap.Logger
customers CustomersDB
transactionsDB TransactionsDB
stripeClient *client.API
coinPayments *coinpayments.Client
log *zap.Logger
db DB
projectsDB console.Projects
stripeClient *client.API
coinPayments *coinpayments.Client
}
// NewService creates a Service instance.
func NewService(log *zap.Logger, config Config, customers CustomersDB, transactionsDB TransactionsDB) *Service {
func NewService(log *zap.Logger, config Config, db DB, projectsDB console.Projects) *Service {
stripeClient := client.New(config.StripeSecretKey, nil)
coinPaymentsClient := coinpayments.NewClient(
@ -56,11 +58,11 @@ func NewService(log *zap.Logger, config Config, customers CustomersDB, transacti
)
return &Service{
log: log,
customers: customers,
transactionsDB: transactionsDB,
stripeClient: stripeClient,
coinPayments: coinPaymentsClient,
log: log,
db: db,
projectsDB: projectsDB,
stripeClient: stripeClient,
coinPayments: coinPaymentsClient,
}
}
@ -76,7 +78,7 @@ func (service *Service) updateTransactionsLoop(ctx context.Context) (err error)
const limit = 25
before := time.Now()
txsPage, err := service.transactionsDB.ListPending(ctx, 0, limit, before)
txsPage, err := service.db.Transactions().ListPending(ctx, 0, limit, before)
if err != nil {
return err
}
@ -90,7 +92,7 @@ func (service *Service) updateTransactionsLoop(ctx context.Context) (err error)
return err
}
txsPage, err = service.transactionsDB.ListPending(ctx, txsPage.NextOffset, limit, before)
txsPage, err = service.db.Transactions().ListPending(ctx, txsPage.NextOffset, limit, before)
if err != nil {
return err
}
@ -138,7 +140,7 @@ func (service *Service) updateTransactions(ctx context.Context, ids coinpayments
}
}
return service.transactionsDB.Update(ctx, updates, applies)
return service.db.Transactions().Update(ctx, updates, applies)
}
// applyAccountBalanceLoop fetches all unapplied transaction in a loop, applying transaction
@ -149,7 +151,7 @@ func (service *Service) updateAccountBalanceLoop(ctx context.Context) (err error
const limit = 25
before := time.Now()
txsPage, err := service.transactionsDB.ListUnapplied(ctx, 0, limit, before)
txsPage, err := service.db.Transactions().ListUnapplied(ctx, 0, limit, before)
if err != nil {
return err
}
@ -169,7 +171,7 @@ func (service *Service) updateAccountBalanceLoop(ctx context.Context) (err error
return err
}
txsPage, err := service.transactionsDB.ListUnapplied(ctx, txsPage.NextOffset, limit, before)
txsPage, err := service.db.Transactions().ListUnapplied(ctx, txsPage.NextOffset, limit, before)
if err != nil {
return err
}
@ -192,12 +194,12 @@ func (service *Service) updateAccountBalanceLoop(ctx context.Context) (err error
func (service *Service) applyTransactionBalance(ctx context.Context, tx Transaction) (err error) {
defer mon.Task()(&ctx)(&err)
cusID, err := service.customers.GetCustomerID(ctx, tx.AccountID)
cusID, err := service.db.Customers().GetCustomerID(ctx, tx.AccountID)
if err != nil {
return err
}
if err = service.transactionsDB.Consume(ctx, tx.ID); err != nil {
if err = service.db.Transactions().Consume(ctx, tx.ID); err != nil {
return err
}
@ -216,3 +218,243 @@ func (service *Service) applyTransactionBalance(ctx context.Context, tx Transact
_, err = service.stripeClient.CustomerBalanceTransactions.New(params)
return err
}
// PrepareInvoiceProjectRecords iterates through all projects and creates invoice records if
// none exists.
func (service *Service) PrepareInvoiceProjectRecords(ctx context.Context, period time.Time) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 25
now := time.Now().UTC()
utc := period.UTC()
start := time.Date(utc.Year(), utc.Month(), 1, 0, 0, 0, 0, time.UTC)
end := time.Date(utc.Year(), utc.Month()+1, 1, 0, 0, 0, 0, time.UTC)
if end.After(now) {
return Error.New("prepare is for past periods only")
}
projsPage, err := service.projectsDB.List(ctx, 0, limit, end)
if err != nil {
return Error.Wrap(err)
}
if err = service.createProjectRecords(ctx, projsPage.Projects, start, end); err != nil {
return Error.Wrap(err)
}
for projsPage.Next {
if err = ctx.Err(); err != nil {
return Error.Wrap(err)
}
projsPage, err = service.projectsDB.List(ctx, projsPage.NextOffset, limit, end)
if err != nil {
return Error.Wrap(err)
}
if err = service.createProjectRecords(ctx, projsPage.Projects, start, end); err != nil {
return Error.Wrap(err)
}
}
return nil
}
// createProjectRecords creates invoice project record if none exists.
func (service *Service) createProjectRecords(ctx context.Context, projects []console.Project, start, end time.Time) (err error) {
defer mon.Task()(&ctx)(&err)
var records []CreateProjectRecord
for _, project := range projects {
if err = ctx.Err(); err != nil {
return err
}
if err = service.db.ProjectRecords().Check(ctx, project.ID, start, end); err != nil {
if err == ErrProjectRecordExists {
continue
}
return err
}
// TODO: account for usage data.
records = append(records,
CreateProjectRecord{
ProjectID: project.ID,
Storage: 0,
Egress: 0,
Objects: 0,
},
)
}
return service.db.ProjectRecords().Create(ctx, records, start, end)
}
// InvoiceApplyProjectRecords iterates through unapplied invoice project records and creates invoice line items
// for stripe customer.
func (service *Service) InvoiceApplyProjectRecords(ctx context.Context) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 25
before := time.Now()
recordsPage, err := service.db.ProjectRecords().ListUnapplied(ctx, 0, limit, before)
if err != nil {
return Error.Wrap(err)
}
if err = service.applyProjectRecords(ctx, recordsPage.Records); err != nil {
return Error.Wrap(err)
}
for recordsPage.Next {
if err = ctx.Err(); err != nil {
return Error.Wrap(err)
}
recordsPage, err = service.db.ProjectRecords().ListUnapplied(ctx, recordsPage.NextOffset, limit, before)
if err != nil {
return Error.Wrap(err)
}
if err = service.applyProjectRecords(ctx, recordsPage.Records); err != nil {
return Error.Wrap(err)
}
}
return nil
}
// applyProjectRecords applies invoice intents as invoice line items to stripe customer.
func (service *Service) applyProjectRecords(ctx context.Context, records []ProjectRecord) (err error) {
defer mon.Task()(&ctx)(&err)
for _, record := range records {
if err = ctx.Err(); err != nil {
return err
}
proj, err := service.projectsDB.Get(ctx, record.ProjectID)
if err != nil {
return err
}
cusID, err := service.db.Customers().GetCustomerID(ctx, proj.OwnerID)
if err != nil {
if err == ErrNoCustomer {
continue
}
return err
}
if err = service.createInvoiceItems(ctx, cusID, proj.Name, record); err != nil {
return err
}
}
return nil
}
// createInvoiceItems consumes invoice project record and creates invoice line items for stripe customer.
func (service *Service) createInvoiceItems(ctx context.Context, cusID, projName string, record ProjectRecord) (err error) {
defer mon.Task()(&ctx)(&err)
if err = service.db.ProjectRecords().Consume(ctx, record.ID); err != nil {
return err
}
// TODO: add and apply pricing.
projectItem := &stripe.InvoiceItemParams{
Amount: stripe.Int64(0),
Currency: stripe.String(string(stripe.CurrencyUSD)),
Customer: stripe.String(cusID),
Description: stripe.String(fmt.Sprintf("project %s", projName)),
Period: &stripe.InvoiceItemPeriodParams{
End: stripe.Int64(record.PeriodEnd.Unix()),
Start: stripe.Int64(record.PeriodStart.Unix()),
},
}
projectItem.AddMetadata("projectID", record.ProjectID.String())
_, err = service.stripeClient.InvoiceItems.New(projectItem)
return err
}
// CreateInvoices lists through all customers and creates invoices.
func (service *Service) CreateInvoices(ctx context.Context) (err error) {
defer mon.Task()(&ctx)(&err)
const limit = 25
before := time.Now()
cusPage, err := service.db.Customers().List(ctx, 0, limit, before)
if err != nil {
return Error.Wrap(err)
}
for _, cus := range cusPage.Customers {
if err = ctx.Err(); err != nil {
return Error.Wrap(err)
}
if err = service.createInvoice(ctx, cus.ID); err != nil {
return Error.Wrap(err)
}
}
for cusPage.Next {
if err = ctx.Err(); err != nil {
return Error.Wrap(err)
}
cusPage, err = service.db.Customers().List(ctx, cusPage.NextOffset, limit, before)
if err != nil {
return Error.Wrap(err)
}
for _, cus := range cusPage.Customers {
if err = ctx.Err(); err != nil {
return Error.Wrap(err)
}
if err = service.createInvoice(ctx, cus.ID); err != nil {
return Error.Wrap(err)
}
}
}
return nil
}
// createInvoice creates invoice for stripe customer. Returns nil error if there are no
// pending invoice line items for customer.
func (service *Service) createInvoice(ctx context.Context, cusID string) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = service.stripeClient.Invoices.New(
&stripe.InvoiceParams{
Customer: stripe.String(cusID),
AutoAdvance: stripe.Bool(true),
},
)
if err != nil {
if stripeErr, ok := err.(*stripe.Error); ok {
switch stripeErr.Code {
case stripe.ErrorCodeInvoiceNoCustomerLineItems:
return nil
default:
return err
}
}
}
return nil
}

View File

@ -28,7 +28,7 @@ type storjTokens struct {
func (tokens *storjTokens) Deposit(ctx context.Context, userID uuid.UUID, amount big.Float) (_ *payments.Transaction, err error) {
defer mon.Task()(&ctx, userID, amount)(&err)
customerID, err := tokens.service.customers.GetCustomerID(ctx, userID)
customerID, err := tokens.service.db.Customers().GetCustomerID(ctx, userID)
if err != nil {
return nil, Error.Wrap(err)
}
@ -55,7 +55,7 @@ func (tokens *storjTokens) Deposit(ctx context.Context, userID uuid.UUID, amount
return nil, Error.Wrap(err)
}
cpTX, err := tokens.service.transactionsDB.Insert(ctx,
cpTX, err := tokens.service.db.Transactions().Insert(ctx,
Transaction{
ID: tx.ID,
AccountID: userID,

View File

@ -22,12 +22,12 @@ import (
"storj.io/storj/satellite/satellitedb/satellitedbtest"
)
func TestInsertUpdateConsume(t *testing.T) {
func TestTransactionsDB(t *testing.T) {
satellitedbtest.Run(t, func(t *testing.T, db satellite.DB) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
transactions := db.CoinpaymentsTransactions()
transactions := db.StripeCoinPayments().Transactions()
amount, ok := new(big.Float).SetPrec(1000).SetString("2.0000000000000000005")
require.True(t, ok)
@ -111,7 +111,7 @@ func TestInsertUpdateConsume(t *testing.T) {
})
}
func TestList(t *testing.T) {
func TestTransactionsDBList(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
@ -146,11 +146,11 @@ func TestList(t *testing.T) {
t.Run("pending transactions", func(t *testing.T) {
satellitedbtest.Run(t, func(t *testing.T, db satellite.DB) {
for _, tx := range txs {
_, err := db.CoinpaymentsTransactions().Insert(ctx, tx)
_, err := db.StripeCoinPayments().Transactions().Insert(ctx, tx)
require.NoError(t, err)
}
page, err := db.CoinpaymentsTransactions().ListPending(ctx, 0, transactionCount, time.Now())
page, err := db.StripeCoinPayments().Transactions().ListPending(ctx, 0, transactionCount, time.Now())
require.NoError(t, err)
require.Equal(t, transactionCount, len(page.Transactions))
@ -171,7 +171,7 @@ func TestList(t *testing.T) {
var applies coinpayments.TransactionIDList
for _, tx := range txs {
_, err := db.CoinpaymentsTransactions().Insert(ctx, tx)
_, err := db.StripeCoinPayments().Transactions().Insert(ctx, tx)
require.NoError(t, err)
tx.Status = coinpayments.StatusReceived
@ -188,10 +188,10 @@ func TestList(t *testing.T) {
updatedTxs = append(updatedTxs, tx)
}
err := db.CoinpaymentsTransactions().Update(ctx, updates, applies)
err := db.StripeCoinPayments().Transactions().Update(ctx, updates, applies)
require.NoError(t, err)
page, err := db.CoinpaymentsTransactions().ListUnapplied(ctx, 0, transactionCount, time.Now())
page, err := db.StripeCoinPayments().Transactions().ListUnapplied(ctx, 0, transactionCount, time.Now())
require.NoError(t, err)
require.Equal(t, transactionCount, len(page.Transactions))

View File

@ -79,10 +79,8 @@ type DB interface {
Buckets() metainfo.BucketsDB
// GracefulExit returns database for graceful exit
GracefulExit() gracefulexit.DB
// StripeCustomers returns table for storing stripe customers
Customers() stripecoinpayments.CustomersDB
// CoinpaymentsTransactions returns db for storing coinpayments transactions.
CoinpaymentsTransactions() stripecoinpayments.TransactionsDB
// StripeCoinPayments returns stripecoinpayments database.
StripeCoinPayments() stripecoinpayments.DB
}
// Config is the global config satellite

View File

@ -15,8 +15,8 @@ import (
dbx "storj.io/storj/satellite/satellitedb/dbx"
)
// ensure that coinpaymentsTransaction implements stripecoinpayments.TransactionsDB.
var _ stripecoinpayments.TransactionsDB = (*coinpaymentsTransactions)(nil)
// ensure that coinpaymentsTransactions implements stripecoinpayments.TransactionsDB.
var _ stripecoinpayments.TransactionsDB = (*coinPaymentsTransactions)(nil)
// applyBalanceIntentState defines states of the apply balance intents.
type applyBalanceIntentState int
@ -33,15 +33,15 @@ func (intent applyBalanceIntentState) Int() int {
return int(intent)
}
// coinpaymentsTransactions is Coinpayments transactions DB.
// coinPaymentsTransactions is CoinPayments transactions DB.
//
// architecture: Database
type coinpaymentsTransactions struct {
type coinPaymentsTransactions struct {
db *dbx.DB
}
// Insert inserts new coinpayments transaction into DB.
func (db *coinpaymentsTransactions) Insert(ctx context.Context, tx stripecoinpayments.Transaction) (*stripecoinpayments.Transaction, error) {
func (db *coinPaymentsTransactions) Insert(ctx context.Context, tx stripecoinpayments.Transaction) (*stripecoinpayments.Transaction, error) {
amount, err := tx.Amount.GobEncode()
if err != nil {
return nil, errs.Wrap(err)
@ -68,7 +68,7 @@ func (db *coinpaymentsTransactions) Insert(ctx context.Context, tx stripecoinpay
}
// Update updates status and received for set of transactions.
func (db *coinpaymentsTransactions) Update(ctx context.Context, updates []stripecoinpayments.TransactionUpdate, applies coinpayments.TransactionIDList) error {
func (db *coinPaymentsTransactions) Update(ctx context.Context, updates []stripecoinpayments.TransactionUpdate, applies coinpayments.TransactionIDList) error {
if len(updates) == 0 {
return nil
}
@ -106,7 +106,7 @@ func (db *coinpaymentsTransactions) Update(ctx context.Context, updates []stripe
}
// Consume marks transaction as consumed, so it won't participate in apply account balance loop.
func (db *coinpaymentsTransactions) Consume(ctx context.Context, id coinpayments.TransactionID) error {
func (db *coinPaymentsTransactions) Consume(ctx context.Context, id coinpayments.TransactionID) error {
_, err := db.db.Update_StripecoinpaymentsApplyBalanceIntent_By_TxId(ctx,
dbx.StripecoinpaymentsApplyBalanceIntent_TxId(id.String()),
dbx.StripecoinpaymentsApplyBalanceIntent_Update_Fields{
@ -117,7 +117,7 @@ func (db *coinpaymentsTransactions) Consume(ctx context.Context, id coinpayments
}
// ListPending returns paginated list of pending transactions.
func (db *coinpaymentsTransactions) ListPending(ctx context.Context, offset int64, limit int, before time.Time) (stripecoinpayments.TransactionsPage, error) {
func (db *coinPaymentsTransactions) ListPending(ctx context.Context, offset int64, limit int, before time.Time) (stripecoinpayments.TransactionsPage, error) {
var page stripecoinpayments.TransactionsPage
dbxTXs, err := db.db.Limited_CoinpaymentsTransaction_By_CreatedAt_LessOrEqual_And_Status_OrderBy_Desc_CreatedAt(
@ -153,7 +153,7 @@ func (db *coinpaymentsTransactions) ListPending(ctx context.Context, offset int6
}
// List Unapplied returns TransactionsPage with transactions completed transaction that should be applied to account balance.
func (db *coinpaymentsTransactions) ListUnapplied(ctx context.Context, offset int64, limit int, before time.Time) (_ stripecoinpayments.TransactionsPage, err error) {
func (db *coinPaymentsTransactions) ListUnapplied(ctx context.Context, offset int64, limit int, before time.Time) (_ stripecoinpayments.TransactionsPage, err error) {
query := db.db.Rebind(`SELECT
txs.id,
txs.user_id,

View File

@ -5,6 +5,8 @@ package satellitedb
import (
"context"
"database/sql"
"time"
"github.com/skyrings/skyring-common/tools/uuid"
@ -16,6 +18,8 @@ import (
var _ stripecoinpayments.CustomersDB = (*customers)(nil)
// customers is an implementation of stripecoinpayments.CustomersDB.
//
// architecture: Database
type customers struct {
db *dbx.DB
}
@ -39,8 +43,59 @@ func (customers *customers) GetCustomerID(ctx context.Context, userID uuid.UUID)
idRow, err := customers.db.Get_StripeCustomer_CustomerId_By_UserId(ctx, dbx.StripeCustomer_UserId(userID[:]))
if err != nil {
if err == sql.ErrNoRows {
return "", stripecoinpayments.ErrNoCustomer
}
return "", err
}
return idRow.CustomerId, nil
}
// List returns paginated customers id list, with customers created before specified date.
func (customers *customers) List(ctx context.Context, offset int64, limit int, before time.Time) (_ stripecoinpayments.CustomersPage, err error) {
defer mon.Task()(&ctx)(&err)
var page stripecoinpayments.CustomersPage
dbxCustomers, err := customers.db.Limited_StripeCustomer_By_CreatedAt_LessOrEqual_OrderBy_Desc_CreatedAt(ctx,
dbx.StripeCustomer_CreatedAt(before),
limit+1,
offset,
)
if err != nil {
return stripecoinpayments.CustomersPage{}, err
}
if len(dbxCustomers) == limit+1 {
page.Next = true
page.NextOffset = offset + int64(limit) + 1
dbxCustomers = dbxCustomers[:len(dbxCustomers)-1]
}
for _, dbxCustomer := range dbxCustomers {
cus, err := fromDBXCustomer(dbxCustomer)
if err != nil {
return stripecoinpayments.CustomersPage{}, err
}
page.Customers = append(page.Customers, *cus)
}
return page, nil
}
// fromDBXCustomer converts *dbx.StripeCustomer to *stripecoinpayments.Customer.
func fromDBXCustomer(dbxCustomer *dbx.StripeCustomer) (*stripecoinpayments.Customer, error) {
userID, err := bytesToUUID(dbxCustomer.UserId)
if err != nil {
return nil, err
}
return &stripecoinpayments.Customer{
ID: dbxCustomer.CustomerId,
UserID: userID,
}, nil
}

View File

@ -144,12 +144,7 @@ func (db *DB) GracefulExit() gracefulexit.DB {
return &gracefulexitDB{db: db.db}
}
// Customers returns database for dealing with stripe customers.
func (db *DB) Customers() stripecoinpayments.CustomersDB {
return &customers{db: db.db}
}
// CoinpaymentsTransactions returns database for dealing with coinpayments transactions.
func (db *DB) CoinpaymentsTransactions() stripecoinpayments.TransactionsDB {
return &coinpaymentsTransactions{db: db.db}
// StripeCoinPayments returns database for stripecoinpayments.
func (db *DB) StripeCoinPayments() stripecoinpayments.DB {
return &stripeCoinPaymentsDB{db: db.db}
}

View File

@ -292,6 +292,12 @@ read all (
orderby asc project.name
)
read limitoffset (
select project
where project.created_at < ?
orderby asc project.created_at
)
model project_invoice_stamp (
key project_id start_date end_date
unique invoice_id
@ -844,10 +850,16 @@ model stripe_customer (
)
create stripe_customer ( )
read one (
select stripe_customer.customer_id
where stripe_customer.user_id = ?
)
read limitoffset (
select stripe_customer
where stripe_customer.created_at <= ?
orderby desc stripe_customer.created_at
)
model coinpayments_transaction (
key id
@ -890,3 +902,40 @@ delete stripecoinpayments_apply_balance_intent (
where stripecoinpayments_apply_balance_intent.tx_id = ?
)
model stripecoinpayments_invoice_project_record (
key id
unique project_id period_start period_end
field id blob
field project_id blob
field storage float64
field egress int64
field objects int64
field period_start timestamp
field period_end timestamp
field state int ( updatable )
field created_at timestamp ( autoinsert )
)
create stripecoinpayments_invoice_project_record ()
update stripecoinpayments_invoice_project_record (
where stripecoinpayments_invoice_project_record.id = ?
)
delete stripecoinpayments_invoice_project_record (
where stripecoinpayments_invoice_project_record.id = ?
)
read one (
select stripecoinpayments_invoice_project_record
where stripecoinpayments_invoice_project_record.project_id = ?
where stripecoinpayments_invoice_project_record.period_start = ?
where stripecoinpayments_invoice_project_record.period_end = ?
)
read limitoffset (
select stripecoinpayments_invoice_project_record
where stripecoinpayments_invoice_project_record.created_at <= ?
where stripecoinpayments_invoice_project_record.state = ?
orderby desc stripecoinpayments_invoice_project_record.created_at
)

View File

@ -476,6 +476,19 @@ CREATE TABLE stripe_customers (
PRIMARY KEY ( user_id ),
UNIQUE ( customer_id )
);
CREATE TABLE stripecoinpayments_invoice_project_records (
id bytea NOT NULL,
project_id bytea NOT NULL,
storage double precision NOT NULL,
egress bigint NOT NULL,
objects bigint NOT NULL,
period_start timestamp with time zone NOT NULL,
period_end timestamp with time zone NOT NULL,
state integer NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY ( id ),
UNIQUE ( project_id, period_start, period_end )
);
CREATE TABLE users (
id bytea NOT NULL,
email text NOT NULL,
@ -4078,6 +4091,199 @@ func (f StripeCustomer_CreatedAt_Field) value() interface{} {
func (StripeCustomer_CreatedAt_Field) _Column() string { return "created_at" }
type StripecoinpaymentsInvoiceProjectRecord struct {
Id []byte
ProjectId []byte
Storage float64
Egress int64
Objects int64
PeriodStart time.Time
PeriodEnd time.Time
State int
CreatedAt time.Time
}
func (StripecoinpaymentsInvoiceProjectRecord) _Table() string {
return "stripecoinpayments_invoice_project_records"
}
type StripecoinpaymentsInvoiceProjectRecord_Update_Fields struct {
State StripecoinpaymentsInvoiceProjectRecord_State_Field
}
type StripecoinpaymentsInvoiceProjectRecord_Id_Field struct {
_set bool
_null bool
_value []byte
}
func StripecoinpaymentsInvoiceProjectRecord_Id(v []byte) StripecoinpaymentsInvoiceProjectRecord_Id_Field {
return StripecoinpaymentsInvoiceProjectRecord_Id_Field{_set: true, _value: v}
}
func (f StripecoinpaymentsInvoiceProjectRecord_Id_Field) value() interface{} {
if !f._set || f._null {
return nil
}
return f._value
}
func (StripecoinpaymentsInvoiceProjectRecord_Id_Field) _Column() string { return "id" }
type StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field struct {
_set bool
_null bool
_value []byte
}
func StripecoinpaymentsInvoiceProjectRecord_ProjectId(v []byte) StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field {
return StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field{_set: true, _value: v}
}
func (f StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field) value() interface{} {
if !f._set || f._null {
return nil
}
return f._value
}
func (StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field) _Column() string { return "project_id" }
type StripecoinpaymentsInvoiceProjectRecord_Storage_Field struct {
_set bool
_null bool
_value float64
}
func StripecoinpaymentsInvoiceProjectRecord_Storage(v float64) StripecoinpaymentsInvoiceProjectRecord_Storage_Field {
return StripecoinpaymentsInvoiceProjectRecord_Storage_Field{_set: true, _value: v}
}
func (f StripecoinpaymentsInvoiceProjectRecord_Storage_Field) value() interface{} {
if !f._set || f._null {
return nil
}
return f._value
}
func (StripecoinpaymentsInvoiceProjectRecord_Storage_Field) _Column() string { return "storage" }
type StripecoinpaymentsInvoiceProjectRecord_Egress_Field struct {
_set bool
_null bool
_value int64
}
func StripecoinpaymentsInvoiceProjectRecord_Egress(v int64) StripecoinpaymentsInvoiceProjectRecord_Egress_Field {
return StripecoinpaymentsInvoiceProjectRecord_Egress_Field{_set: true, _value: v}
}
func (f StripecoinpaymentsInvoiceProjectRecord_Egress_Field) value() interface{} {
if !f._set || f._null {
return nil
}
return f._value
}
func (StripecoinpaymentsInvoiceProjectRecord_Egress_Field) _Column() string { return "egress" }
type StripecoinpaymentsInvoiceProjectRecord_Objects_Field struct {
_set bool
_null bool
_value int64
}
func StripecoinpaymentsInvoiceProjectRecord_Objects(v int64) StripecoinpaymentsInvoiceProjectRecord_Objects_Field {
return StripecoinpaymentsInvoiceProjectRecord_Objects_Field{_set: true, _value: v}
}
func (f StripecoinpaymentsInvoiceProjectRecord_Objects_Field) value() interface{} {
if !f._set || f._null {
return nil
}
return f._value
}
func (StripecoinpaymentsInvoiceProjectRecord_Objects_Field) _Column() string { return "objects" }
type StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field struct {
_set bool
_null bool
_value time.Time
}
func StripecoinpaymentsInvoiceProjectRecord_PeriodStart(v time.Time) StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field {
return StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field{_set: true, _value: v}
}
func (f StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field) value() interface{} {
if !f._set || f._null {
return nil
}
return f._value
}
func (StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field) _Column() string {
return "period_start"
}
type StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field struct {
_set bool
_null bool
_value time.Time
}
func StripecoinpaymentsInvoiceProjectRecord_PeriodEnd(v time.Time) StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field {
return StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field{_set: true, _value: v}
}
func (f StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field) value() interface{} {
if !f._set || f._null {
return nil
}
return f._value
}
func (StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field) _Column() string { return "period_end" }
type StripecoinpaymentsInvoiceProjectRecord_State_Field struct {
_set bool
_null bool
_value int
}
func StripecoinpaymentsInvoiceProjectRecord_State(v int) StripecoinpaymentsInvoiceProjectRecord_State_Field {
return StripecoinpaymentsInvoiceProjectRecord_State_Field{_set: true, _value: v}
}
func (f StripecoinpaymentsInvoiceProjectRecord_State_Field) value() interface{} {
if !f._set || f._null {
return nil
}
return f._value
}
func (StripecoinpaymentsInvoiceProjectRecord_State_Field) _Column() string { return "state" }
type StripecoinpaymentsInvoiceProjectRecord_CreatedAt_Field struct {
_set bool
_null bool
_value time.Time
}
func StripecoinpaymentsInvoiceProjectRecord_CreatedAt(v time.Time) StripecoinpaymentsInvoiceProjectRecord_CreatedAt_Field {
return StripecoinpaymentsInvoiceProjectRecord_CreatedAt_Field{_set: true, _value: v}
}
func (f StripecoinpaymentsInvoiceProjectRecord_CreatedAt_Field) value() interface{} {
if !f._set || f._null {
return nil
}
return f._value
}
func (StripecoinpaymentsInvoiceProjectRecord_CreatedAt_Field) _Column() string { return "created_at" }
type User struct {
Id []byte
Email string
@ -6451,6 +6657,42 @@ func (obj *postgresImpl) Create_StripecoinpaymentsApplyBalanceIntent(ctx context
}
func (obj *postgresImpl) Create_StripecoinpaymentsInvoiceProjectRecord(ctx context.Context,
stripecoinpayments_invoice_project_record_id StripecoinpaymentsInvoiceProjectRecord_Id_Field,
stripecoinpayments_invoice_project_record_project_id StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field,
stripecoinpayments_invoice_project_record_storage StripecoinpaymentsInvoiceProjectRecord_Storage_Field,
stripecoinpayments_invoice_project_record_egress StripecoinpaymentsInvoiceProjectRecord_Egress_Field,
stripecoinpayments_invoice_project_record_objects StripecoinpaymentsInvoiceProjectRecord_Objects_Field,
stripecoinpayments_invoice_project_record_period_start StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field,
stripecoinpayments_invoice_project_record_period_end StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field,
stripecoinpayments_invoice_project_record_state StripecoinpaymentsInvoiceProjectRecord_State_Field) (
stripecoinpayments_invoice_project_record *StripecoinpaymentsInvoiceProjectRecord, err error) {
__now := obj.db.Hooks.Now().UTC()
__id_val := stripecoinpayments_invoice_project_record_id.value()
__project_id_val := stripecoinpayments_invoice_project_record_project_id.value()
__storage_val := stripecoinpayments_invoice_project_record_storage.value()
__egress_val := stripecoinpayments_invoice_project_record_egress.value()
__objects_val := stripecoinpayments_invoice_project_record_objects.value()
__period_start_val := stripecoinpayments_invoice_project_record_period_start.value()
__period_end_val := stripecoinpayments_invoice_project_record_period_end.value()
__state_val := stripecoinpayments_invoice_project_record_state.value()
__created_at_val := __now
var __embed_stmt = __sqlbundle_Literal("INSERT INTO stripecoinpayments_invoice_project_records ( id, project_id, storage, egress, objects, period_start, period_end, state, created_at ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? ) RETURNING stripecoinpayments_invoice_project_records.id, stripecoinpayments_invoice_project_records.project_id, stripecoinpayments_invoice_project_records.storage, stripecoinpayments_invoice_project_records.egress, stripecoinpayments_invoice_project_records.objects, stripecoinpayments_invoice_project_records.period_start, stripecoinpayments_invoice_project_records.period_end, stripecoinpayments_invoice_project_records.state, stripecoinpayments_invoice_project_records.created_at")
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
obj.logStmt(__stmt, __id_val, __project_id_val, __storage_val, __egress_val, __objects_val, __period_start_val, __period_end_val, __state_val, __created_at_val)
stripecoinpayments_invoice_project_record = &StripecoinpaymentsInvoiceProjectRecord{}
err = obj.driver.QueryRow(__stmt, __id_val, __project_id_val, __storage_val, __egress_val, __objects_val, __period_start_val, __period_end_val, __state_val, __created_at_val).Scan(&stripecoinpayments_invoice_project_record.Id, &stripecoinpayments_invoice_project_record.ProjectId, &stripecoinpayments_invoice_project_record.Storage, &stripecoinpayments_invoice_project_record.Egress, &stripecoinpayments_invoice_project_record.Objects, &stripecoinpayments_invoice_project_record.PeriodStart, &stripecoinpayments_invoice_project_record.PeriodEnd, &stripecoinpayments_invoice_project_record.State, &stripecoinpayments_invoice_project_record.CreatedAt)
if err != nil {
return nil, obj.makeErr(err)
}
return stripecoinpayments_invoice_project_record, nil
}
func (obj *postgresImpl) Get_ValueAttribution_By_ProjectId_And_BucketName(ctx context.Context,
value_attribution_project_id ValueAttribution_ProjectId_Field,
value_attribution_bucket_name ValueAttribution_BucketName_Field) (
@ -6969,6 +7211,42 @@ func (obj *postgresImpl) All_Project_By_ProjectMember_MemberId_OrderBy_Asc_Proje
}
func (obj *postgresImpl) Limited_Project_By_CreatedAt_Less_OrderBy_Asc_CreatedAt(ctx context.Context,
project_created_at_less Project_CreatedAt_Field,
limit int, offset int64) (
rows []*Project, err error) {
var __embed_stmt = __sqlbundle_Literal("SELECT projects.id, projects.name, projects.description, projects.usage_limit, projects.partner_id, projects.owner_id, projects.created_at FROM projects WHERE projects.created_at < ? ORDER BY projects.created_at LIMIT ? OFFSET ?")
var __values []interface{}
__values = append(__values, project_created_at_less.value())
__values = append(__values, limit, offset)
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
obj.logStmt(__stmt, __values...)
__rows, err := obj.driver.Query(__stmt, __values...)
if err != nil {
return nil, obj.makeErr(err)
}
defer __rows.Close()
for __rows.Next() {
project := &Project{}
err = __rows.Scan(&project.Id, &project.Name, &project.Description, &project.UsageLimit, &project.PartnerId, &project.OwnerId, &project.CreatedAt)
if err != nil {
return nil, obj.makeErr(err)
}
rows = append(rows, project)
}
if err := __rows.Err(); err != nil {
return nil, obj.makeErr(err)
}
return rows, nil
}
func (obj *postgresImpl) Get_ProjectInvoiceStamp_By_ProjectId_And_StartDate(ctx context.Context,
project_invoice_stamp_project_id ProjectInvoiceStamp_ProjectId_Field,
project_invoice_stamp_start_date ProjectInvoiceStamp_StartDate_Field) (
@ -7939,6 +8217,42 @@ func (obj *postgresImpl) Get_StripeCustomer_CustomerId_By_UserId(ctx context.Con
}
func (obj *postgresImpl) Limited_StripeCustomer_By_CreatedAt_LessOrEqual_OrderBy_Desc_CreatedAt(ctx context.Context,
stripe_customer_created_at_less_or_equal StripeCustomer_CreatedAt_Field,
limit int, offset int64) (
rows []*StripeCustomer, err error) {
var __embed_stmt = __sqlbundle_Literal("SELECT stripe_customers.user_id, stripe_customers.customer_id, stripe_customers.created_at FROM stripe_customers WHERE stripe_customers.created_at <= ? ORDER BY stripe_customers.created_at DESC LIMIT ? OFFSET ?")
var __values []interface{}
__values = append(__values, stripe_customer_created_at_less_or_equal.value())
__values = append(__values, limit, offset)
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
obj.logStmt(__stmt, __values...)
__rows, err := obj.driver.Query(__stmt, __values...)
if err != nil {
return nil, obj.makeErr(err)
}
defer __rows.Close()
for __rows.Next() {
stripe_customer := &StripeCustomer{}
err = __rows.Scan(&stripe_customer.UserId, &stripe_customer.CustomerId, &stripe_customer.CreatedAt)
if err != nil {
return nil, obj.makeErr(err)
}
rows = append(rows, stripe_customer)
}
if err := __rows.Err(); err != nil {
return nil, obj.makeErr(err)
}
return rows, nil
}
func (obj *postgresImpl) Limited_CoinpaymentsTransaction_By_CreatedAt_LessOrEqual_And_Status_OrderBy_Desc_CreatedAt(ctx context.Context,
coinpayments_transaction_created_at_less_or_equal CoinpaymentsTransaction_CreatedAt_Field,
coinpayments_transaction_status CoinpaymentsTransaction_Status_Field,
@ -7976,6 +8290,66 @@ func (obj *postgresImpl) Limited_CoinpaymentsTransaction_By_CreatedAt_LessOrEqua
}
func (obj *postgresImpl) Get_StripecoinpaymentsInvoiceProjectRecord_By_ProjectId_And_PeriodStart_And_PeriodEnd(ctx context.Context,
stripecoinpayments_invoice_project_record_project_id StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field,
stripecoinpayments_invoice_project_record_period_start StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field,
stripecoinpayments_invoice_project_record_period_end StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field) (
stripecoinpayments_invoice_project_record *StripecoinpaymentsInvoiceProjectRecord, err error) {
var __embed_stmt = __sqlbundle_Literal("SELECT stripecoinpayments_invoice_project_records.id, stripecoinpayments_invoice_project_records.project_id, stripecoinpayments_invoice_project_records.storage, stripecoinpayments_invoice_project_records.egress, stripecoinpayments_invoice_project_records.objects, stripecoinpayments_invoice_project_records.period_start, stripecoinpayments_invoice_project_records.period_end, stripecoinpayments_invoice_project_records.state, stripecoinpayments_invoice_project_records.created_at FROM stripecoinpayments_invoice_project_records WHERE stripecoinpayments_invoice_project_records.project_id = ? AND stripecoinpayments_invoice_project_records.period_start = ? AND stripecoinpayments_invoice_project_records.period_end = ?")
var __values []interface{}
__values = append(__values, stripecoinpayments_invoice_project_record_project_id.value(), stripecoinpayments_invoice_project_record_period_start.value(), stripecoinpayments_invoice_project_record_period_end.value())
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
obj.logStmt(__stmt, __values...)
stripecoinpayments_invoice_project_record = &StripecoinpaymentsInvoiceProjectRecord{}
err = obj.driver.QueryRow(__stmt, __values...).Scan(&stripecoinpayments_invoice_project_record.Id, &stripecoinpayments_invoice_project_record.ProjectId, &stripecoinpayments_invoice_project_record.Storage, &stripecoinpayments_invoice_project_record.Egress, &stripecoinpayments_invoice_project_record.Objects, &stripecoinpayments_invoice_project_record.PeriodStart, &stripecoinpayments_invoice_project_record.PeriodEnd, &stripecoinpayments_invoice_project_record.State, &stripecoinpayments_invoice_project_record.CreatedAt)
if err != nil {
return nil, obj.makeErr(err)
}
return stripecoinpayments_invoice_project_record, nil
}
func (obj *postgresImpl) Limited_StripecoinpaymentsInvoiceProjectRecord_By_CreatedAt_LessOrEqual_And_State_OrderBy_Desc_CreatedAt(ctx context.Context,
stripecoinpayments_invoice_project_record_created_at_less_or_equal StripecoinpaymentsInvoiceProjectRecord_CreatedAt_Field,
stripecoinpayments_invoice_project_record_state StripecoinpaymentsInvoiceProjectRecord_State_Field,
limit int, offset int64) (
rows []*StripecoinpaymentsInvoiceProjectRecord, err error) {
var __embed_stmt = __sqlbundle_Literal("SELECT stripecoinpayments_invoice_project_records.id, stripecoinpayments_invoice_project_records.project_id, stripecoinpayments_invoice_project_records.storage, stripecoinpayments_invoice_project_records.egress, stripecoinpayments_invoice_project_records.objects, stripecoinpayments_invoice_project_records.period_start, stripecoinpayments_invoice_project_records.period_end, stripecoinpayments_invoice_project_records.state, stripecoinpayments_invoice_project_records.created_at FROM stripecoinpayments_invoice_project_records WHERE stripecoinpayments_invoice_project_records.created_at <= ? AND stripecoinpayments_invoice_project_records.state = ? ORDER BY stripecoinpayments_invoice_project_records.created_at DESC LIMIT ? OFFSET ?")
var __values []interface{}
__values = append(__values, stripecoinpayments_invoice_project_record_created_at_less_or_equal.value(), stripecoinpayments_invoice_project_record_state.value())
__values = append(__values, limit, offset)
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
obj.logStmt(__stmt, __values...)
__rows, err := obj.driver.Query(__stmt, __values...)
if err != nil {
return nil, obj.makeErr(err)
}
defer __rows.Close()
for __rows.Next() {
stripecoinpayments_invoice_project_record := &StripecoinpaymentsInvoiceProjectRecord{}
err = __rows.Scan(&stripecoinpayments_invoice_project_record.Id, &stripecoinpayments_invoice_project_record.ProjectId, &stripecoinpayments_invoice_project_record.Storage, &stripecoinpayments_invoice_project_record.Egress, &stripecoinpayments_invoice_project_record.Objects, &stripecoinpayments_invoice_project_record.PeriodStart, &stripecoinpayments_invoice_project_record.PeriodEnd, &stripecoinpayments_invoice_project_record.State, &stripecoinpayments_invoice_project_record.CreatedAt)
if err != nil {
return nil, obj.makeErr(err)
}
rows = append(rows, stripecoinpayments_invoice_project_record)
}
if err := __rows.Err(); err != nil {
return nil, obj.makeErr(err)
}
return rows, nil
}
func (obj *postgresImpl) Update_PendingAudits_By_NodeId(ctx context.Context,
pending_audits_node_id PendingAudits_NodeId_Field,
update PendingAudits_Update_Fields) (
@ -9081,6 +9455,46 @@ func (obj *postgresImpl) Update_StripecoinpaymentsApplyBalanceIntent_By_TxId(ctx
return stripecoinpayments_apply_balance_intent, nil
}
func (obj *postgresImpl) Update_StripecoinpaymentsInvoiceProjectRecord_By_Id(ctx context.Context,
stripecoinpayments_invoice_project_record_id StripecoinpaymentsInvoiceProjectRecord_Id_Field,
update StripecoinpaymentsInvoiceProjectRecord_Update_Fields) (
stripecoinpayments_invoice_project_record *StripecoinpaymentsInvoiceProjectRecord, err error) {
var __sets = &__sqlbundle_Hole{}
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPDATE stripecoinpayments_invoice_project_records SET "), __sets, __sqlbundle_Literal(" WHERE stripecoinpayments_invoice_project_records.id = ? RETURNING stripecoinpayments_invoice_project_records.id, stripecoinpayments_invoice_project_records.project_id, stripecoinpayments_invoice_project_records.storage, stripecoinpayments_invoice_project_records.egress, stripecoinpayments_invoice_project_records.objects, stripecoinpayments_invoice_project_records.period_start, stripecoinpayments_invoice_project_records.period_end, stripecoinpayments_invoice_project_records.state, stripecoinpayments_invoice_project_records.created_at")}}
__sets_sql := __sqlbundle_Literals{Join: ", "}
var __values []interface{}
var __args []interface{}
if update.State._set {
__values = append(__values, update.State.value())
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("state = ?"))
}
if len(__sets_sql.SQLs) == 0 {
return nil, emptyUpdate()
}
__args = append(__args, stripecoinpayments_invoice_project_record_id.value())
__values = append(__values, __args...)
__sets.SQL = __sets_sql
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
obj.logStmt(__stmt, __values...)
stripecoinpayments_invoice_project_record = &StripecoinpaymentsInvoiceProjectRecord{}
err = obj.driver.QueryRow(__stmt, __values...).Scan(&stripecoinpayments_invoice_project_record.Id, &stripecoinpayments_invoice_project_record.ProjectId, &stripecoinpayments_invoice_project_record.Storage, &stripecoinpayments_invoice_project_record.Egress, &stripecoinpayments_invoice_project_record.Objects, &stripecoinpayments_invoice_project_record.PeriodStart, &stripecoinpayments_invoice_project_record.PeriodEnd, &stripecoinpayments_invoice_project_record.State, &stripecoinpayments_invoice_project_record.CreatedAt)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, obj.makeErr(err)
}
return stripecoinpayments_invoice_project_record, nil
}
func (obj *postgresImpl) Delete_ValueAttribution_By_ProjectId_And_BucketName(ctx context.Context,
value_attribution_project_id ValueAttribution_ProjectId_Field,
value_attribution_bucket_name ValueAttribution_BucketName_Field) (
@ -9554,6 +9968,32 @@ func (obj *postgresImpl) Delete_StripecoinpaymentsApplyBalanceIntent_By_TxId(ctx
}
func (obj *postgresImpl) Delete_StripecoinpaymentsInvoiceProjectRecord_By_Id(ctx context.Context,
stripecoinpayments_invoice_project_record_id StripecoinpaymentsInvoiceProjectRecord_Id_Field) (
deleted bool, err error) {
var __embed_stmt = __sqlbundle_Literal("DELETE FROM stripecoinpayments_invoice_project_records WHERE stripecoinpayments_invoice_project_records.id = ?")
var __values []interface{}
__values = append(__values, stripecoinpayments_invoice_project_record_id.value())
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
obj.logStmt(__stmt, __values...)
__res, err := obj.driver.Exec(__stmt, __values...)
if err != nil {
return false, obj.makeErr(err)
}
__count, err := __res.RowsAffected()
if err != nil {
return false, obj.makeErr(err)
}
return __count > 0, nil
}
func (impl postgresImpl) isConstraintError(err error) (
constraint string, ok bool) {
if e, ok := err.(*pq.Error); ok {
@ -9652,6 +10092,16 @@ func (obj *postgresImpl) deleteAll(ctx context.Context) (count int64, err error)
return 0, obj.makeErr(err)
}
__count, err = __res.RowsAffected()
if err != nil {
return 0, obj.makeErr(err)
}
count += __count
__res, err = obj.driver.Exec("DELETE FROM stripecoinpayments_invoice_project_records;")
if err != nil {
return 0, obj.makeErr(err)
}
__count, err = __res.RowsAffected()
if err != nil {
return 0, obj.makeErr(err)
@ -10437,6 +10887,24 @@ func (rx *Rx) Create_StripecoinpaymentsApplyBalanceIntent(ctx context.Context,
}
func (rx *Rx) Create_StripecoinpaymentsInvoiceProjectRecord(ctx context.Context,
stripecoinpayments_invoice_project_record_id StripecoinpaymentsInvoiceProjectRecord_Id_Field,
stripecoinpayments_invoice_project_record_project_id StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field,
stripecoinpayments_invoice_project_record_storage StripecoinpaymentsInvoiceProjectRecord_Storage_Field,
stripecoinpayments_invoice_project_record_egress StripecoinpaymentsInvoiceProjectRecord_Egress_Field,
stripecoinpayments_invoice_project_record_objects StripecoinpaymentsInvoiceProjectRecord_Objects_Field,
stripecoinpayments_invoice_project_record_period_start StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field,
stripecoinpayments_invoice_project_record_period_end StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field,
stripecoinpayments_invoice_project_record_state StripecoinpaymentsInvoiceProjectRecord_State_Field) (
stripecoinpayments_invoice_project_record *StripecoinpaymentsInvoiceProjectRecord, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Create_StripecoinpaymentsInvoiceProjectRecord(ctx, stripecoinpayments_invoice_project_record_id, stripecoinpayments_invoice_project_record_project_id, stripecoinpayments_invoice_project_record_storage, stripecoinpayments_invoice_project_record_egress, stripecoinpayments_invoice_project_record_objects, stripecoinpayments_invoice_project_record_period_start, stripecoinpayments_invoice_project_record_period_end, stripecoinpayments_invoice_project_record_state)
}
func (rx *Rx) Create_User(ctx context.Context,
user_id User_Id_Field,
user_email User_Email_Field,
@ -10649,6 +11117,16 @@ func (rx *Rx) Delete_StripecoinpaymentsApplyBalanceIntent_By_TxId(ctx context.Co
return tx.Delete_StripecoinpaymentsApplyBalanceIntent_By_TxId(ctx, stripecoinpayments_apply_balance_intent_tx_id)
}
func (rx *Rx) Delete_StripecoinpaymentsInvoiceProjectRecord_By_Id(ctx context.Context,
stripecoinpayments_invoice_project_record_id StripecoinpaymentsInvoiceProjectRecord_Id_Field) (
deleted bool, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Delete_StripecoinpaymentsInvoiceProjectRecord_By_Id(ctx, stripecoinpayments_invoice_project_record_id)
}
func (rx *Rx) Delete_User_By_Id(ctx context.Context,
user_id User_Id_Field) (
deleted bool, err error) {
@ -10940,6 +11418,18 @@ func (rx *Rx) Get_StripeCustomer_CustomerId_By_UserId(ctx context.Context,
return tx.Get_StripeCustomer_CustomerId_By_UserId(ctx, stripe_customer_user_id)
}
func (rx *Rx) Get_StripecoinpaymentsInvoiceProjectRecord_By_ProjectId_And_PeriodStart_And_PeriodEnd(ctx context.Context,
stripecoinpayments_invoice_project_record_project_id StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field,
stripecoinpayments_invoice_project_record_period_start StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field,
stripecoinpayments_invoice_project_record_period_end StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field) (
stripecoinpayments_invoice_project_record *StripecoinpaymentsInvoiceProjectRecord, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Get_StripecoinpaymentsInvoiceProjectRecord_By_ProjectId_And_PeriodStart_And_PeriodEnd(ctx, stripecoinpayments_invoice_project_record_project_id, stripecoinpayments_invoice_project_record_period_start, stripecoinpayments_invoice_project_record_period_end)
}
func (rx *Rx) Get_User_By_Id(ctx context.Context,
user_id User_Id_Field) (
user *User, err error) {
@ -11051,6 +11541,40 @@ func (rx *Rx) Limited_ProjectMember_By_ProjectId(ctx context.Context,
return tx.Limited_ProjectMember_By_ProjectId(ctx, project_member_project_id, limit, offset)
}
func (rx *Rx) Limited_Project_By_CreatedAt_Less_OrderBy_Asc_CreatedAt(ctx context.Context,
project_created_at_less Project_CreatedAt_Field,
limit int, offset int64) (
rows []*Project, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Limited_Project_By_CreatedAt_Less_OrderBy_Asc_CreatedAt(ctx, project_created_at_less, limit, offset)
}
func (rx *Rx) Limited_StripeCustomer_By_CreatedAt_LessOrEqual_OrderBy_Desc_CreatedAt(ctx context.Context,
stripe_customer_created_at_less_or_equal StripeCustomer_CreatedAt_Field,
limit int, offset int64) (
rows []*StripeCustomer, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Limited_StripeCustomer_By_CreatedAt_LessOrEqual_OrderBy_Desc_CreatedAt(ctx, stripe_customer_created_at_less_or_equal, limit, offset)
}
func (rx *Rx) Limited_StripecoinpaymentsInvoiceProjectRecord_By_CreatedAt_LessOrEqual_And_State_OrderBy_Desc_CreatedAt(ctx context.Context,
stripecoinpayments_invoice_project_record_created_at_less_or_equal StripecoinpaymentsInvoiceProjectRecord_CreatedAt_Field,
stripecoinpayments_invoice_project_record_state StripecoinpaymentsInvoiceProjectRecord_State_Field,
limit int, offset int64) (
rows []*StripecoinpaymentsInvoiceProjectRecord, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Limited_StripecoinpaymentsInvoiceProjectRecord_By_CreatedAt_LessOrEqual_And_State_OrderBy_Desc_CreatedAt(ctx, stripecoinpayments_invoice_project_record_created_at_less_or_equal, stripecoinpayments_invoice_project_record_state, limit, offset)
}
func (rx *Rx) UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
accounting_timestamps_name AccountingTimestamps_Name_Field,
update AccountingTimestamps_Update_Fields) (
@ -11219,6 +11743,17 @@ func (rx *Rx) Update_StripecoinpaymentsApplyBalanceIntent_By_TxId(ctx context.Co
return tx.Update_StripecoinpaymentsApplyBalanceIntent_By_TxId(ctx, stripecoinpayments_apply_balance_intent_tx_id, update)
}
func (rx *Rx) Update_StripecoinpaymentsInvoiceProjectRecord_By_Id(ctx context.Context,
stripecoinpayments_invoice_project_record_id StripecoinpaymentsInvoiceProjectRecord_Id_Field,
update StripecoinpaymentsInvoiceProjectRecord_Update_Fields) (
stripecoinpayments_invoice_project_record *StripecoinpaymentsInvoiceProjectRecord, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Update_StripecoinpaymentsInvoiceProjectRecord_By_Id(ctx, stripecoinpayments_invoice_project_record_id, update)
}
func (rx *Rx) Update_User_By_Id(ctx context.Context,
user_id User_Id_Field,
update User_Update_Fields) (
@ -11501,6 +12036,17 @@ type Methods interface {
stripecoinpayments_apply_balance_intent_state StripecoinpaymentsApplyBalanceIntent_State_Field) (
stripecoinpayments_apply_balance_intent *StripecoinpaymentsApplyBalanceIntent, err error)
Create_StripecoinpaymentsInvoiceProjectRecord(ctx context.Context,
stripecoinpayments_invoice_project_record_id StripecoinpaymentsInvoiceProjectRecord_Id_Field,
stripecoinpayments_invoice_project_record_project_id StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field,
stripecoinpayments_invoice_project_record_storage StripecoinpaymentsInvoiceProjectRecord_Storage_Field,
stripecoinpayments_invoice_project_record_egress StripecoinpaymentsInvoiceProjectRecord_Egress_Field,
stripecoinpayments_invoice_project_record_objects StripecoinpaymentsInvoiceProjectRecord_Objects_Field,
stripecoinpayments_invoice_project_record_period_start StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field,
stripecoinpayments_invoice_project_record_period_end StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field,
stripecoinpayments_invoice_project_record_state StripecoinpaymentsInvoiceProjectRecord_State_Field) (
stripecoinpayments_invoice_project_record *StripecoinpaymentsInvoiceProjectRecord, err error)
Create_User(ctx context.Context,
user_id User_Id_Field,
user_email User_Email_Field,
@ -11593,6 +12139,10 @@ type Methods interface {
stripecoinpayments_apply_balance_intent_tx_id StripecoinpaymentsApplyBalanceIntent_TxId_Field) (
deleted bool, err error)
Delete_StripecoinpaymentsInvoiceProjectRecord_By_Id(ctx context.Context,
stripecoinpayments_invoice_project_record_id StripecoinpaymentsInvoiceProjectRecord_Id_Field) (
deleted bool, err error)
Delete_User_By_Id(ctx context.Context,
user_id User_Id_Field) (
deleted bool, err error)
@ -11716,6 +12266,12 @@ type Methods interface {
stripe_customer_user_id StripeCustomer_UserId_Field) (
row *CustomerId_Row, err error)
Get_StripecoinpaymentsInvoiceProjectRecord_By_ProjectId_And_PeriodStart_And_PeriodEnd(ctx context.Context,
stripecoinpayments_invoice_project_record_project_id StripecoinpaymentsInvoiceProjectRecord_ProjectId_Field,
stripecoinpayments_invoice_project_record_period_start StripecoinpaymentsInvoiceProjectRecord_PeriodStart_Field,
stripecoinpayments_invoice_project_record_period_end StripecoinpaymentsInvoiceProjectRecord_PeriodEnd_Field) (
stripecoinpayments_invoice_project_record *StripecoinpaymentsInvoiceProjectRecord, err error)
Get_User_By_Id(ctx context.Context,
user_id User_Id_Field) (
user *User, err error)
@ -11767,6 +12323,22 @@ type Methods interface {
limit int, offset int64) (
rows []*ProjectMember, err error)
Limited_Project_By_CreatedAt_Less_OrderBy_Asc_CreatedAt(ctx context.Context,
project_created_at_less Project_CreatedAt_Field,
limit int, offset int64) (
rows []*Project, err error)
Limited_StripeCustomer_By_CreatedAt_LessOrEqual_OrderBy_Desc_CreatedAt(ctx context.Context,
stripe_customer_created_at_less_or_equal StripeCustomer_CreatedAt_Field,
limit int, offset int64) (
rows []*StripeCustomer, err error)
Limited_StripecoinpaymentsInvoiceProjectRecord_By_CreatedAt_LessOrEqual_And_State_OrderBy_Desc_CreatedAt(ctx context.Context,
stripecoinpayments_invoice_project_record_created_at_less_or_equal StripecoinpaymentsInvoiceProjectRecord_CreatedAt_Field,
stripecoinpayments_invoice_project_record_state StripecoinpaymentsInvoiceProjectRecord_State_Field,
limit int, offset int64) (
rows []*StripecoinpaymentsInvoiceProjectRecord, err error)
UpdateNoReturn_AccountingTimestamps_By_Name(ctx context.Context,
accounting_timestamps_name AccountingTimestamps_Name_Field,
update AccountingTimestamps_Update_Fields) (
@ -11845,6 +12417,11 @@ type Methods interface {
update StripecoinpaymentsApplyBalanceIntent_Update_Fields) (
stripecoinpayments_apply_balance_intent *StripecoinpaymentsApplyBalanceIntent, err error)
Update_StripecoinpaymentsInvoiceProjectRecord_By_Id(ctx context.Context,
stripecoinpayments_invoice_project_record_id StripecoinpaymentsInvoiceProjectRecord_Id_Field,
update StripecoinpaymentsInvoiceProjectRecord_Update_Fields) (
stripecoinpayments_invoice_project_record *StripecoinpaymentsInvoiceProjectRecord, err error)
Update_User_By_Id(ctx context.Context,
user_id User_Id_Field,
update User_Update_Fields) (

View File

@ -211,6 +211,19 @@ CREATE TABLE stripe_customers (
PRIMARY KEY ( user_id ),
UNIQUE ( customer_id )
);
CREATE TABLE stripecoinpayments_invoice_project_records (
id bytea NOT NULL,
project_id bytea NOT NULL,
storage double precision NOT NULL,
egress bigint NOT NULL,
objects bigint NOT NULL,
period_start timestamp with time zone NOT NULL,
period_end timestamp with time zone NOT NULL,
state integer NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY ( id ),
UNIQUE ( project_id, period_start, period_end )
);
CREATE TABLE users (
id bytea NOT NULL,
email text NOT NULL,

View File

@ -0,0 +1,163 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package satellitedb
import (
"context"
"database/sql"
"time"
"github.com/skyrings/skyring-common/tools/uuid"
"github.com/zeebo/errs"
"storj.io/storj/satellite/payments/stripecoinpayments"
dbx "storj.io/storj/satellite/satellitedb/dbx"
)
// ensure that invoiceProjectRecords implements stripecoinpayments.ProjectRecordsDB.
var _ stripecoinpayments.ProjectRecordsDB = (*invoiceProjectRecords)(nil)
// invoiceProjectRecordState defines states of the invoice project record.
type invoiceProjectRecordState int
const (
// invoice project record is not yet applied to customer invoice.
invoiceProjectRecordStateUnapplied invoiceProjectRecordState = 0
// invoice project record has been used during creating customer invoice.
invoiceProjectRecordStateConsumed invoiceProjectRecordState = 1
)
// Int returns intent state as int.
func (intent invoiceProjectRecordState) Int() int {
return int(intent)
}
// invoiceProjectRecords is stripecoinpayments project records DB.
//
// architecture: Database
type invoiceProjectRecords struct {
db *dbx.DB
}
// Create creates new invoice project record in the DB.
func (db *invoiceProjectRecords) Create(ctx context.Context, records []stripecoinpayments.CreateProjectRecord, start, end time.Time) (err error) {
defer mon.Task()(&ctx)(&err)
return db.db.WithTx(ctx, func(ctx context.Context, tx *dbx.Tx) error {
for _, record := range records {
id, err := uuid.New()
if err != nil {
return Error.Wrap(err)
}
_, err = db.db.Create_StripecoinpaymentsInvoiceProjectRecord(ctx,
dbx.StripecoinpaymentsInvoiceProjectRecord_Id(id[:]),
dbx.StripecoinpaymentsInvoiceProjectRecord_ProjectId(record.ProjectID[:]),
dbx.StripecoinpaymentsInvoiceProjectRecord_Storage(record.Storage),
dbx.StripecoinpaymentsInvoiceProjectRecord_Egress(record.Egress),
dbx.StripecoinpaymentsInvoiceProjectRecord_Objects(record.Objects),
dbx.StripecoinpaymentsInvoiceProjectRecord_PeriodStart(start),
dbx.StripecoinpaymentsInvoiceProjectRecord_PeriodEnd(end),
dbx.StripecoinpaymentsInvoiceProjectRecord_State(invoiceProjectRecordStateUnapplied.Int()),
)
if err != nil {
return err
}
}
return nil
})
}
// Check checks if invoice project record for specified project and billing period exists.
func (db *invoiceProjectRecords) Check(ctx context.Context, projectID uuid.UUID, start, end time.Time) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = db.db.Get_StripecoinpaymentsInvoiceProjectRecord_By_ProjectId_And_PeriodStart_And_PeriodEnd(ctx,
dbx.StripecoinpaymentsInvoiceProjectRecord_ProjectId(projectID[:]),
dbx.StripecoinpaymentsInvoiceProjectRecord_PeriodStart(start),
dbx.StripecoinpaymentsInvoiceProjectRecord_PeriodEnd(end),
)
if err != nil {
if err == sql.ErrNoRows {
return nil
}
return err
}
return stripecoinpayments.ErrProjectRecordExists
}
// Consume consumes invoice project record.
func (db *invoiceProjectRecords) Consume(ctx context.Context, id uuid.UUID) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = db.db.Update_StripecoinpaymentsInvoiceProjectRecord_By_Id(ctx,
dbx.StripecoinpaymentsInvoiceProjectRecord_Id(id[:]),
dbx.StripecoinpaymentsInvoiceProjectRecord_Update_Fields{
State: dbx.StripecoinpaymentsInvoiceProjectRecord_State(invoiceProjectRecordStateConsumed.Int()),
},
)
return err
}
// ListUnapplied returns project records page with unapplied project records.
func (db *invoiceProjectRecords) ListUnapplied(ctx context.Context, offset int64, limit int, before time.Time) (_ stripecoinpayments.ProjectRecordsPage, err error) {
defer mon.Task()(&ctx)(&err)
var page stripecoinpayments.ProjectRecordsPage
dbxRecords, err := db.db.Limited_StripecoinpaymentsInvoiceProjectRecord_By_CreatedAt_LessOrEqual_And_State_OrderBy_Desc_CreatedAt(ctx,
dbx.StripecoinpaymentsInvoiceProjectRecord_CreatedAt(before),
dbx.StripecoinpaymentsInvoiceProjectRecord_State(invoiceProjectRecordStateUnapplied.Int()),
limit+1,
offset,
)
if err != nil {
return stripecoinpayments.ProjectRecordsPage{}, err
}
if len(dbxRecords) == limit+1 {
page.Next = true
page.NextOffset = offset + int64(limit) + 1
dbxRecords = dbxRecords[:len(dbxRecords)-1]
}
for _, dbxRecord := range dbxRecords {
record, err := fromDBXInvoiceProjectRecord(dbxRecord)
if err != nil {
return stripecoinpayments.ProjectRecordsPage{}, err
}
page.Records = append(page.Records, *record)
}
return page, nil
}
// fromDBXInvoiceProjectRecord converts *dbx.StripecoinpaymentsInvoiceProjectRecord to *stripecoinpayments.ProjectRecord
func fromDBXInvoiceProjectRecord(dbxRecord *dbx.StripecoinpaymentsInvoiceProjectRecord) (*stripecoinpayments.ProjectRecord, error) {
id, err := bytesToUUID(dbxRecord.Id)
if err != nil {
return nil, errs.Wrap(err)
}
projectID, err := bytesToUUID(dbxRecord.ProjectId)
if err != nil {
return nil, errs.Wrap(err)
}
return &stripecoinpayments.ProjectRecord{
ID: id,
ProjectID: projectID,
Storage: dbxRecord.Storage,
Egress: dbxRecord.Egress,
Objects: dbxRecord.Objects,
PeriodStart: dbxRecord.PeriodStart,
PeriodEnd: dbxRecord.PeriodEnd,
}, nil
}

View File

@ -1360,6 +1360,26 @@ func (db *DB) PostgresMigration() *migrate.Migration {
`DROP TABLE bucket_usages CASCADE;`,
},
},
{
DB: db.db,
Description: "Add stripecoinpayments_invoice_project_records",
Version: 65,
Action: migrate.SQL{
`CREATE TABLE stripecoinpayments_invoice_project_records (
id bytea NOT NULL,
project_id bytea NOT NULL,
storage double precision NOT NULL,
egress bigint NOT NULL,
objects bigint NOT NULL,
period_start timestamp with time zone NOT NULL,
period_end timestamp with time zone NOT NULL,
state integer NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY ( id ),
UNIQUE ( project_id, period_start, period_end )
);`,
},
},
},
}
}

View File

@ -124,6 +124,37 @@ func (projects *projects) Update(ctx context.Context, project *console.Project)
return err
}
// List returns paginated projects, created before provided timestamp.
func (projects *projects) List(ctx context.Context, offset int64, limit int, before time.Time) (_ console.ProjectsPage, err error) {
defer mon.Task()(&ctx)(&err)
var page console.ProjectsPage
dbxProjects, err := projects.db.Limited_Project_By_CreatedAt_Less_OrderBy_Asc_CreatedAt(ctx,
dbx.Project_CreatedAt(before.UTC()),
limit+1,
offset,
)
if err != nil {
return console.ProjectsPage{}, err
}
if len(dbxProjects) == limit+1 {
page.Next = true
page.NextOffset = offset + int64(limit) + 1
dbxProjects = dbxProjects[:len(dbxProjects)-1]
}
projs, err := projectsFromDbxSlice(ctx, dbxProjects)
if err != nil {
return console.ProjectsPage{}, err
}
page.Projects = projs
return page, nil
}
// projectFromDBX is used for creating Project entity from autogenerated dbx.Project struct
func projectFromDBX(ctx context.Context, project *dbx.Project) (_ *console.Project, err error) {
defer mon.Task()(&ctx)(&err)

View File

@ -0,0 +1,34 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package satellitedb
import (
"storj.io/storj/satellite/payments/stripecoinpayments"
dbx "storj.io/storj/satellite/satellitedb/dbx"
)
// ensures that *stripeCoinPaymentsDB implements stripecoinpayments.DB.
var _ stripecoinpayments.DB = (*stripeCoinPaymentsDB)(nil)
// stripeCoinPaymentsDB is stripecoinpayments DB.
//
// architecture: Database
type stripeCoinPaymentsDB struct {
db *dbx.DB
}
// Customers is getter for customers db.
func (db *stripeCoinPaymentsDB) Customers() stripecoinpayments.CustomersDB {
return &customers{db: db.db}
}
// Transactions is getter for transactions db.
func (db *stripeCoinPaymentsDB) Transactions() stripecoinpayments.TransactionsDB {
return &coinPaymentsTransactions{db: db.db}
}
// ProjectRecords is getter for invoice project records db.
func (db *stripeCoinPaymentsDB) ProjectRecords() stripecoinpayments.ProjectRecordsDB {
return &invoiceProjectRecords{db: db.db}
}

View File

@ -0,0 +1,420 @@
-- AUTOGENERATED BY gopkg.in/spacemonkeygo/dbx.v1
-- DO NOT EDIT
CREATE TABLE accounting_rollups
(
id bigserial NOT NULL,
node_id bytea NOT NULL,
start_time timestamp with time zone NOT NULL,
put_total bigint NOT NULL,
get_total bigint NOT NULL,
get_audit_total bigint NOT NULL,
get_repair_total bigint NOT NULL,
put_repair_total bigint NOT NULL,
at_rest_total double precision NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE accounting_timestamps
(
name text NOT NULL,
value timestamp with time zone NOT NULL,
PRIMARY KEY (name)
);
CREATE TABLE bucket_bandwidth_rollups
(
bucket_name bytea NOT NULL,
project_id bytea NOT NULL,
interval_start timestamp NOT NULL,
interval_seconds integer NOT NULL,
action integer NOT NULL,
inline bigint NOT NULL,
allocated bigint NOT NULL,
settled bigint NOT NULL,
PRIMARY KEY (bucket_name, project_id, interval_start, action)
);
CREATE TABLE bucket_storage_tallies
(
bucket_name bytea NOT NULL,
project_id bytea NOT NULL,
interval_start timestamp NOT NULL,
inline bigint NOT NULL,
remote bigint NOT NULL,
remote_segments_count integer NOT NULL,
inline_segments_count integer NOT NULL,
object_count integer NOT NULL,
metadata_size bigint NOT NULL,
PRIMARY KEY (bucket_name, project_id, interval_start)
);
CREATE TABLE injuredsegments
(
path bytea NOT NULL,
data bytea NOT NULL,
attempted timestamp,
PRIMARY KEY (path)
);
CREATE TABLE irreparabledbs
(
segmentpath bytea NOT NULL,
segmentdetail bytea NOT NULL,
pieces_lost_count bigint NOT NULL,
seg_damaged_unix_sec bigint NOT NULL,
repair_attempt_count bigint NOT NULL,
PRIMARY KEY (segmentpath)
);
CREATE TABLE nodes
(
id bytea NOT NULL,
address text NOT NULL,
last_net text NOT NULL,
protocol integer NOT NULL,
type integer NOT NULL,
email text NOT NULL,
wallet text NOT NULL,
free_bandwidth bigint NOT NULL,
free_disk bigint NOT NULL,
piece_count bigint NOT NULL,
major bigint NOT NULL,
minor bigint NOT NULL,
patch bigint NOT NULL,
hash text NOT NULL,
timestamp timestamp with time zone NOT NULL,
release boolean NOT NULL,
latency_90 bigint NOT NULL,
audit_success_count bigint NOT NULL,
total_audit_count bigint NOT NULL,
uptime_success_count bigint NOT NULL,
total_uptime_count bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
last_contact_success timestamp with time zone NOT NULL,
last_contact_failure timestamp with time zone NOT NULL,
contained boolean NOT NULL,
disqualified timestamp with time zone,
audit_reputation_alpha double precision NOT NULL,
audit_reputation_beta double precision NOT NULL,
uptime_reputation_alpha double precision NOT NULL,
uptime_reputation_beta double precision NOT NULL,
exit_initiated_at timestamp,
exit_loop_completed_at timestamp,
exit_finished_at timestamp,
exit_success boolean NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE offers
(
id serial NOT NULL,
name text NOT NULL,
description text NOT NULL,
award_credit_in_cents integer NOT NULL,
invitee_credit_in_cents integer NOT NULL,
award_credit_duration_days integer,
invitee_credit_duration_days integer,
redeemable_cap integer,
expires_at timestamp with time zone NOT NULL,
created_at timestamp with time zone NOT NULL,
status integer NOT NULL,
type integer NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE peer_identities
(
node_id bytea NOT NULL,
leaf_serial_number bytea NOT NULL,
chain bytea NOT NULL,
updated_at timestamp with time zone NOT NULL,
PRIMARY KEY (node_id)
);
CREATE TABLE pending_audits
(
node_id bytea NOT NULL,
piece_id bytea NOT NULL,
stripe_index bigint NOT NULL,
share_size bigint NOT NULL,
expected_share_hash bytea NOT NULL,
reverify_count bigint NOT NULL,
path bytea NOT NULL,
PRIMARY KEY (node_id)
);
CREATE TABLE projects
(
id bytea NOT NULL,
name text NOT NULL,
description text NOT NULL,
usage_limit bigint NOT NULL,
partner_id bytea,
owner_id bytea NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE registration_tokens
(
secret bytea NOT NULL,
owner_id bytea,
project_limit integer NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY (secret),
UNIQUE (owner_id)
);
CREATE TABLE reset_password_tokens
(
secret bytea NOT NULL,
owner_id bytea NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY (secret),
UNIQUE (owner_id)
);
CREATE TABLE serial_numbers
(
id serial NOT NULL,
serial_number bytea NOT NULL,
bucket_id bytea NOT NULL,
expires_at timestamp NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE storagenode_bandwidth_rollups
(
storagenode_id bytea NOT NULL,
interval_start timestamp NOT NULL,
interval_seconds integer NOT NULL,
action integer NOT NULL,
allocated bigint NOT NULL,
settled bigint NOT NULL,
PRIMARY KEY (storagenode_id, interval_start, action)
);
CREATE TABLE storagenode_storage_tallies
(
id bigserial NOT NULL,
node_id bytea NOT NULL,
interval_end_time timestamp with time zone NOT NULL,
data_total double precision NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE users (
id bytea NOT NULL,
email text NOT NULL,
normalized_email text NOT NULL,
full_name text NOT NULL,
short_name text,
password_hash bytea NOT NULL,
status integer NOT NULL,
partner_id bytea,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY ( id )
);
CREATE TABLE value_attributions
(
project_id bytea NOT NULL,
bucket_name bytea NOT NULL,
partner_id bytea NOT NULL,
last_updated timestamp NOT NULL,
PRIMARY KEY (project_id, bucket_name)
);
CREATE TABLE api_keys
(
id bytea NOT NULL,
project_id bytea NOT NULL REFERENCES projects (id) ON DELETE CASCADE,
head bytea NOT NULL,
name text NOT NULL,
secret bytea NOT NULL,
partner_id bytea,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY (id),
UNIQUE (head),
UNIQUE (name, project_id)
);
CREATE TABLE bucket_metainfos
(
id bytea NOT NULL,
project_id bytea NOT NULL REFERENCES projects (id),
name bytea NOT NULL,
partner_id bytea,
path_cipher integer NOT NULL,
created_at timestamp with time zone NOT NULL,
default_segment_size integer NOT NULL,
default_encryption_cipher_suite integer NOT NULL,
default_encryption_block_size integer NOT NULL,
default_redundancy_algorithm integer NOT NULL,
default_redundancy_share_size integer NOT NULL,
default_redundancy_required_shares integer NOT NULL,
default_redundancy_repair_shares integer NOT NULL,
default_redundancy_optimal_shares integer NOT NULL,
default_redundancy_total_shares integer NOT NULL,
PRIMARY KEY (id),
UNIQUE (name, project_id)
);
CREATE TABLE project_invoice_stamps
(
project_id bytea NOT NULL REFERENCES projects (id) ON DELETE CASCADE,
invoice_id bytea NOT NULL,
start_date timestamp with time zone NOT NULL,
end_date timestamp with time zone NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY (project_id, start_date, end_date),
UNIQUE (invoice_id)
);
CREATE TABLE project_members
(
member_id bytea NOT NULL REFERENCES users (id) ON DELETE CASCADE,
project_id bytea NOT NULL REFERENCES projects (id) ON DELETE CASCADE,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY (member_id, project_id)
);
CREATE TABLE used_serials
(
serial_number_id integer NOT NULL REFERENCES serial_numbers (id) ON DELETE CASCADE,
storage_node_id bytea NOT NULL,
PRIMARY KEY (serial_number_id, storage_node_id)
);
CREATE TABLE user_credits
(
id serial NOT NULL,
user_id bytea NOT NULL REFERENCES users (id) ON DELETE CASCADE,
offer_id integer NOT NULL REFERENCES offers (id),
referred_by bytea REFERENCES users (id) ON DELETE SET NULL,
type text NOT NULL,
credits_earned_in_cents integer NOT NULL,
credits_used_in_cents integer NOT NULL,
expires_at timestamp with time zone NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE graceful_exit_progress (
node_id bytea NOT NULL,
bytes_transferred bigint NOT NULL,
pieces_transferred bigint NOT NULL,
pieces_failed bigint NOT NULL,
updated_at timestamp NOT NULL,
PRIMARY KEY ( node_id )
);
CREATE TABLE graceful_exit_transfer_queue (
node_id bytea NOT NULL,
path bytea NOT NULL,
piece_num integer NOT NULL,
durability_ratio double precision NOT NULL,
queued_at timestamp NOT NULL,
requested_at timestamp,
last_failed_at timestamp,
last_failed_code integer,
failed_count integer,
finished_at timestamp,
PRIMARY KEY ( node_id, path, piece_num )
);
CREATE TABLE stripe_customers (
user_id bytea NOT NULL,
customer_id text NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY ( user_id ),
UNIQUE ( customer_id )
);
CREATE TABLE coinpayments_transactions (
id text NOT NULL,
user_id bytea NOT NULL,
address text NOT NULL,
amount bytea NOT NULL,
received bytea NOT NULL,
status integer NOT NULL,
key text NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY ( id )
);
CREATE TABLE stripecoinpayments_apply_balance_intents (
tx_id text NOT NULL REFERENCES coinpayments_transactions( id ) ON DELETE CASCADE,
state integer NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY ( tx_id )
);
CREATE TABLE stripecoinpayments_invoice_project_records (
id bytea NOT NULL,
project_id bytea NOT NULL,
storage double precision NOT NULL,
egress bigint NOT NULL,
objects bigint NOT NULL,
period_start timestamp with time zone NOT NULL,
period_end timestamp with time zone NOT NULL,
state integer NOT NULL,
created_at timestamp with time zone NOT NULL,
PRIMARY KEY ( id ),
UNIQUE ( project_id, period_start, period_end )
);
CREATE INDEX bucket_name_project_id_interval_start_interval_seconds ON bucket_bandwidth_rollups ( bucket_name, project_id, interval_start, interval_seconds );
CREATE INDEX node_last_ip ON nodes ( last_net );
CREATE UNIQUE INDEX serial_number ON serial_numbers ( serial_number );
CREATE INDEX serial_numbers_expires_at_index ON serial_numbers ( expires_at );
CREATE INDEX storagenode_id_interval_start_interval_seconds ON storagenode_bandwidth_rollups ( storagenode_id, interval_start, interval_seconds );
CREATE UNIQUE INDEX credits_earned_user_id_offer_id ON user_credits (id, offer_id) WHERE credits_earned_in_cents=0;
INSERT INTO "accounting_rollups"("id", "node_id", "start_time", "put_total", "get_total", "get_audit_total", "get_repair_total", "put_repair_total", "at_rest_total") VALUES (1, E'\\367M\\177\\251]t/\\022\\256\\214\\265\\025\\224\\204:\\217\\212\\0102<\\321\\374\\020&\\271Qc\\325\\261\\354\\246\\233'::bytea, '2019-02-09 00:00:00+00', 1000, 2000, 3000, 4000, 0, 5000);
INSERT INTO "accounting_timestamps" VALUES ('LastAtRestTally', '0001-01-01 00:00:00+00');
INSERT INTO "accounting_timestamps" VALUES ('LastRollup', '0001-01-01 00:00:00+00');
INSERT INTO "accounting_timestamps" VALUES ('LastBandwidthTally', '0001-01-01 00:00:00+00');
INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "piece_count", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta", "exit_success") VALUES (E'\\153\\313\\233\\074\\327\\177\\136\\070\\346\\001', '127.0.0.1:55516', '', 0, 4, '', '', -1, -1, 0, 0, 1, 0, '', 'epoch', false, 0, 0, 5, 0, 5, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 50, 5, 100, 5, false);
INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "piece_count", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta", "exit_success") VALUES (E'\\006\\223\\250R\\221\\005\\365\\377v>0\\266\\365\\216\\255?\\347\\244\\371?2\\264\\262\\230\\007<\\001\\262\\263\\237\\247n', '127.0.0.1:55518', '', 0, 4, '', '', -1, -1, 0, 0, 1, 0, '', 'epoch', false, 0, 0, 0, 3, 3, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 50, 0, 100, 0, false);
INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "piece_count", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta", "exit_success") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014', '127.0.0.1:55517', '', 0, 4, '', '', -1, -1, 0, 0, 1, 0, '', 'epoch', false, 0, 0, 0, 0, 0, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 50, 0, 100, 0, false);
INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "piece_count", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta", "exit_success") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\015', '127.0.0.1:55519', '', 0, 4, '', '', -1, -1, 0, 0, 1, 0, '', 'epoch', false, 0, 1, 2, 1, 2, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 50, 1, 100, 1, false);
INSERT INTO "nodes"("id", "address", "last_net", "protocol", "type", "email", "wallet", "free_bandwidth", "free_disk", "piece_count", "major", "minor", "patch", "hash", "timestamp", "release","latency_90", "audit_success_count", "total_audit_count", "uptime_success_count", "total_uptime_count", "created_at", "updated_at", "last_contact_success", "last_contact_failure", "contained", "disqualified", "audit_reputation_alpha", "audit_reputation_beta", "uptime_reputation_alpha", "uptime_reputation_beta", "exit_success") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\016', '127.0.0.1:55520', '', 0, 4, '', '', -1, -1, 0, 0, 1, 0, '', 'epoch', false, 0, 300, 400, 300, 400, '2019-02-14 08:07:31.028103+00', '2019-02-14 08:07:31.108963+00', 'epoch', 'epoch', false, NULL, 300, 100, 300, 100, false);
INSERT INTO "users"("id", "full_name", "short_name", "email", "normalized_email", "password_hash", "status", "partner_id", "created_at") VALUES (E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 'Noahson', 'William', '1email1@mail.test', '1EMAIL1@MAIL.TEST', E'some_readable_hash'::bytea, 1, NULL, '2019-02-14 08:28:24.614594+00');
INSERT INTO "projects"("id", "name", "description", "usage_limit", "partner_id", "owner_id", "created_at") VALUES (E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, 'ProjectName', 'projects description', 0, NULL, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, '2019-02-14 08:28:24.254934+00');
INSERT INTO "projects"("id", "name", "description", "usage_limit", "partner_id", "owner_id", "created_at") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea, 'projName1', 'Test project 1', 0, NULL, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, '2019-02-14 08:28:24.636949+00');
INSERT INTO "project_members"("member_id", "project_id", "created_at") VALUES (E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea, '2019-02-14 08:28:24.677953+00');
INSERT INTO "project_members"("member_id", "project_id", "created_at") VALUES (E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, '2019-02-13 08:28:24.677953+00');
INSERT INTO "irreparabledbs" ("segmentpath", "segmentdetail", "pieces_lost_count", "seg_damaged_unix_sec", "repair_attempt_count") VALUES ('\x49616d5365676d656e746b6579696e666f30', '\x49616d5365676d656e7464657461696c696e666f30', 10, 1550159554, 10);
INSERT INTO "injuredsegments" ("path", "data") VALUES ('0', '\x0a0130120100');
INSERT INTO "injuredsegments" ("path", "data") VALUES ('here''s/a/great/path', '\x0a136865726527732f612f67726561742f70617468120a0102030405060708090a');
INSERT INTO "injuredsegments" ("path", "data") VALUES ('yet/another/cool/path', '\x0a157965742f616e6f746865722f636f6f6c2f70617468120a0102030405060708090a');
INSERT INTO "injuredsegments" ("path", "data") VALUES ('so/many/iconic/paths/to/choose/from', '\x0a23736f2f6d616e792f69636f6e69632f70617468732f746f2f63686f6f73652f66726f6d120a0102030405060708090a');
INSERT INTO "registration_tokens" ("secret", "owner_id", "project_limit", "created_at") VALUES (E'\\070\\127\\144\\013\\332\\344\\102\\376\\306\\056\\303\\130\\106\\132\\321\\276\\321\\274\\170\\264\\054\\333\\221\\116\\154\\221\\335\\070\\220\\146\\344\\216'::bytea, null, 1, '2019-02-14 08:28:24.677953+00');
INSERT INTO "serial_numbers" ("id", "serial_number", "bucket_id", "expires_at") VALUES (1, E'0123456701234567'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014/testbucket'::bytea, '2019-03-06 08:28:24.677953+00');
INSERT INTO "used_serials" ("serial_number_id", "storage_node_id") VALUES (1, E'\\006\\223\\250R\\221\\005\\365\\377v>0\\266\\365\\216\\255?\\347\\244\\371?2\\264\\262\\230\\007<\\001\\262\\263\\237\\247n');
INSERT INTO "storagenode_bandwidth_rollups" ("storagenode_id", "interval_start", "interval_seconds", "action", "allocated", "settled") VALUES (E'\\006\\223\\250R\\221\\005\\365\\377v>0\\266\\365\\216\\255?\\347\\244\\371?2\\264\\262\\230\\007<\\001\\262\\263\\237\\247n', '2019-03-06 08:00:00.000000+00', 3600, 1, 1024, 2024);
INSERT INTO "storagenode_storage_tallies" VALUES (1, E'\\3510\\323\\225"~\\036<\\342\\330m\\0253Jhr\\246\\233K\\246#\\2303\\351\\256\\275j\\212UM\\362\\207', '2019-02-14 08:16:57.812849+00', 1000);
INSERT INTO "bucket_bandwidth_rollups" ("bucket_name", "project_id", "interval_start", "interval_seconds", "action", "inline", "allocated", "settled") VALUES (E'testbucket'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea,'2019-03-06 08:00:00.000000+00', 3600, 1, 1024, 2024, 3024);
INSERT INTO "bucket_storage_tallies" ("bucket_name", "project_id", "interval_start", "inline", "remote", "remote_segments_count", "inline_segments_count", "object_count", "metadata_size") VALUES (E'testbucket'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea,'2019-03-06 08:00:00.000000+00', 4024, 5024, 0, 0, 0, 0);
INSERT INTO "bucket_bandwidth_rollups" ("bucket_name", "project_id", "interval_start", "interval_seconds", "action", "inline", "allocated", "settled") VALUES (E'testbucket'::bytea, E'\\170\\160\\157\\370\\274\\366\\113\\364\\272\\235\\301\\243\\321\\102\\321\\136'::bytea,'2019-03-06 08:00:00.000000+00', 3600, 1, 1024, 2024, 3024);
INSERT INTO "bucket_storage_tallies" ("bucket_name", "project_id", "interval_start", "inline", "remote", "remote_segments_count", "inline_segments_count", "object_count", "metadata_size") VALUES (E'testbucket'::bytea, E'\\170\\160\\157\\370\\274\\366\\113\\364\\272\\235\\301\\243\\321\\102\\321\\136'::bytea,'2019-03-06 08:00:00.000000+00', 4024, 5024, 0, 0, 0, 0);
INSERT INTO "reset_password_tokens" ("secret", "owner_id", "created_at") VALUES (E'\\070\\127\\144\\013\\332\\344\\102\\376\\306\\056\\303\\130\\106\\132\\321\\276\\321\\274\\170\\264\\054\\333\\221\\116\\154\\221\\335\\070\\220\\146\\344\\216'::bytea, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, '2019-05-08 08:28:24.677953+00');
INSERT INTO "offers" ("name", "description", "award_credit_in_cents", "invitee_credit_in_cents", "award_credit_duration_days", "invitee_credit_duration_days", "redeemable_cap", "expires_at", "created_at", "status", "type") VALUES ('testOffer', 'Test offer 1', 0, 0, 14, 14, 50, '2019-03-14 08:28:24.636949+00', '2019-02-14 08:28:24.636949+00', 0, 0);
INSERT INTO "offers" ("name","description","award_credit_in_cents","award_credit_duration_days", "invitee_credit_in_cents","invitee_credit_duration_days", "expires_at","created_at","status","type") VALUES ('Default free credit offer','Is active when no active free credit offer',0, NULL,300, 14, '2119-03-14 08:28:24.636949+00','2019-07-14 08:28:24.636949+00',1,1);
INSERT INTO "api_keys" ("id", "project_id", "head", "name", "secret", "partner_id", "created_at") VALUES (E'\\334/\\302;\\225\\355O\\323\\276f\\247\\354/6\\241\\033'::bytea, E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, E'\\111\\142\\147\\304\\132\\375\\070\\163\\270\\160\\251\\370\\126\\063\\351\\037\\257\\071\\143\\375\\351\\320\\253\\232\\220\\260\\075\\173\\306\\307\\115\\136'::bytea, 'key 2', E'\\254\\011\\315\\333\\273\\365\\001\\071\\024\\154\\253\\332\\301\\216\\361\\074\\221\\367\\251\\231\\274\\333\\300\\367\\001\\272\\327\\111\\315\\123\\042\\016'::bytea, NULL, '2019-02-14 08:28:24.267934+00');
INSERT INTO "project_invoice_stamps" ("project_id", "invoice_id", "start_date", "end_date", "created_at") VALUES (E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, E'\\363\\311\\033w\\222\\303,'::bytea, '2019-06-01 08:28:24.267934+00', '2019-06-29 08:28:24.267934+00', '2019-06-01 08:28:24.267934+00');
INSERT INTO "value_attributions" ("project_id", "bucket_name", "partner_id", "last_updated") VALUES (E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, E''::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea,'2019-02-14 08:07:31.028103+00');
INSERT INTO "user_credits" ("id", "user_id", "offer_id", "referred_by", "credits_earned_in_cents", "credits_used_in_cents", "type", "expires_at", "created_at") VALUES (1, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 1, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 200, 0, 'invalid', '2019-10-01 08:28:24.267934+00', '2019-06-01 08:28:24.267934+00');
INSERT INTO "bucket_metainfos" ("id", "project_id", "name", "partner_id", "created_at", "path_cipher", "default_segment_size", "default_encryption_cipher_suite", "default_encryption_block_size", "default_redundancy_algorithm", "default_redundancy_share_size", "default_redundancy_required_shares", "default_redundancy_repair_shares", "default_redundancy_optimal_shares", "default_redundancy_total_shares") VALUES (E'\\334/\\302;\\225\\355O\\323\\276f\\247\\354/6\\241\\033'::bytea, E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, E'testbucketuniquename'::bytea, NULL, '2019-06-14 08:28:24.677953+00', 1, 65536, 1, 8192, 1, 4096, 4, 6, 8, 10);
INSERT INTO "pending_audits" ("node_id", "piece_id", "stripe_index", "share_size", "expected_share_hash", "reverify_count", "path") VALUES (E'\\153\\313\\233\\074\\327\\177\\136\\070\\346\\001'::bytea, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 5, 1024, E'\\070\\127\\144\\013\\332\\344\\102\\376\\306\\056\\303\\130\\106\\132\\321\\276\\321\\274\\170\\264\\054\\333\\221\\116\\154\\221\\335\\070\\220\\146\\344\\216'::bytea, 1, 'not null');
INSERT INTO "peer_identities" VALUES (E'\\334/\\302;\\225\\355O\\323\\276f\\247\\354/6\\241\\033'::bytea, E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\014'::bytea, E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, '2019-02-14 08:07:31.335028+00');
INSERT INTO "graceful_exit_progress" ("node_id", "bytes_transferred", "pieces_transferred", "pieces_failed", "updated_at") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\016', 1000000000000000, 0, 0, '2019-09-12 10:07:31.028103');
INSERT INTO "graceful_exit_transfer_queue" ("node_id", "path", "piece_num", "durability_ratio", "queued_at", "requested_at", "last_failed_at", "last_failed_code", "failed_count", "finished_at") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\016', E'f8419768-5baa-4901-b3ba-62808013ec45/s0/test3/\\240\\243\\223n \\334~b}\\2624)\\250m\\201\\202\\235\\276\\361\\3304\\323\\352\\311\\361\\353;\\326\\311', 8, 1.0, '2019-09-12 10:07:31.028103', '2019-09-12 10:07:32.028103', null, null, 0, '2019-09-12 10:07:33.028103');
INSERT INTO "graceful_exit_transfer_queue" ("node_id", "path", "piece_num", "durability_ratio", "queued_at", "requested_at", "last_failed_at", "last_failed_code", "failed_count", "finished_at") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\016', E'f8419768-5baa-4901-b3ba-62808013ec45/s0/test3/\\240\\243\\223n \\334~b}\\2624)\\250m\\201\\202\\235\\276\\361\\3304\\323\\352\\311\\361\\353;\\326\\312', 8, 1.0, '2019-09-12 10:07:31.028103', '2019-09-12 10:07:32.028103', null, null, 0, '2019-09-12 10:07:33.028103');
INSERT INTO "stripe_customers" ("user_id", "customer_id", "created_at") VALUES (E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 'stripe_id', '2019-06-01 08:28:24.267934+00');
INSERT INTO "coinpayments_transactions" ("id", "user_id", "address", "amount", "received", "status", "key", "created_at") VALUES ('tx_id', E'\\363\\311\\033w\\222\\303Ci\\265\\343U\\303\\312\\204",'::bytea, 'address', E'\\363\\311\\033w'::bytea, E'\\363\\311\\033w'::bytea, 1, 'key', '2019-06-01 08:28:24.267934+00');
INSERT INTO "graceful_exit_transfer_queue" ("node_id", "path", "piece_num", "durability_ratio", "queued_at", "requested_at", "last_failed_at", "last_failed_code", "failed_count", "finished_at") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\016', E'f8419768-5baa-4901-b3ba-62808013ec45/s0/test3/\\240\\243\\223n \\334~b}\\2624)\\250m\\201\\202\\235\\276\\361\\3304\\323\\352\\311\\361\\353;\\326\\311', 9, 1.0, '2019-09-12 10:07:31.028103', '2019-09-12 10:07:32.028103', null, null, 0, '2019-09-12 10:07:33.028103');
INSERT INTO "graceful_exit_transfer_queue" ("node_id", "path", "piece_num", "durability_ratio", "queued_at", "requested_at", "last_failed_at", "last_failed_code", "failed_count", "finished_at") VALUES (E'\\363\\342\\363\\371>+F\\256\\263\\300\\273|\\342N\\347\\016', E'f8419768-5baa-4901-b3ba-62808013ec45/s0/test3/\\240\\243\\223n \\334~b}\\2624)\\250m\\201\\202\\235\\276\\361\\3304\\323\\352\\311\\361\\353;\\326\\312', 9, 1.0, '2019-09-12 10:07:31.028103', '2019-09-12 10:07:32.028103', null, null, 0, '2019-09-12 10:07:33.028103');
INSERT INTO "stripecoinpayments_apply_balance_intents" ("tx_id", "state", "created_at") VALUES ('tx_id', 0, '2019-06-01 08:28:24.267934+00');
-- NEW DATA --
INSERT INTO "stripecoinpayments_invoice_project_records"("id", "project_id", "storage", "egress", "objects", "period_start", "period_end", "state", "created_at") VALUES (E'\\022\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, E'\\021\\217/\\014\\376!K\\023\\276\\031\\311}m\\236\\205\\300'::bytea, 0, 0, 0, '2019-06-01 08:28:24.267934+00', '2019-06-01 08:28:24.267934+00', 0, '2019-06-01 08:28:24.267934+00')