idempotency key usage fix

This commit is contained in:
Stephan D
2026-01-21 15:23:50 +01:00
parent a15375f18e
commit d2e78356e6
48 changed files with 729 additions and 559 deletions

View File

@@ -12,12 +12,14 @@ type PaymentQuoteRecord struct {
storable.Base `bson:",inline" json:",inline"`
model.OrganizationBoundBase `bson:",inline" json:",inline"`
QuoteRef string `bson:"quoteRef" json:"quoteRef"`
Intent PaymentIntent `bson:"intent,omitempty" json:"intent,omitempty"`
Intents []PaymentIntent `bson:"intents,omitempty" json:"intents,omitempty"`
Quote *PaymentQuoteSnapshot `bson:"quote,omitempty" json:"quote,omitempty"`
Quotes []*PaymentQuoteSnapshot `bson:"quotes,omitempty" json:"quotes,omitempty"`
ExpiresAt time.Time `bson:"expiresAt" json:"expiresAt"`
QuoteRef string `bson:"quoteRef" json:"quoteRef"`
IdempotencyKey string `bson:"idempotencyKey" json:"idempotencyKey"`
Intent PaymentIntent `bson:"intent,omitempty" json:"intent,omitempty"`
Intents []PaymentIntent `bson:"intents,omitempty" json:"intents,omitempty"`
Quote *PaymentQuoteSnapshot `bson:"quote,omitempty" json:"quote,omitempty"`
Quotes []*PaymentQuoteSnapshot `bson:"quotes,omitempty" json:"quotes,omitempty"`
ExpiresAt time.Time `bson:"expiresAt" json:"expiresAt"`
Hash string `bson:"hash" json:"hash"`
}
// Collection implements storable.Storable.

View File

@@ -65,6 +65,9 @@ func (q *Quotes) Create(ctx context.Context, quote *model.PaymentQuoteRecord) er
if quote.OrganizationRef == primitive.NilObjectID {
return merrors.InvalidArgument("quotesStore: organization_ref is required")
}
if quote.IdempotencyKey == "" {
return merrors.InvalidArgument("quotesStore: idempotency key is required")
}
if quote.ExpiresAt.IsZero() {
return merrors.InvalidArgument("quotesStore: expires_at is required")
}
@@ -120,6 +123,25 @@ func (q *Quotes) GetByRef(ctx context.Context, orgRef primitive.ObjectID, quoteR
return entity, nil
}
func (q *Quotes) GetByIdempotencyKey(ctx context.Context, idempotencyKey string) (*model.PaymentQuoteRecord, error) {
idempotencyKey = strings.TrimSpace(idempotencyKey)
if idempotencyKey == "" {
return nil, merrors.InvalidArgument("quotesStore: empty idempotency key")
}
entity := &model.PaymentQuoteRecord{}
query := repository.Filter("idempotencyKey", idempotencyKey)
if err := q.repo.FindOneByFilter(ctx, query, entity); err != nil {
if errors.Is(err, merrors.ErrNoData) {
return nil, storage.ErrQuoteNotFound
}
return nil, err
}
if !entity.ExpiresAt.IsZero() && time.Now().After(entity.ExpiresAt) {
return nil, storage.ErrQuoteNotFound
}
return entity, nil
}
var _ storage.QuotesStore = (*Quotes)(nil)
func int32Ptr(v int32) *int32 {

View File

@@ -55,6 +55,7 @@ type PaymentsStore interface {
type QuotesStore interface {
Create(ctx context.Context, quote *model.PaymentQuoteRecord) error
GetByRef(ctx context.Context, orgRef primitive.ObjectID, quoteRef string) (*model.PaymentQuoteRecord, error)
GetByIdempotencyKey(ctx context.Context, idempotencyKey string) (*model.PaymentQuoteRecord, error)
}
// RoutesStore manages allowed routing transitions.