// 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