subscriptionstore

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 21, 2025 License: AGPL-3.0 Imports: 17 Imported by: 0

README

Subscription Store Open in Gitpod

Tests Status Go Report Card PkgGoDev

Introduction

A powerful, extensible Go module for managing subscription plans and subscriptions, using a clean interface-driven architecture. All data access is performed through interfaces, supporting real database connections (including in-memory SQLite for testing) and allowing for easy extension or customization.

Features

  • Plan & Subscription Management: Create, update, query, and delete plans and subscriptions.
  • Interface-Driven: All entities and stores are accessed via interfaces, enabling mocking, swapping implementations, and extension.
  • Custom Metadata (Metas): Attach arbitrary key-value data to plans and subscriptions.
  • Flexible Queries: Use query interfaces to filter, paginate, and sort results.
  • Real DB Testing: Tests use in-memory SQLite for realistic, high-confidence tests.
  • Multi-Database Support: Powered by Goqu, you can use SQLite, MySQL, PostgreSQL, and more—just change the driver and connection string.

Architecture Overview

  • Entities: Plan and Subscription are private types, always accessed via their public interfaces (PlanInterface, SubscriptionInterface).
  • Stores: Data access objects implement the StoreInterface and are responsible for all DB operations, using Goqu for SQL dialect independence.
  • Queries: Query interfaces (PlanQueryInterface, SubscriptionQueryInterface) allow for composable, type-safe filtering.
  • Extensibility: Swap or extend any entity, store, or query logic by implementing the relevant interface.

Quick Start Example

1. Initialize the Store (SQLite Example, easily swappable)
import (
    "context"
    "github.com/dracory/subscriptionstore"
    _ "modernc.org/sqlite" // Or use _ "github.com/go-sql-driver/mysql" for MySQL, etc.
    "database/sql"
)

// For SQLite (in-memory):
db, _ := sql.Open("sqlite", ":memory:")
// For MySQL: db, _ := sql.Open("mysql", "user:pass@tcp(localhost:3306)/dbname")
// For PostgreSQL: db, _ := sql.Open("postgres", "host=localhost user=... password=... dbname=... sslmode=disable")

store := subscriptionstore.NewStore(db, "sqlite", "plans", "subscriptions")
store.AutoMigrate(context.Background())
2. Create a New Plan
plan := subscriptionstore.NewPlan().
    SetTitle("Pro Plan").
    SetDescription("Best for teams").
    SetFeatures("Unlimited users, Priority support").
    SetPrice("19.99").
    SetStatus("active").
    SetType("subscription").
    SetInterval(subscriptionstore.PLAN_INTERVAL_MONTHLY).
    SetCurrency("usd")

// Add custom metadata
plan.SetMeta("max_projects", "100")
plan.SetMeta("support_level", "priority")

err := store.PlanCreate(context.Background(), plan)
3. Create a Subscription and Attach to a Plan
subscription := subscriptionstore.NewSubscription().
    SetSubscriberID("user_123").
    SetPlanID(plan.ID()).
    SetStatus("active").
    SetPaymentMethodID("pm_abc123")

// Add custom metadata to subscription
subscription.SetMeta("trial", "true")

err = store.SubscriptionCreate(context.Background(), subscription)
4. Querying Plans and Subscriptions
// List all active plans
query := subscriptionstore.PlanQuery().SetStatus("active")
plans, err := store.PlanList(context.Background(), query)

// Find subscriptions for a user
subQuery := subscriptionstore.SubscriptionQuery().SetSubscriberID("user_123")
subs, err := store.SubscriptionList(context.Background(), subQuery)
5. Using Metas for Custom Data
// Set a meta value
plan.SetMeta("custom_key", "custom_value")

// Get a meta value
value, _ := plan.Meta("custom_key")

// Check if a meta exists
exists, _ := plan.HasMeta("custom_key")

// Remove a meta value
plan.DeleteMeta("custom_key")

Extending the System

Everything in subscriptionstore is accessed via interfaces. To extend or customize:

  • Custom Entities: Implement PlanInterface or SubscriptionInterface if you want to add new fields or behaviors.
  • Custom Stores: Implement StoreInterface for new data sources or custom logic (e.g., sharding, caching).
  • Custom Queries: Implement the query interfaces for advanced filtering or external integrations.

This design enables easy swapping of implementations, mocking for tests, or plugging in new backends.


Testing

Tests use a real, in-memory SQLite database. No mocks are used—tests exercise the actual store logic for maximum reliability.


License

This software is subject to both open-source and commercial licensing.

For commercial licensing inquiries, please contact: https://lesichkov.co.uk/contact

Documentation

Index

Constants

View Source
const COLUMN_CANCEL_AT_PERIOD_END = "cancel_at_period_end"
View Source
const COLUMN_CREATED_AT = "created_at"
View Source
const COLUMN_CURRENCY = "currency"
View Source
const COLUMN_DESCRIPTION = "description"
View Source
const COLUMN_FEATURES = "features"
View Source
const COLUMN_ID = "id"
View Source
const COLUMN_INTERVAL = "interval"
View Source
const COLUMN_MEMO = "memo"
View Source
const COLUMN_METAS = "metas"
View Source
const COLUMN_PAYMENT_METHOD_ID = "payment_method_id"
View Source
const COLUMN_PERIOD_END = "period_end"
View Source
const COLUMN_PERIOD_START = "period_start"
View Source
const COLUMN_PLAN_ID = "plan_id"
View Source
const COLUMN_PRICE = "price"
View Source
const COLUMN_SOFT_DELETED_AT = "soft_deleted_at"
View Source
const COLUMN_STATUS = "status"
View Source
const COLUMN_STRIPE_PRICE_ID = "stripe_price_id"
View Source
const COLUMN_SUBSCRIBER_ID = "subscriber_id"
View Source
const COLUMN_TITLE = "title"
View Source
const COLUMN_TYPE = "type"
View Source
const COLUMN_UPDATED_AT = "updated_at"
View Source
const CURRENCY_EUR = "EUR"
View Source
const CURRENCY_GBP = "GBP"
View Source
const CURRENCY_USD = "USD"
View Source
const NO = "no"
View Source
const PLAN_INTERVAL_DAILY = "daily"
View Source
const PLAN_INTERVAL_MONTHLY = "monthly"
View Source
const PLAN_INTERVAL_NONE = "none"
View Source
const PLAN_INTERVAL_QUARTERLY = "quarterly"
View Source
const PLAN_INTERVAL_WEEKLY = "weekly"
View Source
const PLAN_INTERVAL_YEARLY = "yearly"
View Source
const PLAN_STATUS_ACTIVE = "active"
View Source
const PLAN_STATUS_INACTIVE = "inactive"
View Source
const PLAN_TYPE_BRONZE = "bronze"
View Source
const PLAN_TYPE_GOLD = "gold"
View Source
const PLAN_TYPE_PLATINUM = "platinum"
View Source
const PLAN_TYPE_SILVER = "silver"
View Source
const PLAN_TYPE_TRIAL = "trial"
View Source
const SUBSCRIPTION_STATUS_ACTIVE = "active"
View Source
const SUBSCRIPTION_STATUS_CANCELLED = "cancelled"
View Source
const SUBSCRIPTION_STATUS_INACTIVE = "inactive"
View Source
const YES = "yes"

Variables

This section is empty.

Functions

This section is empty.

Types

type NewStoreOptions

type NewStoreOptions struct {
	PlanTableName         string
	SubscriptionTableName string
	DB                    *sql.DB
	DbDriverName          string
	AutomigrateEnabled    bool
	DebugEnabled          bool
	SqlLogger             *slog.Logger
}

NewStoreOptions define the options for creating a new block store

type PlanInterface

type PlanInterface interface {
	Data() map[string]string
	DataChanged() map[string]string
	MarkAsNotDirty()

	CreatedAt() string
	SetCreatedAt(createdAt string) PlanInterface
	CreatedAtCarbon() *carbon.Carbon

	Currency() string
	SetCurrency(currency string) PlanInterface

	SoftDeletedAt() string
	SetSoftDeletedAt(softDeletedAt string) PlanInterface
	SoftDeletedAtCarbon() *carbon.Carbon

	Description() string
	SetDescription(description string) PlanInterface

	ID() string
	SetID(id string) PlanInterface

	Features() string
	SetFeatures(features string) PlanInterface

	Interval() string
	SetInterval(interval string) PlanInterface

	Memo() string
	SetMemo(memo string) PlanInterface

	Metas() (map[string]string, error)
	SetMetas(data map[string]string) (PlanInterface, error)

	HasMeta(key string) (bool, error)
	Meta(key string) (string, error)
	SetMeta(key string, value string) (PlanInterface, error)
	DeleteMeta(key string) (PlanInterface, error)

	Price() string
	PriceFloat() float64
	SetPrice(price string) PlanInterface

	Status() string
	SetStatus(status string) PlanInterface

	Title() string
	SetTitle(title string) PlanInterface

	Type() string
	SetType(type_ string) PlanInterface

	StripePriceID() string
	SetStripePriceID(stripePriceID string) PlanInterface

	UpdatedAt() string
	SetUpdatedAt(updatedAt string) PlanInterface
	UpdatedAtCarbon() *carbon.Carbon
}

PlanInterface defines the methods for a Plan entity This interface can be implemented by any Plan struct for flexibility and testability.

func NewPlan

func NewPlan() PlanInterface

func NewPlanFromExistingData

func NewPlanFromExistingData(data map[string]string) PlanInterface

type PlanQueryInterface

type PlanQueryInterface interface {
	ToQuery(store StoreInterface) *goqu.SelectDataset

	ID() string
	HasID() bool
	SetID(id string) PlanQueryInterface

	IDIn() []string
	HasIDIn() bool
	SetIDIn(idIn []string) PlanQueryInterface

	Status() string
	HasStatus() bool
	SetStatus(status string) PlanQueryInterface

	StatusIn() []string
	HasStatusIn() bool
	SetStatusIn(statusIn []string) PlanQueryInterface

	Interval() string
	HasInterval() bool
	SetInterval(interval string) PlanQueryInterface

	IntervalIn() []string
	HasIntervalIn() bool
	SetIntervalIn(intervalIn []string) PlanQueryInterface

	Type() string
	HasType() bool
	SetType(type_ string) PlanQueryInterface

	Offset() int
	HasOffset() bool
	SetOffset(offset int) PlanQueryInterface

	Limit() int
	HasLimit() bool
	SetLimit(limit int) PlanQueryInterface

	OrderBy() string
	HasOrderBy() bool
	SetOrderBy(orderBy string) PlanQueryInterface

	OrderDirection() string
	HasOrderDirection() bool
	SetOrderDirection(orderByDirection string) PlanQueryInterface

	CountOnly() bool
	HasCountOnly() bool
	SetCountOnly(countOnly bool) PlanQueryInterface

	WithDeleted() bool
	HasWithDeleted() bool
	SetWithDeleted(withDeleted bool) PlanQueryInterface
}

PlanQueryInterface defines the methods for querying plans using PlanQueryOptions.

func NewPlanQuery

func NewPlanQuery() PlanQueryInterface

func PlanQuery

func PlanQuery() PlanQueryInterface

PlanQuery is a shortcut alias for NewPlanQuery

type StoreInterface

type StoreInterface interface {
	AutoMigrate(ctx context.Context) error
	EnableDebug(debug bool)

	DatabaseDriverName() string

	PlanCount(ctx context.Context, query PlanQueryInterface) (int64, error)
	PlanCreate(ctx context.Context, plan PlanInterface) error
	PlanDelete(ctx context.Context, plan PlanInterface) error
	PlanDeleteByID(ctx context.Context, id string) error
	PlanExists(ctx context.Context, planID string) (bool, error)
	PlanFindByID(ctx context.Context, id string) (PlanInterface, error)
	PlanList(ctx context.Context, query PlanQueryInterface) ([]PlanInterface, error)
	PlanSoftDelete(ctx context.Context, plan PlanInterface) error
	PlanSoftDeleteByID(ctx context.Context, id string) error
	PlanTableName() string
	PlanUpdate(ctx context.Context, plan PlanInterface) error

	SubscriptionCount(ctx context.Context, query SubscriptionQueryInterface) (int64, error)
	SubscriptionCreate(ctx context.Context, subscription SubscriptionInterface) error
	SubscriptionDelete(ctx context.Context, subscription SubscriptionInterface) error
	SubscriptionDeleteByID(ctx context.Context, id string) error
	SubscriptionExists(ctx context.Context, subscriptionID string) (bool, error)
	SubscriptionFindByID(ctx context.Context, id string) (SubscriptionInterface, error)
	SubscriptionList(ctx context.Context, query SubscriptionQueryInterface) ([]SubscriptionInterface, error)
	SubscriptionSoftDelete(ctx context.Context, subscription SubscriptionInterface) error
	SubscriptionSoftDeleteByID(ctx context.Context, id string) error
	SubscriptionTableName() string
	SubscriptionUpdate(ctx context.Context, subscription SubscriptionInterface) error
}

StoreInterface defines the methods for the Subscription store This interface allows for easier testing and separation of concerns between the store implementation and its consumers.

func NewStore

func NewStore(opts NewStoreOptions) (StoreInterface, error)

NewStore creates a new block store

type SubscriptionInterface

type SubscriptionInterface interface {
	Data() map[string]string
	DataChanged() map[string]string
	MarkAsNotDirty()

	CreatedAt() string
	SetCreatedAt(createdAt string) SubscriptionInterface
	CreatedAtCarbon() *carbon.Carbon

	ID() string
	SetID(id string) SubscriptionInterface

	Status() string
	SetStatus(status string) SubscriptionInterface

	SubscriberID() string
	SetSubscriberID(subscriberID string) SubscriptionInterface

	PlanID() string
	SetPlanID(planID string) SubscriptionInterface

	PeriodStart() string
	SetPeriodStart(periodStart string) SubscriptionInterface

	PeriodEnd() string
	SetPeriodEnd(periodEnd string) SubscriptionInterface

	CancelAtPeriodEnd() bool
	SetCancelAtPeriodEnd(cancelAtPeriodEnd bool) SubscriptionInterface

	PaymentMethodID() string
	SetPaymentMethodID(paymentMethodID string) SubscriptionInterface

	Memo() string
	SetMemo(memo string) SubscriptionInterface

	Metas() (map[string]string, error)
	SetMetas(data map[string]string) (SubscriptionInterface, error)

	HasMeta(key string) (bool, error)
	Meta(key string) (string, error)
	SetMeta(key string, value string) (SubscriptionInterface, error)
	DeleteMeta(key string) (SubscriptionInterface, error)

	SoftDeletedAt() string
	SetSoftDeletedAt(softDeletedAt string) SubscriptionInterface
	SoftDeletedAtCarbon() *carbon.Carbon

	UpdatedAt() string
	SetUpdatedAt(updatedAt string) SubscriptionInterface
	UpdatedAtCarbon() *carbon.Carbon
}

SubscriptionInterface defines the methods for a Subscription entity This interface can be implemented by any Subscription struct for flexibility and testability.

func NewSubscription

func NewSubscription() SubscriptionInterface

func NewSubscriptionFromExistingData

func NewSubscriptionFromExistingData(data map[string]string) SubscriptionInterface

type SubscriptionQueryInterface

type SubscriptionQueryInterface interface {
	ToQuery(store StoreInterface) *goqu.SelectDataset

	ID() string
	HasID() bool
	SetID(id string) SubscriptionQueryInterface

	IDIn() []string
	HasIDIn() bool
	SetIDIn(idIn []string) SubscriptionQueryInterface

	Status() string
	HasStatus() bool
	SetStatus(status string) SubscriptionQueryInterface

	StatusIn() []string
	HasStatusIn() bool
	SetStatusIn(statusIn []string) SubscriptionQueryInterface

	SubscriberID() string
	HasSubscriberID() bool
	SetSubscriberID(subscriberID string) SubscriptionQueryInterface

	PlanID() string
	HasPlanID() bool
	SetPlanID(planID string) SubscriptionQueryInterface

	Offset() int
	HasOffset() bool
	SetOffset(offset int) SubscriptionQueryInterface

	Limit() int
	HasLimit() bool
	SetLimit(limit int) SubscriptionQueryInterface

	OrderBy() string
	HasOrderBy() bool
	SetOrderBy(orderBy string) SubscriptionQueryInterface

	OrderDirection() string
	HasOrderDirection() bool
	SetOrderDirection(orderByDirection string) SubscriptionQueryInterface

	CountOnly() bool
	HasCountOnly() bool
	SetCountOnly(countOnly bool) SubscriptionQueryInterface

	WithDeleted() bool
	HasWithDeleted() bool
	SetWithDeleted(withDeleted bool) SubscriptionQueryInterface
}

SubscriptionQueryInterface defines the methods for querying subscriptions using SubscriptionQueryOptions.

func NewSubscriptionQuery

func NewSubscriptionQuery() SubscriptionQueryInterface

func SubscriptionQuery

func SubscriptionQuery() SubscriptionQueryInterface

SubscriptionQuery is a shortcut alias for NewSubscriptionQuery

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL