digitalgoods

package module
v0.0.0-...-61fd96d Latest Latest
Warning

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

Go to latest
Published: Jan 5, 2026 License: GPL-3.0 Imports: 12 Imported by: 0

README

digitalgoods

We sell coupons using our BTCPay Server. No reservations are made (first pay, first serve). Underfulfilled purchases are fulfilled when goods are in stock again. digitalgoods uses an SQLite Database, continuous replication using Litestream is recommended.

A short note on the security model

  • Every purchase has a short but unique ID.
  • The access key gives access to the purchase. It is part of the purchase URL: https://example.com/order/id/access_key. It is important to use rel="noreferrer" in all outgoing links, so the URL won't leak.
  • The purchase id and access key are stored in a cookie for eight hours. If a customer closes their purchase while paying through the BTCPay server, it can be restored without the BTCPay server knowing it.
  • Every purchase has a payment key as a safeguard for some payment methods.

BTCPay Server Configuration

  • User API Keys: enable btcpay.store.canviewinvoices and btcpay.store.cancreateinvoice
  • Store Webhook
    • Payload URL: https://example.com/rpc
    • Automatic redelivery: yes
    • Is enabled: yes
    • Events: "An invoice is processing", "An invoice has expired", "An invoice has been settled"

Documentation

Index

Constants

View Source
const DateFmt = "2006-01-02"

Variables

This section is empty.

Functions

func MakeBrandCatalogs

func MakeBrandCatalogs(catalog Catalog) map[string]BrandCatalog

MakeBrandCatalogs creates a catalog for the backend upload view. It collects stock units by brand.

func Mask

func Mask(s string) string

Mask replaces all but the last six letters of a string by asterisks.

Types

type Article

type Article struct {
	Brand     string
	Name      string // not translated
	Hide      bool
	ImageLink string
	ID        string // for <details> and #anchor
	Desc      map[string]Description
	Variants  []Variant
}

func MakePurchaseCatalog

func MakePurchaseCatalog(catalog Catalog) []Article

MakePurchaseCatalog prepares a catalog for the purchase view. It removes categories and moves duplicate variants into separate articles.

func (Article) NameHTML

func (article Article) NameHTML() template.HTML

func (Article) TranslateAbout

func (article Article) TranslateAbout(l lang.Lang) template.HTML

only supports langs which exist as key, TODO: language.Matcher

func (Article) TranslateAlert

func (article Article) TranslateAlert(l lang.Lang) template.HTML

only supports langs which exist as key, TODO: language.Matcher

func (Article) TranslateHowto

func (article Article) TranslateHowto(l lang.Lang) template.HTML

only supports langs which exist as key, TODO: language.Matcher

func (Article) TranslateLegal

func (article Article) TranslateLegal(l lang.Lang) template.HTML

only supports langs which exist as key, TODO: language.Matcher

type BrandCatalog

type BrandCatalog struct {
	Name       string
	Categories []Category
}

type Cart

type Cart struct {
	CountryID string
	Units     map[string]int // variant id => quantity
}

func (*Cart) Get

func (cart *Cart) Get(articleID, variantID string) int

func (*Cart) Has

func (cart *Cart) Has(article Article) bool

type Catalog

type Catalog []Category

func (Catalog) Articles

func (catalog Catalog) Articles() iter.Seq[Article]

func (Catalog) Products

func (catalog Catalog) Products() []productfeed.Product

assumes that catalog contains every article exactly once

func (Catalog) Variant

func (catalog Catalog) Variant(id string) (Variant, bool)

type Category

type Category struct {
	Name     map[string]string
	Articles []Article
}

func (*Category) TranslateName

func (cat *Category) TranslateName(l lang.Lang) template.HTML

only supports langs which exist as Name key, TODO: language.Matcher

type DeliveredItem

type DeliveredItem struct {
	VariantID    string `json:"article-id"`
	Payload      string `json:"id"`
	DeliveryDate string `json:"delivery-date"`
}

type Delivery

type Delivery []DeliveredItem

type Description

type Description struct {
	Alert string
	About string
	Howto string
	Legal string
}

type Order

type Order []OrderRow

func (*Order) Decrement

func (order *Order) Decrement(variantID string) error

func (Order) Empty

func (order Order) Empty() bool

func (Order) Sum

func (order Order) Sum() int

type OrderRow

type OrderRow struct {
	Quantity  int    `json:"amount"`
	VariantID string `json:"article-id"`
	ItemPrice int    `json:"item-price"`
}

type Purchase

type Purchase struct {
	ID          string
	AccessKey   string
	PaymentKey  string
	Status      Status
	Message     string // from store to customer, in case of problems
	NotifyProto string
	NotifyAddr  string
	Ordered     Order
	Delivered   Delivery
	CreateDate  string // yyyy-mm-dd, for foreign currency rates
	DeleteDate  string // yyyy-mm-dd
	CountryCode string // EU country
}

func (*Purchase) GetUnfulfilled

func (p *Purchase) GetUnfulfilled() (Order, error)

func (*Purchase) Underdelivered

func (p *Purchase) Underdelivered() bool

func (*Purchase) Unpaid

func (p *Purchase) Unpaid() bool

func (*Purchase) Waiting

func (p *Purchase) Waiting() bool

type PurchaseArticle

type PurchaseArticle struct {
	Article
	Variants []PurchaseVariant // shadows Article.Variants
}

func MakePurchaseArticles

func MakePurchaseArticles(catalog []Article, purchase *Purchase) []PurchaseArticle

MakePurchaseArticles runs in O(n^2). Only use it for small catalogs.

func (PurchaseArticle) AnythingDelivered

func (pa PurchaseArticle) AnythingDelivered() bool

type PurchaseVariant

type PurchaseVariant struct {
	Variant
	Quantity   int
	GrossPrice int // in case Variant.Price has changed
	Delivered  []DeliveredItem
}

func (PurchaseVariant) GrossSum

func (pv PurchaseVariant) GrossSum() int

type Sale

type Sale struct {
	ID        string
	Country   string
	PayDate   string
	Name      string
	Amount    int
	GrossSum  int // for all items
	Difftax   int
	IsService bool
	VATRate   string
}

type Status

type Status string
const (
	StatusNew               Status = "new"            // unpaid
	StatusPaymentProcessing Status = "processing"     // e.g. btcpay: "InvoiceProcessing Webhook: Triggers when an invoice is fully paid, but doesn't have the required amount of confirmations on the blockchain yet according to your store's settings."
	StatusUnderdelivered    Status = "underdelivered" // payment settled, but we had not had enough items in stock
	StatusFinalized         Status = "finalized"      // payment settled, codes delivered
)

func (Status) TranslateDescription

func (s Status) TranslateDescription(l lang.Lang) string

func (Status) TranslateName

func (s Status) TranslateName(l lang.Lang) string

type Stock

type Stock map[string]int // stockID => quantity

type UploadBrand

type UploadBrand struct {
	Brand string
	Units []UploadStockUnit
}

type UploadCatalog

type UploadCatalog []UploadBrand

func MakeUploadCatalog

func MakeUploadCatalog(catalog Catalog) UploadCatalog

MakeUploadCatalog creates a catalog for the backend upload view. It collects stock units by brand.

func (UploadCatalog) UploadStockUnit

func (ucatalog UploadCatalog) UploadStockUnit(id string) (UploadStockUnit, bool)

type UploadStockUnit

type UploadStockUnit struct {
	StockID  string
	Variants []Variant
}

type Variant

type Variant struct {
	ID              string
	Name            string
	ImageLink       string
	OptionalFmt     string // must contain exactly one %s placeholder
	OptionalStockID string // same stock for multiple variants
	Price           int    // euro cents
	WarnStock       int
}

func (Variant) Fmt

func (v Variant) Fmt(payload string) string

func (Variant) NameHTML

func (variant Variant) NameHTML() template.HTML

func (Variant) StockID

func (variant Variant) StockID() string

Directories

Path Synopsis
cmd
digitalgoods command
Package userdb implements a very simple, read-only user database.
Package userdb implements a very simple, read-only user database.
cmd/bcrypt command

Jump to

Keyboard shortcuts

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