digitalgoods

package module
v0.0.0-...-3ce0f23 Latest Latest
Warning

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

Go to latest
Published: Apr 5, 2024 License: GPL-3.0 Imports: 8 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

View Source
var ISOCountryCodes = [...]string{}/* 249 elements not displayed */

ISO-3166-1

Functions

func IsISOCountryCode

func IsISOCountryCode(s string) bool

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 {
	Name        string // not translated
	ID          string // for <details>
	Alert       map[string]string
	Description map[string]string
	Variants    []Variant
}

func (Article) NameHTML

func (article Article) NameHTML() template.HTML

func (Article) TranslateAlert

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

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

func (Article) TranslateDescription

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

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

type Cart

type Cart struct {
	CountryID string
	Units     map[CartItem]int // item -> quantity
}

func (*Cart) Add

func (cart *Cart) Add(variantID, variantCountry string, quantity int)

func (*Cart) Get

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

func (*Cart) Has

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

type CartItem

type CartItem struct {
	VariantID      string
	VariantCountry string
}

type Catalog

type Catalog []Category

func (Catalog) GroupOrder

func (catalog Catalog) GroupOrder(order Order) []OrderedArticle

groups order by article

func (Catalog) Variant

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

func (Catalog) Variants

func (catalog Catalog) Variants() []Variant

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"` // legacy json id
	CountryID    string `json:"country-id"`
	ID           string `json:"id"` // can be the code, but not necessarily
	Image        []byte `json:"image"`
	DeliveryDate string `json:"delivery-date"`
}

func (*DeliveredItem) ImageSrc

func (item *DeliveredItem) ImageSrc() template.URL

type Delivery

type Delivery []DeliveredItem

type Order

type Order []OrderRow

func (*Order) Decrement

func (order *Order) Decrement(variantID, countryID 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"`     // legacy json id
	VariantID string `json:"article-id"` // legacy json id
	CountryID string `json:"country-id"`
	ItemPrice int    `json:"item-price"` // euro cents, price at order time
}

func (OrderRow) Sum

func (o OrderRow) Sum() int

type OrderedArticle

type OrderedArticle struct {
	Article
	Variants []OrderedVariant
}

type OrderedVariant

type OrderedVariant struct {
	Variant
	Rows []OrderRow // TODO just one OrderRow?
}

type Purchase

type Purchase struct {
	ID          string
	AccessKey   string
	PaymentKey  string
	Status      Status
	NotifyProto string
	NotifyAddr  string
	Ordered     Order
	Delivered   Delivery
	CreateDate  string // yyyy-mm-dd
	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 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 on 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]map[string]int // variant => country (or "all") => count

func (Stock) FeaturedCountryIDs

func (stock Stock) FeaturedCountryIDs(variant Variant) []string

func (Stock) Get

func (stock Stock) Get(variant Variant, country string) int

func (Stock) Max

func (stock Stock) Max(variant Variant, country string) int

Max returns the max value which can be ordered (like the HTML input max attribute). The stock quantity should be displayed separately, so users know how many items can be delivered instantly.

func (Stock) OnDemandOnly

func (stock Stock) OnDemandOnly(variant Variant, country string) bool

func (Stock) OtherCountryIDs

func (stock Stock) OtherCountryIDs(variant Variant) []string

type Variant

type Variant struct {
	ID         string
	Name       string
	Price      int // euro cents
	OnDemand   bool
	HasCountry bool // ISO country
}

func (Variant) NameHTML

func (variant Variant) NameHTML() template.HTML

Directories

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

Jump to

Keyboard shortcuts

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