subscriptionstore

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 21, 2026 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.
  • ORM Powered: Built on dracory/neat for clean database abstraction.

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 via neat ORM.
  • 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)
import (
    "context"
    "database/sql"
    "github.com/dracory/subscriptionstore"
    _ "modernc.org/sqlite"
)

db, _ := sql.Open("sqlite", ":memory:")

store, err := subscriptionstore.NewStore(subscriptionstore.NewStoreOptions{
    DB:                    db,
    PlanTableName:         "plans",
    SubscriptionTableName: "subscriptions",
    AutomigrateEnabled:    true,
})
if err != nil {
    panic(err)
}
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.GetID()).
    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 MAX_DATETIME = "9999-12-31 23:59:59"
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
	AutomigrateEnabled    bool
	DebugEnabled          bool
}

NewStoreOptions define the options for creating a new subscription store

type PlanInterface

type PlanInterface interface {
	IsSoftDeleted() bool

	GetCreatedAt() string
	GetCreatedAtCarbon() *carbon.Carbon
	SetCreatedAt(createdAt string) PlanInterface

	GetCurrency() string
	SetCurrency(currency string) PlanInterface

	GetDescription() string
	SetDescription(description string) PlanInterface

	GetFeatures() string
	SetFeatures(features string) PlanInterface

	GetID() string
	SetID(id string) PlanInterface

	GetInterval() string
	SetInterval(interval string) PlanInterface

	GetMemo() string
	SetMemo(memo string) PlanInterface

	GetMetas() (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)

	GetPrice() string
	GetPriceFloat() float64
	SetPrice(price string) PlanInterface

	GetSoftDeletedAt() string
	GetSoftDeletedAtCarbon() *carbon.Carbon
	SetSoftDeletedAt(deletedAt string) PlanInterface

	GetStatus() string
	SetStatus(status string) PlanInterface

	GetStripePriceID() string
	SetStripePriceID(stripePriceID string) PlanInterface

	GetTitle() string
	SetTitle(title string) PlanInterface

	GetType() string
	SetType(type_ string) PlanInterface

	GetUpdatedAt() string
	GetUpdatedAtCarbon() *carbon.Carbon
	SetUpdatedAt(updatedAt string) PlanInterface
}

PlanInterface defines the methods for a Plan entity

func NewPlan

func NewPlan() PlanInterface

func NewPlanFromExistingData

func NewPlanFromExistingData(data map[string]string) PlanInterface

type PlanQueryInterface

type PlanQueryInterface interface {
	Validate() error

	IsCountOnly() bool
	HasCountOnly() bool
	SetCountOnly(countOnly bool) PlanQueryInterface

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

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

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

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

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

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

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

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

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

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

	HasSortOrder() bool
	SortOrder() string
	SetSortOrder(sortOrder string) PlanQueryInterface

	HasSoftDeletedIncluded() bool
	SoftDeletedIncluded() bool
	SetSoftDeletedIncluded(withSoftDeleted bool) PlanQueryInterface
}

PlanQueryInterface defines the interface for querying plans.

func NewPlanQuery

func NewPlanQuery() PlanQueryInterface

NewPlanQuery creates a new plan query

func PlanQuery

func PlanQuery() PlanQueryInterface

PlanQuery is a shortcut alias for NewPlanQuery

type StoreInterface

type StoreInterface interface {
	MigrateDown(ctx context.Context, tx ...*sql.Tx) error
	MigrateUp(ctx context.Context, tx ...*sql.Tx) error
	EnableDebug(debug bool)

	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 interface for the subscription store.

func NewStore

func NewStore(opts NewStoreOptions) (StoreInterface, error)

NewStore creates a new subscription store

type SubscriptionInterface

type SubscriptionInterface interface {
	IsSoftDeleted() bool

	GetCreatedAt() string
	GetCreatedAtCarbon() *carbon.Carbon
	SetCreatedAt(createdAt string) SubscriptionInterface

	GetID() string
	SetID(id string) SubscriptionInterface

	GetStatus() string
	SetStatus(status string) SubscriptionInterface

	GetSubscriberID() string
	SetSubscriberID(subscriberID string) SubscriptionInterface

	GetPlanID() string
	SetPlanID(planID string) SubscriptionInterface

	GetPeriodStart() string
	GetPeriodStartCarbon() *carbon.Carbon
	SetPeriodStart(periodStart string) SubscriptionInterface

	GetPeriodEnd() string
	GetPeriodEndCarbon() *carbon.Carbon
	SetPeriodEnd(periodEnd string) SubscriptionInterface

	GetCancelAtPeriodEnd() bool
	SetCancelAtPeriodEnd(cancelAtPeriodEnd bool) SubscriptionInterface

	GetPaymentMethodID() string
	SetPaymentMethodID(paymentMethodID string) SubscriptionInterface

	GetMemo() string
	SetMemo(memo string) SubscriptionInterface

	GetMetas() (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)

	GetSoftDeletedAt() string
	GetSoftDeletedAtCarbon() *carbon.Carbon
	SetSoftDeletedAt(deletedAt string) SubscriptionInterface

	GetUpdatedAt() string
	GetUpdatedAtCarbon() *carbon.Carbon
	SetUpdatedAt(updatedAt string) SubscriptionInterface
}

SubscriptionInterface defines the methods for a Subscription entity

func NewSubscription

func NewSubscription() SubscriptionInterface

func NewSubscriptionFromExistingData

func NewSubscriptionFromExistingData(data map[string]string) SubscriptionInterface

type SubscriptionQueryInterface

type SubscriptionQueryInterface interface {
	Validate() error

	IsCountOnly() bool
	HasCountOnly() bool
	SetCountOnly(countOnly bool) SubscriptionQueryInterface

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

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

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

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

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

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

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

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

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

	HasSortOrder() bool
	SortOrder() string
	SetSortOrder(sortOrder string) SubscriptionQueryInterface

	HasSoftDeletedIncluded() bool
	SoftDeletedIncluded() bool
	SetSoftDeletedIncluded(withSoftDeleted bool) SubscriptionQueryInterface
}

SubscriptionQueryInterface defines the interface for querying subscriptions.

func NewSubscriptionQuery

func NewSubscriptionQuery() SubscriptionQueryInterface

NewSubscriptionQuery creates a new subscription query

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