diff --git a/services/skus/controllers.go b/services/skus/controllers.go index 8a5af61b1..54fc0d50d 100644 --- a/services/skus/controllers.go +++ b/services/skus/controllers.go @@ -596,6 +596,9 @@ func CreateOrderCreds(svc *Service) handlers.AppHandler { if err := svc.CreateOrderItemCredentials(ctx, *orderID.UUID(), req.ItemID, reqID, req.BlindedCreds); err != nil { lg.Error().Err(err).Msg("failed to create the order credentials") + if errors.Is(err, errCredsAlreadySubmittedMismatch) { + return handlers.WrapError(err, "Order credentials already exist", http.StatusConflict) + } return handlers.WrapError(err, "Error creating order creds", http.StatusBadRequest) } @@ -651,6 +654,9 @@ func createItemCreds(svc *Service) handlers.AppHandler { if err := svc.CreateOrderItemCredentials(ctx, *orderID.UUID(), *itemID.UUID(), *reqID.UUID(), req.BlindedCreds); err != nil { lg.Error().Err(err).Msg("failed to create the order credentials") + if errors.Is(err, errCredsAlreadySubmittedMismatch) { + return handlers.WrapError(err, "Order credentials already exist", http.StatusConflict) + } return handlers.WrapError(err, "Error creating order creds", http.StatusBadRequest) } diff --git a/services/skus/credentials.go b/services/skus/credentials.go index c17a8b1a2..1749891e5 100644 --- a/services/skus/credentials.go +++ b/services/skus/credentials.go @@ -37,12 +37,13 @@ var ( ErrOrderHasNoItems model.Error = "order has no items" ErrCredsAlreadyExist model.Error = "credentials already exist" - errInvalidIssuerResp model.Error = "invalid issuer response" - errInvalidNCredsSingleUse model.Error = "submitted more blinded creds than quantity of order item" - errInvalidNCredsTlv2 model.Error = "submitted more blinded creds than allowed for order" - errUnsupportedCredType model.Error = "unsupported credential type" - errItemDoesNotExist model.Error = "order item does not exist for order" - errCredsAlreadySubmitted model.Error = "credentials already submitted" + errInvalidIssuerResp model.Error = "invalid issuer response" + errInvalidNCredsSingleUse model.Error = "submitted more blinded creds than quantity of order item" + errInvalidNCredsTlv2 model.Error = "submitted more blinded creds than allowed for order" + errUnsupportedCredType model.Error = "unsupported credential type" + errItemDoesNotExist model.Error = "order item does not exist for order" + errCredsAlreadySubmitted model.Error = "credentials already submitted" + errCredsAlreadySubmittedMismatch model.Error = "credentials already submitted with a different request" defaultExpiresAt = time.Now().Add(17532 * time.Hour) // 2 years retryPolicy = retrypolicy.DefaultRetry @@ -254,7 +255,7 @@ func (s *Service) CreateOrderItemCredentials(ctx context.Context, orderID, itemI return errItemDoesNotExist } - if err := s.doCredentialsExist(ctx, orderItem, blindedCreds); err != nil { + if err := s.doCredentialsExist(ctx, requestID, orderItem, blindedCreds); err != nil { if errors.Is(err, errCredsAlreadySubmitted) { return nil } @@ -307,7 +308,7 @@ func (s *Service) CreateOrderItemCredentials(ctx context.Context, orderID, itemI return nil } -func (s *Service) doCredentialsExist(ctx context.Context, item *model.OrderItem, blindedCreds []string) error { +func (s *Service) doCredentialsExist(ctx context.Context, requestID uuid.UUID, item *model.OrderItem, blindedCreds []string) error { switch item.CredentialType { case timeLimitedV2: // NOTE: This creates a possible race to submit between clients. @@ -318,27 +319,31 @@ func (s *Service) doCredentialsExist(ctx context.Context, item *model.OrderItem, // As a result, one client will successfully unblind the credentials and // the others will fail. - return s.doTLV2Exist(ctx, item, blindedCreds) + return s.doTLV2Exist(ctx, requestID, item, blindedCreds) default: return s.doCredsExist(ctx, item) } } -func (s *Service) doTLV2Exist(ctx context.Context, item *model.OrderItem, blindedCreds []string) error { +func (s *Service) doTLV2Exist(ctx context.Context, requestID uuid.UUID, item *model.OrderItem, blindedCreds []string) error { if item.CredentialType != timeLimitedV2 { return errUnsupportedCredType } // Check TLV2 to see if we have credentials signed that match incoming blinded tokens. - alreadySubmitted, err := s.Datastore.AreTimeLimitedV2CredsSubmitted(ctx, blindedCreds...) + credsSubmitted, err := s.Datastore.AreTimeLimitedV2CredsSubmitted(ctx, requestID, blindedCreds...) if err != nil { return fmt.Errorf("error validating credentials exist for order item: %w", err) } - if alreadySubmitted { + if credsSubmitted.AlreadySubmitted { // No need to create order credentials, since these are already submitted. return errCredsAlreadySubmitted } + if credsSubmitted.Mismatch { + // conflict because those credentials were submitted with a different request id + return errCredsAlreadySubmittedMismatch + } // Check if we have signed credentials for this order item. // If there is no order and no creds, we can submit again. diff --git a/services/skus/datastore.go b/services/skus/datastore.go index 67937c96b..2fb98df98 100644 --- a/services/skus/datastore.go +++ b/services/skus/datastore.go @@ -80,7 +80,7 @@ type Datastore interface { GetOrderCreds(orderID uuid.UUID, isSigned bool) ([]OrderCreds, error) SendSigningRequest(ctx context.Context, signingRequestWriter SigningRequestWriter) error InsertSignedOrderCredentialsTx(ctx context.Context, tx *sqlx.Tx, signedOrderResult *SigningOrderResult) error - AreTimeLimitedV2CredsSubmitted(ctx context.Context, blindedCreds ...string) (bool, error) + AreTimeLimitedV2CredsSubmitted(ctx context.Context, requestID uuid.UUID, blindedCreds ...string) (*AreTimeLimitedV2CredsSubmittedResult, error) GetTimeLimitedV2OrderCredsByOrder(orderID uuid.UUID) (*TimeLimitedV2Creds, error) GetTLV2Creds(ctx context.Context, dbi sqlx.QueryerContext, ordID, itemID, reqID uuid.UUID) (*TimeLimitedV2Creds, error) DeleteTimeLimitedV2OrderCredsByOrderTx(ctx context.Context, tx *sqlx.Tx, orderID uuid.UUID) error @@ -950,24 +950,40 @@ type TimeAwareSubIssuedCreds struct { RequestID string `json:"-" db:"request_id"` } -func (pg *Postgres) AreTimeLimitedV2CredsSubmitted(ctx context.Context, blindedCreds ...string) (bool, error) { +type AreTimeLimitedV2CredsSubmittedResult struct { + AlreadySubmitted bool `db:"already_submitted"` + Mismatch bool `db:"mismatch"` +} + +func (pg *Postgres) AreTimeLimitedV2CredsSubmitted(ctx context.Context, requestID uuid.UUID, blindedCreds ...string) (*AreTimeLimitedV2CredsSubmittedResult, error) { + return areTimeLimitedV2CredsSubmitted(ctx, pg.RawDB(), requestID, blindedCreds...) +} + +type getContext interface { + GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error +} + +func areTimeLimitedV2CredsSubmitted(ctx context.Context, dbi getContext, requestID uuid.UUID, blindedCreds ...string) (*AreTimeLimitedV2CredsSubmittedResult, error) { if len(blindedCreds) < 1 { - return false, errors.New("invalid parameter to tlv2 creds signed") + return nil, errors.New("invalid parameter to tlv2 creds signed") } + var result = AreTimeLimitedV2CredsSubmittedResult{} + query := ` select exists( select 1 from time_limited_v2_order_creds where blinded_creds->>0 = $1 - ) + ) as already_submitted, + exists( + select 1 from time_limited_v2_order_creds where blinded_creds->>0 = $1 and request_id != $2 + ) as mismatch ` - - var alreadySubmitted bool - err := pg.RawDB().Get(&alreadySubmitted, query, blindedCreds[0]) + err := dbi.GetContext(ctx, &result, query, blindedCreds[0], requestID) if err != nil { - return false, err + return nil, err } - return alreadySubmitted, nil + return &result, nil } // GetTimeLimitedV2OrderCredsByOrder returns all the non expired time limited v2 order credentials for a given order. diff --git a/services/skus/datastore_noint_test.go b/services/skus/datastore_noint_test.go new file mode 100644 index 000000000..6fddab83d --- /dev/null +++ b/services/skus/datastore_noint_test.go @@ -0,0 +1,70 @@ +package skus + +import ( + "context" + "testing" + + "github.com/golang/mock/gomock" + uuid "github.com/satori/go.uuid" + should "github.com/stretchr/testify/assert" +) + +type mockGetContext struct { + getContext func(ctx context.Context, dest interface{}, query string, args ...interface{}) error +} + +func (mgc *mockGetContext) GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error { + if mgc.getContext != nil { + return mgc.getContext(ctx, dest, query, args) + } + return nil +} + +func TestAreTimeLimitedV2CredsSubmitted(t *testing.T) { + type tcExpected struct { + result map[string]bool + noErr bool + } + + type testCase struct { + name string + dbi getContext + given uuid.UUID + exp tcExpected + } + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + tests := []testCase{ + { + name: "mismatch", + dbi: &mockGetContext{ + getContext: func(ctx context.Context, dest interface{}, query string, args ...interface{}) error { + *dest.(*AreTimeLimitedV2CredsSubmittedResult) = AreTimeLimitedV2CredsSubmittedResult{ + AlreadySubmitted: true, + Mismatch: true, + } + return nil + }, + }, + given: uuid.Must(uuid.FromString("8f51f9ca-b593-4200-9bfb-91ac34748e09")), + exp: tcExpected{ + noErr: true, + result: map[string]bool{ + "mismatch": true, + }, + }, + }, + } + + for i := range tests { + tc := tests[i] + + t.Run(tc.name, func(t *testing.T) { + result, err := areTimeLimitedV2CredsSubmitted(context.TODO(), tc.dbi, tc.given, "") + should.Equal(t, tc.exp.result["mismatch"], result.Mismatch) + should.Equal(t, tc.exp.noErr, err == nil) + }) + } +} diff --git a/services/skus/instrumented_datastore.go b/services/skus/instrumented_datastore.go index f79130141..1907b5bac 100644 --- a/services/skus/instrumented_datastore.go +++ b/services/skus/instrumented_datastore.go @@ -1,9 +1,9 @@ -package skus - // Code generated by gowrap. DO NOT EDIT. // template: ../../.prom-gowrap.tmpl // gowrap: http://github.com/hexdigest/gowrap +package skus + //go:generate gowrap gen -p github.com/brave-intl/bat-go/services/skus -i Datastore -t ../../.prom-gowrap.tmpl -o instrumented_datastore.go -l "" import ( @@ -87,7 +87,7 @@ func (_d DatastoreWithPrometheus) AppendOrderMetadataInt64(ctx context.Context, } // AreTimeLimitedV2CredsSubmitted implements Datastore -func (_d DatastoreWithPrometheus) AreTimeLimitedV2CredsSubmitted(ctx context.Context, blindedCreds ...string) (b1 bool, err error) { +func (_d DatastoreWithPrometheus) AreTimeLimitedV2CredsSubmitted(ctx context.Context, requestID uuid.UUID, blindedCreds ...string) (ap1 *AreTimeLimitedV2CredsSubmittedResult, err error) { _since := time.Now() defer func() { result := "ok" @@ -97,7 +97,7 @@ func (_d DatastoreWithPrometheus) AreTimeLimitedV2CredsSubmitted(ctx context.Con datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "AreTimeLimitedV2CredsSubmitted", result).Observe(time.Since(_since).Seconds()) }() - return _d.base.AreTimeLimitedV2CredsSubmitted(ctx, blindedCreds...) + return _d.base.AreTimeLimitedV2CredsSubmitted(ctx, requestID, blindedCreds...) } // BeginTx implements Datastore @@ -450,8 +450,8 @@ func (_d DatastoreWithPrometheus) GetSumForTransactions(orderID uuid.UUID) (d1 d return _d.base.GetSumForTransactions(orderID) } -// GetTimeLimitedV2OrderCredsByOrder implements Datastore -func (_d DatastoreWithPrometheus) GetTimeLimitedV2OrderCredsByOrder(orderID uuid.UUID) (tp1 *TimeLimitedV2Creds, err error) { +// GetTLV2Creds implements Datastore +func (_d DatastoreWithPrometheus) GetTLV2Creds(ctx context.Context, dbi sqlx.QueryerContext, ordID uuid.UUID, itemID uuid.UUID, reqID uuid.UUID) (tp1 *TimeLimitedV2Creds, err error) { _since := time.Now() defer func() { result := "ok" @@ -459,13 +459,13 @@ func (_d DatastoreWithPrometheus) GetTimeLimitedV2OrderCredsByOrder(orderID uuid result = "error" } - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTimeLimitedV2OrderCredsByOrder", result).Observe(time.Since(_since).Seconds()) + datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTLV2Creds", result).Observe(time.Since(_since).Seconds()) }() - return _d.base.GetTimeLimitedV2OrderCredsByOrder(orderID) + return _d.base.GetTLV2Creds(ctx, dbi, ordID, itemID, reqID) } -// GetTimeLimitedV2OrderCredsByOrderItem implements Datastore -func (_d DatastoreWithPrometheus) GetTimeLimitedV2OrderCredsByOrderItem(itemID uuid.UUID) (tp1 *TimeLimitedV2Creds, err error) { +// GetTimeLimitedV2OrderCredsByOrder implements Datastore +func (_d DatastoreWithPrometheus) GetTimeLimitedV2OrderCredsByOrder(orderID uuid.UUID) (tp1 *TimeLimitedV2Creds, err error) { _since := time.Now() defer func() { result := "ok" @@ -473,13 +473,13 @@ func (_d DatastoreWithPrometheus) GetTimeLimitedV2OrderCredsByOrderItem(itemID u result = "error" } - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTimeLimitedV2OrderCredsByOrderItem", result).Observe(time.Since(_since).Seconds()) + datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTimeLimitedV2OrderCredsByOrder", result).Observe(time.Since(_since).Seconds()) }() - return _d.base.GetTimeLimitedV2OrderCredsByOrderItem(itemID) + return _d.base.GetTimeLimitedV2OrderCredsByOrder(orderID) } -// GetTLV2Creds implements Datastore -func (_d DatastoreWithPrometheus) GetTLV2Creds(ctx context.Context, dbi sqlx.QueryerContext, ordID, itemID, reqID uuid.UUID) (tp1 *TimeLimitedV2Creds, err error) { +// GetTimeLimitedV2OrderCredsByOrderItem implements Datastore +func (_d DatastoreWithPrometheus) GetTimeLimitedV2OrderCredsByOrderItem(itemID uuid.UUID) (tp1 *TimeLimitedV2Creds, err error) { _since := time.Now() defer func() { result := "ok" @@ -487,9 +487,9 @@ func (_d DatastoreWithPrometheus) GetTLV2Creds(ctx context.Context, dbi sqlx.Que result = "error" } - datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTLV2Creds", result).Observe(time.Since(_since).Seconds()) + datastoreDurationSummaryVec.WithLabelValues(_d.instanceName, "GetTimeLimitedV2OrderCredsByOrderItem", result).Observe(time.Since(_since).Seconds()) }() - return _d.base.GetTLV2Creds(ctx, dbi, ordID, itemID, reqID) + return _d.base.GetTimeLimitedV2OrderCredsByOrderItem(itemID) } // GetTransaction implements Datastore diff --git a/services/skus/mockdatastore.go b/services/skus/mockdatastore.go index 9375051c8..b7263a06d 100644 --- a/services/skus/mockdatastore.go +++ b/services/skus/mockdatastore.go @@ -9,6 +9,7 @@ import ( reflect "reflect" time "time" + datastore "github.com/brave-intl/bat-go/libs/datastore" inputs "github.com/brave-intl/bat-go/libs/inputs" model "github.com/brave-intl/bat-go/services/skus/model" v4 "github.com/golang-migrate/migrate/v4" @@ -84,22 +85,22 @@ func (mr *MockDatastoreMockRecorder) AppendOrderMetadataInt64(arg0, arg1, arg2, } // AreTimeLimitedV2CredsSubmitted mocks base method. -func (m *MockDatastore) AreTimeLimitedV2CredsSubmitted(ctx context.Context, blindedCreds ...string) (bool, error) { +func (m *MockDatastore) AreTimeLimitedV2CredsSubmitted(ctx context.Context, requestID go_uuid.UUID, blindedCreds ...string) (*AreTimeLimitedV2CredsSubmittedResult, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx} + varargs := []interface{}{ctx, requestID} for _, a := range blindedCreds { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "AreTimeLimitedV2CredsSubmitted", varargs...) - ret0, _ := ret[0].(bool) + ret0, _ := ret[0].(*AreTimeLimitedV2CredsSubmittedResult) ret1, _ := ret[1].(error) return ret0, ret1 } // AreTimeLimitedV2CredsSubmitted indicates an expected call of AreTimeLimitedV2CredsSubmitted. -func (mr *MockDatastoreMockRecorder) AreTimeLimitedV2CredsSubmitted(ctx interface{}, blindedCreds ...interface{}) *gomock.Call { +func (mr *MockDatastoreMockRecorder) AreTimeLimitedV2CredsSubmitted(ctx, requestID interface{}, blindedCreds ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx}, blindedCreds...) + varargs := append([]interface{}{ctx, requestID}, blindedCreds...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AreTimeLimitedV2CredsSubmitted", reflect.TypeOf((*MockDatastore)(nil).AreTimeLimitedV2CredsSubmitted), varargs...) } @@ -164,18 +165,18 @@ func (mr *MockDatastoreMockRecorder) CreateKey(merchant, name, encryptedSecretKe } // CreateOrder mocks base method. -func (m *MockDatastore) CreateOrder(ctx context.Context, dbi sqlx.ExtContext, req *model.OrderNew, items []model.OrderItem) (*model.Order, error) { +func (m *MockDatastore) CreateOrder(ctx context.Context, dbi sqlx.ExtContext, oreq *model.OrderNew, items []model.OrderItem) (*model.Order, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateOrder", ctx, dbi, req, items) + ret := m.ctrl.Call(m, "CreateOrder", ctx, dbi, oreq, items) ret0, _ := ret[0].(*model.Order) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateOrder indicates an expected call of CreateOrder. -func (mr *MockDatastoreMockRecorder) CreateOrder(ctx, dbi, req, items interface{}) *gomock.Call { +func (mr *MockDatastoreMockRecorder) CreateOrder(ctx, dbi, oreq, items interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrder", reflect.TypeOf((*MockDatastore)(nil).CreateOrder), ctx, dbi, req, items) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrder", reflect.TypeOf((*MockDatastore)(nil).CreateOrder), ctx, dbi, oreq, items) } // CreateTransaction mocks base method. @@ -476,6 +477,21 @@ func (mr *MockDatastoreMockRecorder) GetSumForTransactions(orderID interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSumForTransactions", reflect.TypeOf((*MockDatastore)(nil).GetSumForTransactions), orderID) } +// GetTLV2Creds mocks base method. +func (m *MockDatastore) GetTLV2Creds(ctx context.Context, dbi sqlx.QueryerContext, ordID, itemID, reqID go_uuid.UUID) (*TimeLimitedV2Creds, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTLV2Creds", ctx, dbi, ordID, itemID, reqID) + ret0, _ := ret[0].(*TimeLimitedV2Creds) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTLV2Creds indicates an expected call of GetTLV2Creds. +func (mr *MockDatastoreMockRecorder) GetTLV2Creds(ctx, dbi, ordID, itemID, reqID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTLV2Creds", reflect.TypeOf((*MockDatastore)(nil).GetTLV2Creds), ctx, dbi, ordID, itemID, reqID) +} + // GetTimeLimitedV2OrderCredsByOrder mocks base method. func (m *MockDatastore) GetTimeLimitedV2OrderCredsByOrder(orderID go_uuid.UUID) (*TimeLimitedV2Creds, error) { m.ctrl.T.Helper() @@ -506,21 +522,6 @@ func (mr *MockDatastoreMockRecorder) GetTimeLimitedV2OrderCredsByOrderItem(itemI return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeLimitedV2OrderCredsByOrderItem", reflect.TypeOf((*MockDatastore)(nil).GetTimeLimitedV2OrderCredsByOrderItem), itemID) } -// GetTLV2Creds mocks base method. -func (m *MockDatastore) GetTLV2Creds(ctx context.Context, dbi sqlx.QueryerContext, ordID, itemID, reqID go_uuid.UUID) (*TimeLimitedV2Creds, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTLV2Creds", ctx, dbi, ordID, itemID, reqID) - ret0, _ := ret[0].(*TimeLimitedV2Creds) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetTLV2Creds indicates an expected call of GetTLV2Creds. -func (mr *MockDatastoreMockRecorder) GetTLV2Creds(ctx, dbi, ordID, itemID, reqID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTLV2Creds", reflect.TypeOf((*MockDatastore)(nil).GetTLV2Creds), ctx, dbi, ordID, itemID, reqID) -} - // GetTransaction mocks base method. func (m *MockDatastore) GetTransaction(externalTransactionID string) (*Transaction, error) { m.ctrl.T.Helper() @@ -839,3 +840,422 @@ func (mr *MockDatastoreMockRecorder) UpdateTransaction(orderID, externalTransact mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTransaction", reflect.TypeOf((*MockDatastore)(nil).UpdateTransaction), orderID, externalTransactionID, status, currency, kind, amount) } + +// MockorderStore is a mock of orderStore interface. +type MockorderStore struct { + ctrl *gomock.Controller + recorder *MockorderStoreMockRecorder +} + +// MockorderStoreMockRecorder is the mock recorder for MockorderStore. +type MockorderStoreMockRecorder struct { + mock *MockorderStore +} + +// NewMockorderStore creates a new mock instance. +func NewMockorderStore(ctrl *gomock.Controller) *MockorderStore { + mock := &MockorderStore{ctrl: ctrl} + mock.recorder = &MockorderStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockorderStore) EXPECT() *MockorderStoreMockRecorder { + return m.recorder +} + +// AppendMetadata mocks base method. +func (m *MockorderStore) AppendMetadata(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, key, val string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AppendMetadata", ctx, dbi, id, key, val) + ret0, _ := ret[0].(error) + return ret0 +} + +// AppendMetadata indicates an expected call of AppendMetadata. +func (mr *MockorderStoreMockRecorder) AppendMetadata(ctx, dbi, id, key, val interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendMetadata", reflect.TypeOf((*MockorderStore)(nil).AppendMetadata), ctx, dbi, id, key, val) +} + +// AppendMetadataInt mocks base method. +func (m *MockorderStore) AppendMetadataInt(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, key string, val int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AppendMetadataInt", ctx, dbi, id, key, val) + ret0, _ := ret[0].(error) + return ret0 +} + +// AppendMetadataInt indicates an expected call of AppendMetadataInt. +func (mr *MockorderStoreMockRecorder) AppendMetadataInt(ctx, dbi, id, key, val interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendMetadataInt", reflect.TypeOf((*MockorderStore)(nil).AppendMetadataInt), ctx, dbi, id, key, val) +} + +// AppendMetadataInt64 mocks base method. +func (m *MockorderStore) AppendMetadataInt64(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, key string, val int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AppendMetadataInt64", ctx, dbi, id, key, val) + ret0, _ := ret[0].(error) + return ret0 +} + +// AppendMetadataInt64 indicates an expected call of AppendMetadataInt64. +func (mr *MockorderStoreMockRecorder) AppendMetadataInt64(ctx, dbi, id, key, val interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendMetadataInt64", reflect.TypeOf((*MockorderStore)(nil).AppendMetadataInt64), ctx, dbi, id, key, val) +} + +// Create mocks base method. +func (m *MockorderStore) Create(ctx context.Context, dbi sqlx.QueryerContext, oreq *model.OrderNew) (*model.Order, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, dbi, oreq) + ret0, _ := ret[0].(*model.Order) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockorderStoreMockRecorder) Create(ctx, dbi, oreq interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockorderStore)(nil).Create), ctx, dbi, oreq) +} + +// Get mocks base method. +func (m *MockorderStore) Get(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID) (*model.Order, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, dbi, id) + ret0, _ := ret[0].(*model.Order) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockorderStoreMockRecorder) Get(ctx, dbi, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockorderStore)(nil).Get), ctx, dbi, id) +} + +// GetByExternalID mocks base method. +func (m *MockorderStore) GetByExternalID(ctx context.Context, dbi sqlx.QueryerContext, extID string) (*model.Order, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByExternalID", ctx, dbi, extID) + ret0, _ := ret[0].(*model.Order) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByExternalID indicates an expected call of GetByExternalID. +func (mr *MockorderStoreMockRecorder) GetByExternalID(ctx, dbi, extID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByExternalID", reflect.TypeOf((*MockorderStore)(nil).GetByExternalID), ctx, dbi, extID) +} + +// GetExpiredStripeCheckoutSessionID mocks base method. +func (m *MockorderStore) GetExpiredStripeCheckoutSessionID(ctx context.Context, dbi sqlx.QueryerContext, orderID go_uuid.UUID) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetExpiredStripeCheckoutSessionID", ctx, dbi, orderID) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetExpiredStripeCheckoutSessionID indicates an expected call of GetExpiredStripeCheckoutSessionID. +func (mr *MockorderStoreMockRecorder) GetExpiredStripeCheckoutSessionID(ctx, dbi, orderID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExpiredStripeCheckoutSessionID", reflect.TypeOf((*MockorderStore)(nil).GetExpiredStripeCheckoutSessionID), ctx, dbi, orderID) +} + +// GetExpiresAtAfterISOPeriod mocks base method. +func (m *MockorderStore) GetExpiresAtAfterISOPeriod(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID) (time.Time, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetExpiresAtAfterISOPeriod", ctx, dbi, id) + ret0, _ := ret[0].(time.Time) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetExpiresAtAfterISOPeriod indicates an expected call of GetExpiresAtAfterISOPeriod. +func (mr *MockorderStoreMockRecorder) GetExpiresAtAfterISOPeriod(ctx, dbi, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExpiresAtAfterISOPeriod", reflect.TypeOf((*MockorderStore)(nil).GetExpiresAtAfterISOPeriod), ctx, dbi, id) +} + +// GetMetadata mocks base method. +func (m *MockorderStore) GetMetadata(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID) (datastore.Metadata, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMetadata", ctx, dbi, id) + ret0, _ := ret[0].(datastore.Metadata) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetMetadata indicates an expected call of GetMetadata. +func (mr *MockorderStoreMockRecorder) GetMetadata(ctx, dbi, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetadata", reflect.TypeOf((*MockorderStore)(nil).GetMetadata), ctx, dbi, id) +} + +// HasExternalID mocks base method. +func (m *MockorderStore) HasExternalID(ctx context.Context, dbi sqlx.QueryerContext, extID string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasExternalID", ctx, dbi, extID) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HasExternalID indicates an expected call of HasExternalID. +func (mr *MockorderStoreMockRecorder) HasExternalID(ctx, dbi, extID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasExternalID", reflect.TypeOf((*MockorderStore)(nil).HasExternalID), ctx, dbi, extID) +} + +// SetExpiresAt mocks base method. +func (m *MockorderStore) SetExpiresAt(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, when time.Time) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetExpiresAt", ctx, dbi, id, when) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetExpiresAt indicates an expected call of SetExpiresAt. +func (mr *MockorderStoreMockRecorder) SetExpiresAt(ctx, dbi, id, when interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetExpiresAt", reflect.TypeOf((*MockorderStore)(nil).SetExpiresAt), ctx, dbi, id, when) +} + +// SetLastPaidAt mocks base method. +func (m *MockorderStore) SetLastPaidAt(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, when time.Time) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetLastPaidAt", ctx, dbi, id, when) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetLastPaidAt indicates an expected call of SetLastPaidAt. +func (mr *MockorderStoreMockRecorder) SetLastPaidAt(ctx, dbi, id, when interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLastPaidAt", reflect.TypeOf((*MockorderStore)(nil).SetLastPaidAt), ctx, dbi, id, when) +} + +// SetStatus mocks base method. +func (m *MockorderStore) SetStatus(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, status string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetStatus", ctx, dbi, id, status) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetStatus indicates an expected call of SetStatus. +func (mr *MockorderStoreMockRecorder) SetStatus(ctx, dbi, id, status interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStatus", reflect.TypeOf((*MockorderStore)(nil).SetStatus), ctx, dbi, id, status) +} + +// SetTrialDays mocks base method. +func (m *MockorderStore) SetTrialDays(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID, ndays int64) (*model.Order, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetTrialDays", ctx, dbi, id, ndays) + ret0, _ := ret[0].(*model.Order) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SetTrialDays indicates an expected call of SetTrialDays. +func (mr *MockorderStoreMockRecorder) SetTrialDays(ctx, dbi, id, ndays interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTrialDays", reflect.TypeOf((*MockorderStore)(nil).SetTrialDays), ctx, dbi, id, ndays) +} + +// UpdateMetadata mocks base method. +func (m *MockorderStore) UpdateMetadata(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, data datastore.Metadata) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateMetadata", ctx, dbi, id, data) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateMetadata indicates an expected call of UpdateMetadata. +func (mr *MockorderStoreMockRecorder) UpdateMetadata(ctx, dbi, id, data interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateMetadata", reflect.TypeOf((*MockorderStore)(nil).UpdateMetadata), ctx, dbi, id, data) +} + +// MockorderItemStore is a mock of orderItemStore interface. +type MockorderItemStore struct { + ctrl *gomock.Controller + recorder *MockorderItemStoreMockRecorder +} + +// MockorderItemStoreMockRecorder is the mock recorder for MockorderItemStore. +type MockorderItemStoreMockRecorder struct { + mock *MockorderItemStore +} + +// NewMockorderItemStore creates a new mock instance. +func NewMockorderItemStore(ctrl *gomock.Controller) *MockorderItemStore { + mock := &MockorderItemStore{ctrl: ctrl} + mock.recorder = &MockorderItemStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockorderItemStore) EXPECT() *MockorderItemStoreMockRecorder { + return m.recorder +} + +// FindByOrderID mocks base method. +func (m *MockorderItemStore) FindByOrderID(ctx context.Context, dbi sqlx.QueryerContext, orderID go_uuid.UUID) ([]model.OrderItem, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindByOrderID", ctx, dbi, orderID) + ret0, _ := ret[0].([]model.OrderItem) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindByOrderID indicates an expected call of FindByOrderID. +func (mr *MockorderItemStoreMockRecorder) FindByOrderID(ctx, dbi, orderID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByOrderID", reflect.TypeOf((*MockorderItemStore)(nil).FindByOrderID), ctx, dbi, orderID) +} + +// Get mocks base method. +func (m *MockorderItemStore) Get(ctx context.Context, dbi sqlx.QueryerContext, id go_uuid.UUID) (*model.OrderItem, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, dbi, id) + ret0, _ := ret[0].(*model.OrderItem) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockorderItemStoreMockRecorder) Get(ctx, dbi, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockorderItemStore)(nil).Get), ctx, dbi, id) +} + +// InsertMany mocks base method. +func (m *MockorderItemStore) InsertMany(ctx context.Context, dbi sqlx.ExtContext, items ...model.OrderItem) ([]model.OrderItem, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, dbi} + for _, a := range items { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "InsertMany", varargs...) + ret0, _ := ret[0].([]model.OrderItem) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InsertMany indicates an expected call of InsertMany. +func (mr *MockorderItemStoreMockRecorder) InsertMany(ctx, dbi interface{}, items ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, dbi}, items...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertMany", reflect.TypeOf((*MockorderItemStore)(nil).InsertMany), varargs...) +} + +// MockorderPayHistoryStore is a mock of orderPayHistoryStore interface. +type MockorderPayHistoryStore struct { + ctrl *gomock.Controller + recorder *MockorderPayHistoryStoreMockRecorder +} + +// MockorderPayHistoryStoreMockRecorder is the mock recorder for MockorderPayHistoryStore. +type MockorderPayHistoryStoreMockRecorder struct { + mock *MockorderPayHistoryStore +} + +// NewMockorderPayHistoryStore creates a new mock instance. +func NewMockorderPayHistoryStore(ctrl *gomock.Controller) *MockorderPayHistoryStore { + mock := &MockorderPayHistoryStore{ctrl: ctrl} + mock.recorder = &MockorderPayHistoryStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockorderPayHistoryStore) EXPECT() *MockorderPayHistoryStoreMockRecorder { + return m.recorder +} + +// Insert mocks base method. +func (m *MockorderPayHistoryStore) Insert(ctx context.Context, dbi sqlx.ExecerContext, id go_uuid.UUID, when time.Time) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, dbi, id, when) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockorderPayHistoryStoreMockRecorder) Insert(ctx, dbi, id, when interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockorderPayHistoryStore)(nil).Insert), ctx, dbi, id, when) +} + +// MockissuerStore is a mock of issuerStore interface. +type MockissuerStore struct { + ctrl *gomock.Controller + recorder *MockissuerStoreMockRecorder +} + +// MockissuerStoreMockRecorder is the mock recorder for MockissuerStore. +type MockissuerStoreMockRecorder struct { + mock *MockissuerStore +} + +// NewMockissuerStore creates a new mock instance. +func NewMockissuerStore(ctrl *gomock.Controller) *MockissuerStore { + mock := &MockissuerStore{ctrl: ctrl} + mock.recorder = &MockissuerStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockissuerStore) EXPECT() *MockissuerStoreMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockissuerStore) Create(ctx context.Context, dbi sqlx.QueryerContext, req model.IssuerNew) (*model.Issuer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, dbi, req) + ret0, _ := ret[0].(*model.Issuer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockissuerStoreMockRecorder) Create(ctx, dbi, req interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockissuerStore)(nil).Create), ctx, dbi, req) +} + +// GetByMerchID mocks base method. +func (m *MockissuerStore) GetByMerchID(ctx context.Context, dbi sqlx.QueryerContext, merchID string) (*model.Issuer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByMerchID", ctx, dbi, merchID) + ret0, _ := ret[0].(*model.Issuer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByMerchID indicates an expected call of GetByMerchID. +func (mr *MockissuerStoreMockRecorder) GetByMerchID(ctx, dbi, merchID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByMerchID", reflect.TypeOf((*MockissuerStore)(nil).GetByMerchID), ctx, dbi, merchID) +} + +// GetByPubKey mocks base method. +func (m *MockissuerStore) GetByPubKey(ctx context.Context, dbi sqlx.QueryerContext, pubKey string) (*model.Issuer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByPubKey", ctx, dbi, pubKey) + ret0, _ := ret[0].(*model.Issuer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByPubKey indicates an expected call of GetByPubKey. +func (mr *MockissuerStoreMockRecorder) GetByPubKey(ctx, dbi, pubKey interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByPubKey", reflect.TypeOf((*MockissuerStore)(nil).GetByPubKey), ctx, dbi, pubKey) +}