Files
pangolin/server/internal/codes/generator.go
T
wangjia e128c96d22 feat(server): CreateBatch/生成器切换到 codes 库(Mint 单事务全成或全无)(#codes-lib)
Store 挂库 store(NewStore 内部构造 libcodes.Store,签名不变);
generator.go 三函数委托 libcodes,ErrDuplicate 别名同一哨兵;
Service.CreateBatch 走 libcodes.Mint(签名不变)。openMigratedSQLite
挪到共享 sqlite_helper_test.go,backfill_test.go/service_sqlite_test.go
共用。Store.CreateBatch/CreateCode/CodeExistsByHash 原样保留供
webhook.go(Task 6 改用新原语,Task 7 删除)。
2026-07-10 14:24:18 +08:00

42 lines
1.7 KiB
Go

// Package codes implements the activation-code lifecycle:
// batch generation, card-store webhook ingestion, idempotent redemption,
// subscription extension, and CSV export.
//
// Security invariant: plaintext codes are NEVER written to the database or
// to any log. The database stores only SHA-256(canonical_plaintext).
// Plaintext appears exactly once: in the batch-generation response / CSV export.
package codes
import (
libcodes "github.com/wangjia/codes"
)
// GenerateCode generates a single random activation code in canonical Crockford
// Base32 form (15 random data chars + 1 check char = 16 chars total).
// It delegates to the shared library, which uses crypto/rand.
func GenerateCode() (string, error) {
return libcodes.GenerateCode()
}
// Canonicalize converts an activation-code string into canonical form:
// uppercase, with I/L→1 and O→0 substitutions applied.
// Hyphens and spaces are stripped before validation.
// Returns an error if the string contains characters outside the normalised
// Crockford alphabet, if the length is not exactly 16, or if the check character
// is incorrect.
func Canonicalize(code string) (string, error) {
return libcodes.Canonicalize(code)
}
// Hash returns the hex-encoded SHA-256 of the canonical plaintext code.
// This is the value stored in the database; the plaintext is never stored.
func Hash(canonical string) string {
return libcodes.Hash(canonical)
}
// ErrDuplicate is returned by the batch generator when a generated code
// already exists in the database (hash collision). The caller should retry.
// Aliased to the same sentinel as the shared library so errors.Is succeeds
// across both layers.
var ErrDuplicate = libcodes.ErrDuplicate