Documentation
¶
Index ¶
- Constants
- Variables
- func GenerateCartID() (string, error)
- func GenerateNonceID() (string, error)
- func GenerateRefundID() (string, error)
- type AdminNonce
- type ArchivalConfig
- type ArchivalService
- type CartItem
- type CartQuote
- type FileStore
- func (s *FileStore) ArchiveOldPayments(_ context.Context, olderThan time.Time) (int64, error)
- func (s *FileStore) CleanupExpiredNonces(_ context.Context) (int64, error)
- func (s *FileStore) Close() error
- func (s *FileStore) ConsumeNonce(_ context.Context, nonceID string) error
- func (s *FileStore) CreateNonce(_ context.Context, nonce AdminNonce) error
- func (s *FileStore) DeleteRefundQuote(ctx context.Context, refundID string) error
- func (s *FileStore) DeleteWebhook(ctx context.Context, webhookID string) error
- func (s *FileStore) DequeueWebhooks(ctx context.Context, limit int) ([]PendingWebhook, error)
- func (s *FileStore) EnqueueWebhook(ctx context.Context, webhook PendingWebhook) (string, error)
- func (s *FileStore) GetCartQuote(ctx context.Context, cartID string) (CartQuote, error)
- func (s *FileStore) GetCartQuotes(ctx context.Context, cartIDs []string) ([]CartQuote, error)
- func (s *FileStore) GetPayment(_ context.Context, signature string) (PaymentTransaction, error)
- func (s *FileStore) GetRefundQuote(ctx context.Context, refundID string) (RefundQuote, error)
- func (s *FileStore) GetRefundQuoteByOriginalPurchaseID(ctx context.Context, originalPurchaseID string) (RefundQuote, error)
- func (s *FileStore) GetWebhook(ctx context.Context, webhookID string) (PendingWebhook, error)
- func (s *FileStore) HasCartAccess(ctx context.Context, cartID, wallet string) bool
- func (s *FileStore) HasPaymentBeenProcessed(_ context.Context, signature string) (bool, error)
- func (s *FileStore) ListPendingRefunds(_ context.Context) ([]RefundQuote, error)
- func (s *FileStore) ListWebhooks(ctx context.Context, status WebhookStatus, limit int) ([]PendingWebhook, error)
- func (s *FileStore) MarkCartPaid(ctx context.Context, cartID, wallet string) error
- func (s *FileStore) MarkRefundProcessed(ctx context.Context, refundID, processedBy, signature string) error
- func (s *FileStore) MarkWebhookFailed(ctx context.Context, webhookID string, errorMsg string, ...) error
- func (s *FileStore) MarkWebhookProcessing(ctx context.Context, webhookID string) error
- func (s *FileStore) MarkWebhookSuccess(ctx context.Context, webhookID string) error
- func (s *FileStore) RecordPayment(_ context.Context, tx PaymentTransaction) error
- func (s *FileStore) RecordPayments(ctx context.Context, txs []PaymentTransaction) error
- func (s *FileStore) RetryWebhook(ctx context.Context, webhookID string) error
- func (s *FileStore) SaveCartQuote(ctx context.Context, quote CartQuote) error
- func (s *FileStore) SaveCartQuotes(ctx context.Context, quotes []CartQuote) error
- func (s *FileStore) SaveRefundQuote(ctx context.Context, quote RefundQuote) error
- func (s *FileStore) SaveRefundQuotes(ctx context.Context, quotes []RefundQuote) error
- type MemoryStore
- func (m *MemoryStore) ArchiveOldPayments(_ context.Context, olderThan time.Time) (int64, error)
- func (m *MemoryStore) CleanupExpiredNonces(_ context.Context) (int64, error)
- func (m *MemoryStore) Close() error
- func (m *MemoryStore) ConsumeNonce(_ context.Context, nonceID string) error
- func (m *MemoryStore) CreateNonce(_ context.Context, nonce AdminNonce) error
- func (m *MemoryStore) DeleteRefundQuote(_ context.Context, refundID string) error
- func (m *MemoryStore) DeleteWebhook(ctx context.Context, webhookID string) error
- func (m *MemoryStore) DequeueWebhooks(ctx context.Context, limit int) ([]PendingWebhook, error)
- func (m *MemoryStore) EnqueueWebhook(ctx context.Context, webhook PendingWebhook) (string, error)
- func (m *MemoryStore) GetCartQuote(_ context.Context, cartID string) (CartQuote, error)
- func (m *MemoryStore) GetCartQuotes(_ context.Context, cartIDs []string) ([]CartQuote, error)
- func (m *MemoryStore) GetPayment(_ context.Context, signature string) (PaymentTransaction, error)
- func (m *MemoryStore) GetRefundQuote(_ context.Context, refundID string) (RefundQuote, error)
- func (m *MemoryStore) GetRefundQuoteByOriginalPurchaseID(_ context.Context, originalPurchaseID string) (RefundQuote, error)
- func (m *MemoryStore) GetWebhook(ctx context.Context, webhookID string) (PendingWebhook, error)
- func (m *MemoryStore) HasCartAccess(_ context.Context, cartID, wallet string) bool
- func (m *MemoryStore) HasPaymentBeenProcessed(_ context.Context, signature string) (bool, error)
- func (m *MemoryStore) ListPendingRefunds(_ context.Context) ([]RefundQuote, error)
- func (m *MemoryStore) ListWebhooks(ctx context.Context, status WebhookStatus, limit int) ([]PendingWebhook, error)
- func (m *MemoryStore) MarkCartPaid(_ context.Context, cartID, wallet string) error
- func (m *MemoryStore) MarkRefundProcessed(_ context.Context, refundID, processedBy, signature string) error
- func (m *MemoryStore) MarkWebhookFailed(ctx context.Context, webhookID string, errorMsg string, ...) error
- func (m *MemoryStore) MarkWebhookProcessing(ctx context.Context, webhookID string) error
- func (m *MemoryStore) MarkWebhookSuccess(ctx context.Context, webhookID string) error
- func (m *MemoryStore) RecordPayment(_ context.Context, tx PaymentTransaction) error
- func (m *MemoryStore) RecordPayments(_ context.Context, txs []PaymentTransaction) error
- func (m *MemoryStore) RetryWebhook(ctx context.Context, webhookID string) error
- func (m *MemoryStore) SaveCartQuote(_ context.Context, quote CartQuote) error
- func (m *MemoryStore) SaveCartQuotes(_ context.Context, quotes []CartQuote) error
- func (m *MemoryStore) SaveRefundQuote(_ context.Context, quote RefundQuote) error
- func (m *MemoryStore) SaveRefundQuotes(_ context.Context, quotes []RefundQuote) error
- func (m *MemoryStore) Stop()
- type MongoDBStore
- func (s *MongoDBStore) ArchiveOldPayments(ctx context.Context, olderThan time.Time) (int64, error)
- func (s *MongoDBStore) CleanupExpiredNonces(ctx context.Context) (int64, error)
- func (s *MongoDBStore) Close() error
- func (s *MongoDBStore) ConsumeNonce(ctx context.Context, nonceID string) error
- func (s *MongoDBStore) CreateNonce(ctx context.Context, nonce AdminNonce) error
- func (s *MongoDBStore) DeleteRefundQuote(ctx context.Context, refundID string) error
- func (s *MongoDBStore) DeleteWebhook(ctx context.Context, webhookID string) error
- func (s *MongoDBStore) DequeueWebhooks(ctx context.Context, limit int) ([]PendingWebhook, error)
- func (s *MongoDBStore) EnqueueWebhook(ctx context.Context, webhook PendingWebhook) (string, error)
- func (s *MongoDBStore) GetCartQuote(ctx context.Context, cartID string) (CartQuote, error)
- func (s *MongoDBStore) GetCartQuotes(ctx context.Context, cartIDs []string) ([]CartQuote, error)
- func (s *MongoDBStore) GetPayment(ctx context.Context, signature string) (PaymentTransaction, error)
- func (s *MongoDBStore) GetRefundQuote(ctx context.Context, refundID string) (RefundQuote, error)
- func (s *MongoDBStore) GetRefundQuoteByOriginalPurchaseID(ctx context.Context, originalPurchaseID string) (RefundQuote, error)
- func (s *MongoDBStore) GetWebhook(ctx context.Context, webhookID string) (PendingWebhook, error)
- func (s *MongoDBStore) HasCartAccess(ctx context.Context, cartID, wallet string) bool
- func (s *MongoDBStore) HasPaymentBeenProcessed(ctx context.Context, signature string) (bool, error)
- func (s *MongoDBStore) ListPendingRefunds(ctx context.Context) ([]RefundQuote, error)
- func (s *MongoDBStore) ListWebhooks(ctx context.Context, status WebhookStatus, limit int) ([]PendingWebhook, error)
- func (s *MongoDBStore) MarkCartPaid(ctx context.Context, cartID, wallet string) error
- func (s *MongoDBStore) MarkRefundProcessed(ctx context.Context, refundID, processedBy, signature string) error
- func (s *MongoDBStore) MarkWebhookFailed(ctx context.Context, webhookID string, errorMsg string, ...) error
- func (s *MongoDBStore) MarkWebhookProcessing(ctx context.Context, webhookID string) error
- func (s *MongoDBStore) MarkWebhookSuccess(ctx context.Context, webhookID string) error
- func (s *MongoDBStore) RecordPayment(ctx context.Context, tx PaymentTransaction) error
- func (s *MongoDBStore) RecordPayments(ctx context.Context, txs []PaymentTransaction) error
- func (s *MongoDBStore) RetryWebhook(ctx context.Context, webhookID string) error
- func (s *MongoDBStore) SaveCartQuote(ctx context.Context, quote CartQuote) error
- func (s *MongoDBStore) SaveCartQuotes(ctx context.Context, quotes []CartQuote) error
- func (s *MongoDBStore) SaveRefundQuote(ctx context.Context, quote RefundQuote) error
- func (s *MongoDBStore) SaveRefundQuotes(ctx context.Context, quotes []RefundQuote) error
- type PaymentTransaction
- type PaymentTransactionStore
- type PendingWebhook
- type PostgresStore
- func (s *PostgresStore) ArchiveOldPayments(ctx context.Context, olderThan time.Time) (int64, error)
- func (s *PostgresStore) CleanupExpiredNonces(ctx context.Context) (int64, error)
- func (s *PostgresStore) Close() error
- func (s *PostgresStore) ConsumeNonce(ctx context.Context, nonceID string) error
- func (s *PostgresStore) CreateNonce(ctx context.Context, nonce AdminNonce) error
- func (s *PostgresStore) DeleteRefundQuote(ctx context.Context, refundID string) error
- func (s *PostgresStore) DeleteWebhook(ctx context.Context, webhookID string) error
- func (s *PostgresStore) DequeueWebhooks(ctx context.Context, limit int) ([]PendingWebhook, error)
- func (s *PostgresStore) EnqueueWebhook(ctx context.Context, webhook PendingWebhook) (string, error)
- func (s *PostgresStore) GetCartQuote(ctx context.Context, cartID string) (CartQuote, error)
- func (s *PostgresStore) GetCartQuotes(ctx context.Context, cartIDs []string) ([]CartQuote, error)
- func (s *PostgresStore) GetPayment(ctx context.Context, signature string) (PaymentTransaction, error)
- func (s *PostgresStore) GetRefundQuote(ctx context.Context, refundID string) (RefundQuote, error)
- func (s *PostgresStore) GetRefundQuoteByOriginalPurchaseID(ctx context.Context, originalPurchaseID string) (RefundQuote, error)
- func (s *PostgresStore) GetWebhook(ctx context.Context, webhookID string) (PendingWebhook, error)
- func (s *PostgresStore) HasCartAccess(ctx context.Context, cartID, wallet string) bool
- func (s *PostgresStore) HasPaymentBeenProcessed(ctx context.Context, signature string) (bool, error)
- func (s *PostgresStore) ListPendingRefunds(ctx context.Context) ([]RefundQuote, error)
- func (s *PostgresStore) ListWebhooks(ctx context.Context, status WebhookStatus, limit int) ([]PendingWebhook, error)
- func (s *PostgresStore) MarkCartPaid(ctx context.Context, cartID, wallet string) error
- func (s *PostgresStore) MarkRefundProcessed(ctx context.Context, refundID, processedBy, signature string) error
- func (s *PostgresStore) MarkWebhookFailed(ctx context.Context, webhookID string, errorMsg string, ...) error
- func (s *PostgresStore) MarkWebhookProcessing(ctx context.Context, webhookID string) error
- func (s *PostgresStore) MarkWebhookSuccess(ctx context.Context, webhookID string) error
- func (s *PostgresStore) RecordPayment(ctx context.Context, tx PaymentTransaction) error
- func (s *PostgresStore) RecordPayments(ctx context.Context, txs []PaymentTransaction) error
- func (s *PostgresStore) RetryWebhook(ctx context.Context, webhookID string) error
- func (s *PostgresStore) SaveCartQuote(ctx context.Context, quote CartQuote) error
- func (s *PostgresStore) SaveCartQuotes(ctx context.Context, quotes []CartQuote) error
- func (s *PostgresStore) SaveRefundQuote(ctx context.Context, quote RefundQuote) error
- func (s *PostgresStore) SaveRefundQuotes(ctx context.Context, quotes []RefundQuote) error
- func (s *PostgresStore) WithTableNames(...) *PostgresStore
- type RefundQuote
- type Store
- type StoreConfig
- type WebhookStatus
Constants ¶
const ( // CleanupInterval is how often the cleanup goroutine runs to remove expired records. CleanupInterval = 1 * time.Hour )
const ( // DefaultQueryTimeout is the maximum time allowed for database queries. // This prevents queries from hanging indefinitely and causing cascading failures. DefaultQueryTimeout = 5 * time.Second )
const NonceTTL = 5 * time.Minute
NonceTTL is the time-to-live for admin nonces (5 minutes).
Variables ¶
var ErrCartExpired = errors.New("storage: cart quote expired")
ErrCartExpired is returned when attempting to use an expired cart quote.
var ErrNotFound = errors.New("storage: not found")
ErrNotFound is returned when a requested entity is missing from the store.
var ErrRefundExpired = errors.New("storage: refund expired")
ErrRefundExpired is returned when a refund quote has passed its expiration time.
Functions ¶
func GenerateCartID ¶
GenerateCartID creates a cryptographically random cart identifier.
func GenerateNonceID ¶
GenerateNonceID creates a new random nonce ID.
func GenerateRefundID ¶
GenerateRefundID creates a cryptographically random refund identifier.
Types ¶
type AdminNonce ¶
type AdminNonce struct {
ID string // Unique nonce identifier (UUID)
Purpose string // What action this nonce is for (e.g., "list-pending-refunds")
CreatedAt time.Time // When nonce was created
ExpiresAt time.Time // When nonce expires
ConsumedAt *time.Time // When nonce was consumed (nil if not yet consumed)
}
AdminNonce represents a one-time-use nonce for admin signature replay protection. Each nonce can only be consumed once and expires after a TTL.
func (AdminNonce) IsConsumed ¶
func (n AdminNonce) IsConsumed() bool
IsConsumed returns true if this nonce has been used.
func (AdminNonce) IsExpiredAt ¶
func (n AdminNonce) IsExpiredAt(now time.Time) bool
IsExpiredAt returns true if this nonce has passed its expiration time at the given moment.
type ArchivalConfig ¶
type ArchivalConfig struct {
Enabled bool // Enable automatic archival (default: false)
RetentionPeriod time.Duration // How long to keep payment signatures (default: 90 days)
RunInterval time.Duration // How often to run archival (default: 24 hours)
}
ArchivalConfig holds configuration for automatic payment signature archival.
func DefaultArchivalConfig ¶
func DefaultArchivalConfig() ArchivalConfig
DefaultArchivalConfig returns sensible defaults for signature archival.
type ArchivalService ¶
type ArchivalService struct {
// contains filtered or unexported fields
}
ArchivalService automatically archives old payment signatures on a schedule.
func NewArchivalService ¶
func NewArchivalService(store Store, config ArchivalConfig, metricsCollector *metrics.Metrics, logger zerolog.Logger) *ArchivalService
NewArchivalService creates a new archival service.
func (*ArchivalService) RunNow ¶
func (s *ArchivalService) RunNow() error
RunNow immediately runs an archival pass (useful for testing or manual triggers).
func (*ArchivalService) Start ¶
func (s *ArchivalService) Start()
Start begins the archival service background loop.
func (*ArchivalService) Stop ¶
func (s *ArchivalService) Stop()
Stop gracefully stops the archival service.
type CartItem ¶
type CartItem struct {
ResourceID string // Resource ID from paywall config
Quantity int64 // Number of this item
Price money.Money // Price per unit (locked at quote time)
Metadata map[string]string // Per-item custom metadata
}
CartItem represents a single item in a cart quote.
type CartQuote ¶
type CartQuote struct {
ID string // Unique cart ID (cart_abc123...)
Items []CartItem // All items in the cart
Total money.Money // Total price (sum of all items)
Metadata map[string]string // Cart-level metadata (user_id, campaign, etc.)
CreatedAt time.Time // When quote was generated
ExpiresAt time.Time // When quote becomes invalid
WalletPaidBy string // Set after payment verification (for idempotency)
}
CartQuote represents a temporary cart with locked prices and expiration.
type FileStore ¶
type FileStore struct {
// contains filtered or unexported fields
}
FileStore implements Store using JSON file storage.
func NewFileStore ¶
NewFileStore creates a new file-backed store.
⚠️ PRODUCTION WARNING: FileStore is NOT safe for production use! This storage backend should ONLY be used for local development and testing. For production deployments, use PostgreSQL or MongoDB instead.
Reasons FileStore is unsuitable for production:
- No horizontal scaling support (multiple instances corrupt data)
- Race conditions at high concurrency (>100 req/sec)
- 5-second flush interval creates data loss risk
- No ACID guarantees (partial writes corrupt database)
- Single point of failure (file corruption = total loss)
See docs/PRODUCTION.md for production deployment guide.
func (*FileStore) ArchiveOldPayments ¶
ArchiveOldPayments deletes payment transactions older than the specified time. This prevents unbounded growth of the file store while maintaining replay protection for recent transactions (e.g., last 90 days).
Returns the number of archived (deleted) records.
func (*FileStore) CleanupExpiredNonces ¶
CleanupExpiredNonces deletes expired admin nonces from the file store. This prevents unbounded growth of the file store.
Returns the number of deleted nonces.
func (*FileStore) ConsumeNonce ¶
ConsumeNonce marks a nonce as consumed (one-time use).
func (*FileStore) CreateNonce ¶
func (s *FileStore) CreateNonce(_ context.Context, nonce AdminNonce) error
CreateNonce stores a new admin nonce for replay protection.
func (*FileStore) DeleteRefundQuote ¶
DeleteRefundQuote removes a refund quote by ID.
func (*FileStore) DeleteWebhook ¶
DeleteWebhook removes webhook from queue (admin operation).
func (*FileStore) DequeueWebhooks ¶
DequeueWebhooks retrieves webhooks ready for delivery.
func (*FileStore) EnqueueWebhook ¶
EnqueueWebhook adds a webhook to the delivery queue.
func (*FileStore) GetCartQuote ¶
GetCartQuote retrieves a cart quote by ID.
func (*FileStore) GetCartQuotes ¶
GetCartQuotes retrieves multiple cart quotes using individual queries.
func (*FileStore) GetPayment ¶
GetPayment retrieves a payment transaction by signature. Returns the original payment record showing which resource it was used for.
func (*FileStore) GetRefundQuote ¶
GetRefundQuote retrieves a refund quote by ID.
func (*FileStore) GetRefundQuoteByOriginalPurchaseID ¶
func (s *FileStore) GetRefundQuoteByOriginalPurchaseID(ctx context.Context, originalPurchaseID string) (RefundQuote, error)
GetRefundQuoteByOriginalPurchaseID retrieves a refund quote by original purchase ID (transaction signature). This enforces the one-refund-per-signature limit.
func (*FileStore) GetWebhook ¶
GetWebhook retrieves a webhook by ID (for admin UI).
func (*FileStore) HasCartAccess ¶
HasCartAccess checks if a cart is paid by the wallet.
func (*FileStore) HasPaymentBeenProcessed ¶
HasPaymentBeenProcessed checks if a transaction signature has EVER been used. Returns true if signature exists for ANY resource (prevents cross-resource replay).
func (*FileStore) ListPendingRefunds ¶
func (s *FileStore) ListPendingRefunds(_ context.Context) ([]RefundQuote, error)
ListPendingRefunds returns all unprocessed refund quotes.
func (*FileStore) ListWebhooks ¶
func (s *FileStore) ListWebhooks(ctx context.Context, status WebhookStatus, limit int) ([]PendingWebhook, error)
ListWebhooks lists webhooks with optional status filter (for admin UI).
func (*FileStore) MarkCartPaid ¶
MarkCartPaid marks a cart as paid.
func (*FileStore) MarkRefundProcessed ¶
func (s *FileStore) MarkRefundProcessed(ctx context.Context, refundID, processedBy, signature string) error
MarkRefundProcessed marks a refund as completed.
func (*FileStore) MarkWebhookFailed ¶
func (s *FileStore) MarkWebhookFailed(ctx context.Context, webhookID string, errorMsg string, nextAttemptAt time.Time) error
MarkWebhookFailed records failed attempt and schedules retry (or moves to DLQ if exhausted).
func (*FileStore) MarkWebhookProcessing ¶
MarkWebhookProcessing updates webhook status to prevent duplicate processing.
func (*FileStore) MarkWebhookSuccess ¶
MarkWebhookSuccess marks webhook as successfully delivered and removes from queue.
func (*FileStore) RecordPayment ¶
func (s *FileStore) RecordPayment(_ context.Context, tx PaymentTransaction) error
RecordPayment saves a verified payment transaction for replay protection. CRITICAL: Signature is globally unique - once used, cannot be reused for any resource. Returns error if signature already exists (concurrent replay attack).
func (*FileStore) RecordPayments ¶
func (s *FileStore) RecordPayments(ctx context.Context, txs []PaymentTransaction) error
RecordPayments saves multiple payment transactions using individual operations.
func (*FileStore) RetryWebhook ¶
RetryWebhook resets webhook to pending state for manual retry (admin operation).
func (*FileStore) SaveCartQuote ¶
SaveCartQuote persists or updates a cart quote.
func (*FileStore) SaveCartQuotes ¶
SaveCartQuotes stores multiple cart quotes using individual operations.
func (*FileStore) SaveRefundQuote ¶
func (s *FileStore) SaveRefundQuote(ctx context.Context, quote RefundQuote) error
SaveRefundQuote persists or updates a refund quote.
func (*FileStore) SaveRefundQuotes ¶
func (s *FileStore) SaveRefundQuotes(ctx context.Context, quotes []RefundQuote) error
SaveRefundQuotes stores multiple refund quotes using individual operations.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is an in-memory Store implementation suitable for tests and single-instance deployments.
func NewMemoryStore ¶
func NewMemoryStore() *MemoryStore
NewMemoryStore constructs a MemoryStore and starts background cleanup.
func (*MemoryStore) ArchiveOldPayments ¶
ArchiveOldPayments deletes payment transactions older than the specified time. For MemoryStore, this is primarily for testing - memory stores are ephemeral.
func (*MemoryStore) CleanupExpiredNonces ¶
func (m *MemoryStore) CleanupExpiredNonces(_ context.Context) (int64, error)
CleanupExpiredNonces deletes expired admin nonces. For MemoryStore, this returns the count of deleted nonces.
func (*MemoryStore) Close ¶
func (m *MemoryStore) Close() error
Close implements the Store interface by calling Stop.
func (*MemoryStore) ConsumeNonce ¶
func (m *MemoryStore) ConsumeNonce(_ context.Context, nonceID string) error
ConsumeNonce marks a nonce as consumed (one-time use). Returns error if nonce doesn't exist, is already consumed, or has expired.
func (*MemoryStore) CreateNonce ¶
func (m *MemoryStore) CreateNonce(_ context.Context, nonce AdminNonce) error
CreateNonce stores a new admin nonce for replay protection. Nonce must be unique and not already exist.
func (*MemoryStore) DeleteRefundQuote ¶
func (m *MemoryStore) DeleteRefundQuote(_ context.Context, refundID string) error
DeleteRefundQuote removes a refund quote by ID.
func (*MemoryStore) DeleteWebhook ¶
func (m *MemoryStore) DeleteWebhook(ctx context.Context, webhookID string) error
DeleteWebhook removes webhook from queue (admin operation).
func (*MemoryStore) DequeueWebhooks ¶
func (m *MemoryStore) DequeueWebhooks(ctx context.Context, limit int) ([]PendingWebhook, error)
DequeueWebhooks retrieves webhooks ready for delivery.
func (*MemoryStore) EnqueueWebhook ¶
func (m *MemoryStore) EnqueueWebhook(ctx context.Context, webhook PendingWebhook) (string, error)
EnqueueWebhook adds a webhook to the delivery queue.
func (*MemoryStore) GetCartQuote ¶
GetCartQuote retrieves a cart quote by ID. Returns ErrNotFound if cart doesn't exist, ErrCartExpired if expired.
func (*MemoryStore) GetCartQuotes ¶
GetCartQuotes retrieves multiple cart quotes by ID. Returns a slice with found quotes - missing or expired carts are skipped (partial results).
func (*MemoryStore) GetPayment ¶
func (m *MemoryStore) GetPayment(_ context.Context, signature string) (PaymentTransaction, error)
GetPayment retrieves a payment transaction by signature. Returns the original payment showing which resource it was used for.
func (*MemoryStore) GetRefundQuote ¶
func (m *MemoryStore) GetRefundQuote(_ context.Context, refundID string) (RefundQuote, error)
GetRefundQuote retrieves a refund quote by ID. NOTE: Refund requests never expire - they remain pending until approved or denied by admin.
func (*MemoryStore) GetRefundQuoteByOriginalPurchaseID ¶
func (m *MemoryStore) GetRefundQuoteByOriginalPurchaseID(_ context.Context, originalPurchaseID string) (RefundQuote, error)
GetRefundQuoteByOriginalPurchaseID retrieves a refund quote by original purchase ID (transaction signature). This enforces the one-refund-per-signature limit.
func (*MemoryStore) GetWebhook ¶
func (m *MemoryStore) GetWebhook(ctx context.Context, webhookID string) (PendingWebhook, error)
GetWebhook retrieves a webhook by ID (for admin UI).
func (*MemoryStore) HasCartAccess ¶
func (m *MemoryStore) HasCartAccess(_ context.Context, cartID, wallet string) bool
HasCartAccess checks if a wallet has already paid for a cart.
func (*MemoryStore) HasPaymentBeenProcessed ¶
HasPaymentBeenProcessed checks if a transaction signature has EVER been used. Returns true if signature exists for ANY resource (prevents cross-resource replay).
func (*MemoryStore) ListPendingRefunds ¶
func (m *MemoryStore) ListPendingRefunds(_ context.Context) ([]RefundQuote, error)
ListPendingRefunds returns all unprocessed refund quotes.
func (*MemoryStore) ListWebhooks ¶
func (m *MemoryStore) ListWebhooks(ctx context.Context, status WebhookStatus, limit int) ([]PendingWebhook, error)
ListWebhooks lists webhooks with optional status filter (for admin UI).
func (*MemoryStore) MarkCartPaid ¶
func (m *MemoryStore) MarkCartPaid(_ context.Context, cartID, wallet string) error
MarkCartPaid records the wallet that paid for a cart (for idempotency).
func (*MemoryStore) MarkRefundProcessed ¶
func (m *MemoryStore) MarkRefundProcessed(_ context.Context, refundID, processedBy, signature string) error
MarkRefundProcessed marks a refund as completed with the transaction signature.
func (*MemoryStore) MarkWebhookFailed ¶
func (m *MemoryStore) MarkWebhookFailed(ctx context.Context, webhookID string, errorMsg string, nextAttemptAt time.Time) error
MarkWebhookFailed records failed attempt and schedules retry (or moves to DLQ if exhausted).
func (*MemoryStore) MarkWebhookProcessing ¶
func (m *MemoryStore) MarkWebhookProcessing(ctx context.Context, webhookID string) error
MarkWebhookProcessing updates webhook status to prevent duplicate processing.
func (*MemoryStore) MarkWebhookSuccess ¶
func (m *MemoryStore) MarkWebhookSuccess(ctx context.Context, webhookID string) error
MarkWebhookSuccess marks webhook as successfully delivered and removes from queue.
func (*MemoryStore) RecordPayment ¶
func (m *MemoryStore) RecordPayment(_ context.Context, tx PaymentTransaction) error
RecordPayment saves a verified payment transaction for replay protection. CRITICAL: Signature is the sole key - prevents cross-resource replay attacks. Returns error if signature already exists (concurrent replay attack).
func (*MemoryStore) RecordPayments ¶
func (m *MemoryStore) RecordPayments(_ context.Context, txs []PaymentTransaction) error
RecordPayments saves multiple verified payment transactions in a single operation. CRITICAL: ALL signatures must be globally unique - batch fails if ANY signature exists. This is atomic - either all succeed or none are stored (fail fast on first duplicate).
func (*MemoryStore) RetryWebhook ¶
func (m *MemoryStore) RetryWebhook(ctx context.Context, webhookID string) error
RetryWebhook resets webhook to pending state for manual retry (admin operation).
func (*MemoryStore) SaveCartQuote ¶
func (m *MemoryStore) SaveCartQuote(_ context.Context, quote CartQuote) error
SaveCartQuote stores a cart quote with automatic expiration.
func (*MemoryStore) SaveCartQuotes ¶
func (m *MemoryStore) SaveCartQuotes(_ context.Context, quotes []CartQuote) error
SaveCartQuotes stores multiple cart quotes in a single operation. All quotes are validated before any are stored (atomic batch).
func (*MemoryStore) SaveRefundQuote ¶
func (m *MemoryStore) SaveRefundQuote(_ context.Context, quote RefundQuote) error
SaveRefundQuote persists or updates a refund quote.
func (*MemoryStore) SaveRefundQuotes ¶
func (m *MemoryStore) SaveRefundQuotes(_ context.Context, quotes []RefundQuote) error
SaveRefundQuotes stores multiple refund quotes in a single operation. All quotes are validated before any are stored (atomic batch).
func (*MemoryStore) Stop ¶
func (m *MemoryStore) Stop()
Stop gracefully stops the cleanup goroutine.
type MongoDBStore ¶
type MongoDBStore struct {
// contains filtered or unexported fields
}
MongoDBStore implements Store using MongoDB.
func NewMongoDBStore ¶
func NewMongoDBStore(connectionString, database string) (*MongoDBStore, error)
NewMongoDBStore creates a new MongoDB-backed store.
func (*MongoDBStore) ArchiveOldPayments ¶
ArchiveOldPayments deletes payment transactions older than the specified time. This prevents unbounded growth of the payment_transactions collection while maintaining replay protection for recent transactions (e.g., last 90 days).
Returns the number of archived (deleted) records.
func (*MongoDBStore) CleanupExpiredNonces ¶
func (s *MongoDBStore) CleanupExpiredNonces(ctx context.Context) (int64, error)
CleanupExpiredNonces deletes expired admin nonces from the database. This prevents unbounded growth of the admin_nonces collection.
Returns the number of deleted nonces.
func (*MongoDBStore) Close ¶
func (s *MongoDBStore) Close() error
Close closes the database connection.
func (*MongoDBStore) ConsumeNonce ¶
func (s *MongoDBStore) ConsumeNonce(ctx context.Context, nonceID string) error
ConsumeNonce marks a nonce as consumed (one-time use).
func (*MongoDBStore) CreateNonce ¶
func (s *MongoDBStore) CreateNonce(ctx context.Context, nonce AdminNonce) error
CreateNonce stores a new admin nonce for replay protection.
func (*MongoDBStore) DeleteRefundQuote ¶
func (s *MongoDBStore) DeleteRefundQuote(ctx context.Context, refundID string) error
DeleteRefundQuote removes a refund quote by ID.
func (*MongoDBStore) DeleteWebhook ¶
func (s *MongoDBStore) DeleteWebhook(ctx context.Context, webhookID string) error
DeleteWebhook removes webhook from queue (admin operation).
func (*MongoDBStore) DequeueWebhooks ¶
func (s *MongoDBStore) DequeueWebhooks(ctx context.Context, limit int) ([]PendingWebhook, error)
DequeueWebhooks retrieves webhooks ready for delivery.
func (*MongoDBStore) EnqueueWebhook ¶
func (s *MongoDBStore) EnqueueWebhook(ctx context.Context, webhook PendingWebhook) (string, error)
EnqueueWebhook adds a webhook to the delivery queue.
func (*MongoDBStore) GetCartQuote ¶
GetCartQuote retrieves a cart quote by ID.
func (*MongoDBStore) GetCartQuotes ¶
GetCartQuotes retrieves multiple cart quotes using MongoDB $in operator (single query).
func (*MongoDBStore) GetPayment ¶
func (s *MongoDBStore) GetPayment(ctx context.Context, signature string) (PaymentTransaction, error)
GetPayment retrieves a payment transaction by signature. Returns the original payment record showing which resource it was used for.
func (*MongoDBStore) GetRefundQuote ¶
func (s *MongoDBStore) GetRefundQuote(ctx context.Context, refundID string) (RefundQuote, error)
GetRefundQuote retrieves a refund quote by ID.
func (*MongoDBStore) GetRefundQuoteByOriginalPurchaseID ¶
func (s *MongoDBStore) GetRefundQuoteByOriginalPurchaseID(ctx context.Context, originalPurchaseID string) (RefundQuote, error)
GetRefundQuoteByOriginalPurchaseID retrieves a refund quote by original purchase ID (transaction signature). This enforces the one-refund-per-signature limit.
func (*MongoDBStore) GetWebhook ¶
func (s *MongoDBStore) GetWebhook(ctx context.Context, webhookID string) (PendingWebhook, error)
GetWebhook retrieves a webhook by ID (for admin UI).
func (*MongoDBStore) HasCartAccess ¶
func (s *MongoDBStore) HasCartAccess(ctx context.Context, cartID, wallet string) bool
HasCartAccess checks if a cart is paid by the wallet.
func (*MongoDBStore) HasPaymentBeenProcessed ¶
HasPaymentBeenProcessed checks if a transaction signature has EVER been used. Returns true if signature exists for ANY resource (prevents cross-resource replay).
func (*MongoDBStore) ListPendingRefunds ¶
func (s *MongoDBStore) ListPendingRefunds(ctx context.Context) ([]RefundQuote, error)
ListPendingRefunds returns all unprocessed refund quotes.
func (*MongoDBStore) ListWebhooks ¶
func (s *MongoDBStore) ListWebhooks(ctx context.Context, status WebhookStatus, limit int) ([]PendingWebhook, error)
ListWebhooks lists webhooks with optional status filter (for admin UI).
func (*MongoDBStore) MarkCartPaid ¶
func (s *MongoDBStore) MarkCartPaid(ctx context.Context, cartID, wallet string) error
MarkCartPaid marks a cart as paid.
func (*MongoDBStore) MarkRefundProcessed ¶
func (s *MongoDBStore) MarkRefundProcessed(ctx context.Context, refundID, processedBy, signature string) error
MarkRefundProcessed marks a refund as completed.
func (*MongoDBStore) MarkWebhookFailed ¶
func (s *MongoDBStore) MarkWebhookFailed(ctx context.Context, webhookID string, errorMsg string, nextAttemptAt time.Time) error
MarkWebhookFailed records failed attempt and schedules retry (or moves to DLQ if exhausted).
func (*MongoDBStore) MarkWebhookProcessing ¶
func (s *MongoDBStore) MarkWebhookProcessing(ctx context.Context, webhookID string) error
MarkWebhookProcessing updates webhook status to prevent duplicate processing.
func (*MongoDBStore) MarkWebhookSuccess ¶
func (s *MongoDBStore) MarkWebhookSuccess(ctx context.Context, webhookID string) error
MarkWebhookSuccess marks webhook as successfully delivered and removes from queue.
func (*MongoDBStore) RecordPayment ¶
func (s *MongoDBStore) RecordPayment(ctx context.Context, tx PaymentTransaction) error
RecordPayment saves a verified payment transaction for replay protection. CRITICAL: Signature is globally unique - once used, cannot be reused for any resource. Returns error if signature already exists (concurrent replay attack).
func (*MongoDBStore) RecordPayments ¶
func (s *MongoDBStore) RecordPayments(ctx context.Context, txs []PaymentTransaction) error
RecordPayments saves multiple payment transactions using MongoDB bulk operations. Note: Uses ordered=true to maintain atomic failure on duplicate signatures.
func (*MongoDBStore) RetryWebhook ¶
func (s *MongoDBStore) RetryWebhook(ctx context.Context, webhookID string) error
RetryWebhook resets webhook to pending state for manual retry (admin operation).
func (*MongoDBStore) SaveCartQuote ¶
func (s *MongoDBStore) SaveCartQuote(ctx context.Context, quote CartQuote) error
SaveCartQuote persists or updates a cart quote.
func (*MongoDBStore) SaveCartQuotes ¶
func (s *MongoDBStore) SaveCartQuotes(ctx context.Context, quotes []CartQuote) error
SaveCartQuotes stores multiple cart quotes using MongoDB bulk operations.
func (*MongoDBStore) SaveRefundQuote ¶
func (s *MongoDBStore) SaveRefundQuote(ctx context.Context, quote RefundQuote) error
SaveRefundQuote persists or updates a refund quote.
func (*MongoDBStore) SaveRefundQuotes ¶
func (s *MongoDBStore) SaveRefundQuotes(ctx context.Context, quotes []RefundQuote) error
SaveRefundQuotes stores multiple refund quotes using MongoDB bulk operations.
type PaymentTransaction ¶
type PaymentTransaction struct {
Signature string // Transaction signature (unique ID, globally unique)
ResourceID string // Resource that was purchased
Wallet string // Wallet that made the payment
Amount money.Money // Amount paid
CreatedAt time.Time // When transaction was verified
Metadata map[string]string // Additional metadata
}
PaymentTransaction represents a verified payment transaction. Used for replay protection - ensures each transaction signature is only used ONCE globally.
type PaymentTransactionStore ¶
type PaymentTransactionStore interface {
// RecordPayment saves a verified payment transaction.
// The signature must be globally unique - attempting to record the same signature
// twice (even for different resources) should fail or be silently ignored.
RecordPayment(ctx context.Context, tx PaymentTransaction) error
// HasPaymentBeenProcessed checks if a transaction signature has EVER been used.
// Returns true if the signature exists for ANY resource (not just the specified one).
// This prevents cross-resource replay attacks.
HasPaymentBeenProcessed(ctx context.Context, signature string) (bool, error)
// GetPayment retrieves a payment transaction by signature.
// Returns the original payment record, which shows which resource it was used for.
GetPayment(ctx context.Context, signature string) (PaymentTransaction, error)
}
PaymentTransactionStore defines the interface for payment transaction persistence. CRITICAL: This is used for replay protection to ensure each signature is only used ONCE, regardless of which resource it's being used for. Once a signature is consumed for any resource, it CANNOT be reused for any other resource.
type PendingWebhook ¶
type PendingWebhook struct {
ID string `json:"id"` // Unique webhook identifier (webhook_123...)
URL string `json:"url"` // Destination URL
Payload json.RawMessage `json:"payload"` // JSON payload to send
Headers map[string]string `json:"headers"` // HTTP headers
EventType string `json:"eventType"` // "payment" or "refund"
Status WebhookStatus `json:"status"` // Current status
Attempts int `json:"attempts"` // Number of delivery attempts
MaxAttempts int `json:"maxAttempts"` // Maximum retry attempts (e.g., 5)
LastError string `json:"lastError"` // Error from last attempt
LastAttemptAt time.Time `json:"lastAttemptAt"` // When last attempt was made
NextAttemptAt time.Time `json:"nextAttemptAt"` // When next attempt should be made
CreatedAt time.Time `json:"createdAt"` // When webhook was created
CompletedAt *time.Time `json:"completedAt"` // When webhook was successfully delivered or failed permanently
}
PendingWebhook represents a webhook waiting for delivery or retry. This struct is persisted to the database to ensure delivery across server restarts.
func (PendingWebhook) IsFinallyFailed ¶
func (w PendingWebhook) IsFinallyFailed() bool
IsFinallyFailed returns true if the webhook has exhausted all retries.
func (PendingWebhook) IsReadyForDelivery ¶
func (w PendingWebhook) IsReadyForDelivery() bool
IsReadyForDelivery returns true if the webhook should be processed now.
type PostgresStore ¶
type PostgresStore struct {
// contains filtered or unexported fields
}
PostgresStore implements Store using PostgreSQL.
func NewPostgresStore ¶
func NewPostgresStore(connectionString string, poolConfig config.PostgresPoolConfig) (*PostgresStore, error)
NewPostgresStore creates a new PostgreSQL-backed store.
func NewPostgresStoreWithDB ¶
func NewPostgresStoreWithDB(db *sql.DB) (*PostgresStore, error)
NewPostgresStoreWithDB creates a PostgreSQL-backed store using an existing connection pool. This allows sharing a single connection pool across multiple stores/repositories.
func (*PostgresStore) ArchiveOldPayments ¶
ArchiveOldPayments deletes payment transactions older than the specified time. This prevents unbounded growth of the payment_transactions table while maintaining replay protection for recent transactions (e.g., last 90 days).
Returns the number of archived (deleted) records.
func (*PostgresStore) CleanupExpiredNonces ¶
func (s *PostgresStore) CleanupExpiredNonces(ctx context.Context) (int64, error)
CleanupExpiredNonces deletes expired admin nonces from the database. This prevents unbounded growth of the admin_nonces table.
Returns the number of deleted nonces.
func (*PostgresStore) Close ¶
func (s *PostgresStore) Close() error
Close closes the database connection.
func (*PostgresStore) ConsumeNonce ¶
func (s *PostgresStore) ConsumeNonce(ctx context.Context, nonceID string) error
ConsumeNonce marks a nonce as consumed (one-time use).
func (*PostgresStore) CreateNonce ¶
func (s *PostgresStore) CreateNonce(ctx context.Context, nonce AdminNonce) error
CreateNonce stores a new admin nonce for replay protection.
func (*PostgresStore) DeleteRefundQuote ¶
func (s *PostgresStore) DeleteRefundQuote(ctx context.Context, refundID string) error
DeleteRefundQuote removes a refund quote by ID.
func (*PostgresStore) DeleteWebhook ¶
func (s *PostgresStore) DeleteWebhook(ctx context.Context, webhookID string) error
DeleteWebhook removes webhook from queue (admin operation).
func (*PostgresStore) DequeueWebhooks ¶
func (s *PostgresStore) DequeueWebhooks(ctx context.Context, limit int) ([]PendingWebhook, error)
DequeueWebhooks retrieves webhooks ready for delivery.
func (*PostgresStore) EnqueueWebhook ¶
func (s *PostgresStore) EnqueueWebhook(ctx context.Context, webhook PendingWebhook) (string, error)
EnqueueWebhook adds a webhook to the delivery queue.
func (*PostgresStore) GetCartQuote ¶
GetCartQuote retrieves a cart quote by ID.
func (*PostgresStore) GetCartQuotes ¶
GetCartQuotes retrieves multiple cart quotes by IDs in a single query. Returns found quotes - missing or expired carts are skipped (partial results).
func (*PostgresStore) GetPayment ¶
func (s *PostgresStore) GetPayment(ctx context.Context, signature string) (PaymentTransaction, error)
GetPayment retrieves a payment transaction by signature. Returns the original payment record showing which resource it was used for.
func (*PostgresStore) GetRefundQuote ¶
func (s *PostgresStore) GetRefundQuote(ctx context.Context, refundID string) (RefundQuote, error)
GetRefundQuote retrieves a refund quote by ID.
func (*PostgresStore) GetRefundQuoteByOriginalPurchaseID ¶
func (s *PostgresStore) GetRefundQuoteByOriginalPurchaseID(ctx context.Context, originalPurchaseID string) (RefundQuote, error)
GetRefundQuoteByOriginalPurchaseID retrieves a refund quote by original purchase ID (transaction signature). This enforces the one-refund-per-signature limit.
func (*PostgresStore) GetWebhook ¶
func (s *PostgresStore) GetWebhook(ctx context.Context, webhookID string) (PendingWebhook, error)
GetWebhook retrieves a webhook by ID (for admin UI).
func (*PostgresStore) HasCartAccess ¶
func (s *PostgresStore) HasCartAccess(ctx context.Context, cartID, wallet string) bool
HasCartAccess checks if a cart is paid by the wallet.
func (*PostgresStore) HasPaymentBeenProcessed ¶
func (s *PostgresStore) HasPaymentBeenProcessed(ctx context.Context, signature string) (bool, error)
HasPaymentBeenProcessed checks if a transaction signature has EVER been used. Returns true if signature exists for ANY resource (prevents cross-resource replay).
func (*PostgresStore) ListPendingRefunds ¶
func (s *PostgresStore) ListPendingRefunds(ctx context.Context) ([]RefundQuote, error)
ListPendingRefunds returns all unprocessed refund quotes.
func (*PostgresStore) ListWebhooks ¶
func (s *PostgresStore) ListWebhooks(ctx context.Context, status WebhookStatus, limit int) ([]PendingWebhook, error)
ListWebhooks lists webhooks with optional status filter (for admin UI).
func (*PostgresStore) MarkCartPaid ¶
func (s *PostgresStore) MarkCartPaid(ctx context.Context, cartID, wallet string) error
MarkCartPaid marks a cart as paid.
func (*PostgresStore) MarkRefundProcessed ¶
func (s *PostgresStore) MarkRefundProcessed(ctx context.Context, refundID, processedBy, signature string) error
MarkRefundProcessed marks a refund as completed.
func (*PostgresStore) MarkWebhookFailed ¶
func (s *PostgresStore) MarkWebhookFailed(ctx context.Context, webhookID string, errorMsg string, nextAttemptAt time.Time) error
MarkWebhookFailed records failed attempt and schedules retry (or moves to DLQ if exhausted).
func (*PostgresStore) MarkWebhookProcessing ¶
func (s *PostgresStore) MarkWebhookProcessing(ctx context.Context, webhookID string) error
MarkWebhookProcessing updates webhook status to prevent duplicate processing.
func (*PostgresStore) MarkWebhookSuccess ¶
func (s *PostgresStore) MarkWebhookSuccess(ctx context.Context, webhookID string) error
MarkWebhookSuccess marks webhook as successfully delivered and removes from queue.
func (*PostgresStore) RecordPayment ¶
func (s *PostgresStore) RecordPayment(ctx context.Context, tx PaymentTransaction) error
RecordPayment saves a verified payment transaction for replay protection. CRITICAL: Signature is globally unique - once used, cannot be reused for any resource. Returns error if signature was already used (concurrent request won the race).
func (*PostgresStore) RecordPayments ¶
func (s *PostgresStore) RecordPayments(ctx context.Context, txs []PaymentTransaction) error
RecordPayments saves multiple verified payment transactions in a single batch operation. CRITICAL: ALL signatures must be globally unique - batch fails if ANY signature already exists. Uses multi-row INSERT for optimal performance (single database round-trip).
func (*PostgresStore) RetryWebhook ¶
func (s *PostgresStore) RetryWebhook(ctx context.Context, webhookID string) error
RetryWebhook resets webhook to pending state for manual retry (admin operation).
func (*PostgresStore) SaveCartQuote ¶
func (s *PostgresStore) SaveCartQuote(ctx context.Context, quote CartQuote) error
SaveCartQuote persists or updates a cart quote.
func (*PostgresStore) SaveCartQuotes ¶
func (s *PostgresStore) SaveCartQuotes(ctx context.Context, quotes []CartQuote) error
SaveCartQuotes stores multiple cart quotes in a single batch operation. Uses multi-row INSERT for optimal performance (single database round-trip).
func (*PostgresStore) SaveRefundQuote ¶
func (s *PostgresStore) SaveRefundQuote(ctx context.Context, quote RefundQuote) error
SaveRefundQuote persists or updates a refund quote.
func (*PostgresStore) SaveRefundQuotes ¶
func (s *PostgresStore) SaveRefundQuotes(ctx context.Context, quotes []RefundQuote) error
SaveRefundQuotes stores multiple refund quotes in a single batch operation. Uses multi-row INSERT for optimal performance (single database round-trip).
func (*PostgresStore) WithTableNames ¶
func (s *PostgresStore) WithTableNames(paymentTransactions, adminNonces, cartQuotes, refundQuotes, webhookQueue string) *PostgresStore
WithTableNames sets custom table names (for schema_mapping support). After setting table names, it recreates tables with the new names.
type RefundQuote ¶
type RefundQuote struct {
ID string
OriginalPurchaseID string // Reference to original purchase (resource ID, cart ID, session ID)
RecipientWallet string // Wallet receiving the refund
Amount money.Money
Reason string
Metadata map[string]string
CreatedAt time.Time
ExpiresAt time.Time
ProcessedBy string // Wallet that executed the refund
ProcessedAt *time.Time
Signature string // Transaction signature
}
RefundQuote represents a generated refund quote with payment details.
func (*RefundQuote) IsExpiredAt ¶
func (r *RefundQuote) IsExpiredAt(now time.Time) bool
IsExpiredAt returns true if the refund quote's transaction execution window has passed at the given moment. This means the blockhash is expired and the transaction cannot be executed. NOTE: This does NOT mean the refund request is deleted - it remains in storage and can be re-quoted or denied by an admin.
func (*RefundQuote) IsProcessed ¶
func (r *RefundQuote) IsProcessed() bool
IsProcessed returns true if the refund has been completed.
type Store ¶
type Store interface {
// Single-record cart operations
SaveCartQuote(ctx context.Context, quote CartQuote) error
GetCartQuote(ctx context.Context, cartID string) (CartQuote, error)
MarkCartPaid(ctx context.Context, cartID, wallet string) error
HasCartAccess(ctx context.Context, cartID, wallet string) bool
// Batch cart operations (for bulk imports, analytics, admin dashboards)
// SaveCartQuotes stores multiple quotes efficiently (atomic: all succeed or all fail).
// GetCartQuotes retrieves multiple quotes in a single query (partial results: skips missing/expired).
SaveCartQuotes(ctx context.Context, quotes []CartQuote) error
GetCartQuotes(ctx context.Context, cartIDs []string) ([]CartQuote, error)
// Single-record refund operations
SaveRefundQuote(ctx context.Context, quote RefundQuote) error
GetRefundQuote(ctx context.Context, refundID string) (RefundQuote, error)
GetRefundQuoteByOriginalPurchaseID(ctx context.Context, originalPurchaseID string) (RefundQuote, error)
ListPendingRefunds(ctx context.Context) ([]RefundQuote, error)
MarkRefundProcessed(ctx context.Context, refundID, processedBy, signature string) error
DeleteRefundQuote(ctx context.Context, refundID string) error
// Batch refund operations (for bulk refund processing)
SaveRefundQuotes(ctx context.Context, quotes []RefundQuote) error
// Single-record payment transaction tracking for replay protection
// CRITICAL: Signatures are globally unique - once used for any resource, they cannot be reused
RecordPayment(ctx context.Context, tx PaymentTransaction) error
HasPaymentBeenProcessed(ctx context.Context, signature string) (bool, error)
GetPayment(ctx context.Context, signature string) (PaymentTransaction, error)
// Batch payment operations (for bulk settlement jobs, batch imports)
// CRITICAL: All signatures must be globally unique - batch fails if any signature exists
RecordPayments(ctx context.Context, txs []PaymentTransaction) error
// Payment archival for database cleanup
// Archives old payment signatures beyond the retention period to prevent unbounded growth
ArchiveOldPayments(ctx context.Context, olderThan time.Time) (int64, error) // Returns count of archived records
// Nonce management for admin signature replay protection
// CRITICAL: Each nonce can only be used once - prevents signature replay attacks
CreateNonce(ctx context.Context, nonce AdminNonce) error
ConsumeNonce(ctx context.Context, nonceID string) error // Returns error if already consumed or not found
// Admin nonce cleanup for database maintenance
CleanupExpiredNonces(ctx context.Context) (int64, error) // Returns count of deleted nonces
// Webhook queue operations for persistent webhook delivery
// EnqueueWebhook adds a webhook to the delivery queue (returns webhook ID)
EnqueueWebhook(ctx context.Context, webhook PendingWebhook) (string, error)
// DequeueWebhooks retrieves webhooks ready for delivery (up to limit, ordered by next attempt time)
DequeueWebhooks(ctx context.Context, limit int) ([]PendingWebhook, error)
// MarkWebhookProcessing updates webhook status to prevent duplicate processing
MarkWebhookProcessing(ctx context.Context, webhookID string) error
// MarkWebhookSuccess marks webhook as successfully delivered and removes from queue
MarkWebhookSuccess(ctx context.Context, webhookID string) error
// MarkWebhookFailed records failed attempt and schedules retry (or moves to DLQ if exhausted)
MarkWebhookFailed(ctx context.Context, webhookID string, errorMsg string, nextAttemptAt time.Time) error
// GetWebhook retrieves a webhook by ID (for admin UI)
GetWebhook(ctx context.Context, webhookID string) (PendingWebhook, error)
// ListWebhooks lists webhooks with optional status filter (for admin UI)
ListWebhooks(ctx context.Context, status WebhookStatus, limit int) ([]PendingWebhook, error)
// RetryWebhook resets webhook to pending state for manual retry (admin operation)
RetryWebhook(ctx context.Context, webhookID string) error
// DeleteWebhook removes webhook from queue (admin operation)
DeleteWebhook(ctx context.Context, webhookID string) error
Close() error
}
Store captures the persistence requirements for paywall state.
Batch Operations ¶
The Store interface provides batch operations for improved performance:
- SaveCartQuotes / GetCartQuotes: Bulk cart operations (100x faster for bulk imports)
- SaveRefundQuotes: Bulk refund creation (for batch refund processing)
- RecordPayments: Bulk payment recording (for settlement jobs)
PostgreSQL implementation uses prepared statements and bulk queries. MongoDB/FileStore implementations use loop-based fallbacks (can be optimized later).
func NewStore ¶
func NewStore(cfg StoreConfig) (Store, error)
NewStore creates a Store instance based on the provided configuration.
func NewStoreWithDB ¶
func NewStoreWithDB(cfg StoreConfig, sharedDB *sql.DB) (Store, error)
NewStoreWithDB creates a Store instance with an optional shared database pool. If sharedDB is provided (non-nil) for postgres backends, it will be used instead of creating a new connection. Pass nil to create a new connection pool.
type StoreConfig ¶
type StoreConfig struct {
Backend string // "memory", "postgres", "mongodb", or "file"
PostgresURL string
MongoDBURL string
MongoDBDatabase string
FilePath string
PostgresPool config.PostgresPoolConfig // PostgreSQL connection pool settings
CartQuoteTTL time.Duration // How long cart quotes remain valid
RefundQuoteTTL time.Duration // How long refund quotes remain valid
CleanupInterval time.Duration // How often to clean up expired quotes
// Schema mapping (table names for Postgres, collection names for MongoDB)
PaymentTransactionsTableName string // Default: "payment_transactions"
AdminNoncesTableName string // Default: "admin_nonces"
CartQuotesTableName string // Default: "cart_quotes"
RefundQuotesTableName string // Default: "refund_quotes"
WebhookQueueTableName string // Default: "webhook_queue"
}
StoreConfig holds storage backend configuration.
type WebhookStatus ¶
type WebhookStatus string
WebhookStatus represents the current state of a webhook in the queue.
const ( WebhookStatusPending WebhookStatus = "pending" // Waiting for delivery WebhookStatusProcessing WebhookStatus = "processing" // Currently being delivered WebhookStatusFailed WebhookStatus = "failed" // Failed after all retries (DLQ) WebhookStatusSuccess WebhookStatus = "success" // Successfully delivered )