feat(shop-sales): ledger contracts + docs for consumer PSP sales tracking

platform/order + platform/event gain the sale.paid and refund.issued wire
contracts (per-rate VatBuckets, PSP provider, timestamps) that drive the
ledger's SHOP-/REFUND- vouchers (go-cart-actor 2b819bd). docs/status.md
records the shop-sales + PSP settlement stack (tickets 17 + 18); the
wayfinder ledger tracker gains the two closed tickets and their Decisions
entries. Bumps go-cart-actor + backoffice (PSP float stat).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
mats committed 2026-08-17 20:01:58 +02:00
1 parent 16655e8dbc
commit 7c2a34cc7a
3 files changed
+107

No files matched your search

+48
View File
@@ -137,3 +137,51 @@ func DecodeInvoiceCredited(raw []byte) (InvoiceCredited, error) {
return c, err
}
// SalePaid is the payload of an event.SalePaid envelope: a consumer (PSP)
// order was captured. It is the accounting fact for shop sales — the ledger
// aggregates captured sales into the daily SHOP- voucher (clearing D /
// 304x C per rate / 261x C per rate). Invoice orders never emit it (they book
// via invoice.issued).
//
// PaymentMethod is the PSP provider name (e.g. "stripe", "klarna"), carried
// from the order's payment record. CapturedAtMs is the capture moment — the
// ledger keys the daily voucher on its UTC date (fakturametoden: capture date
// decides the VAT period).
type SalePaid struct {
OrderID string `json:"order_id"`
PaymentMethod string `json:"payment_method,omitempty"`
AmountIncVat int64 `json:"amount_inc_vat"`
VatBuckets []VatBucket `json:"vat_buckets,omitempty"`
CapturedAtMs int64 `json:"captured_at_ms"`
}
func (s SalePaid) Encode() ([]byte, error) { return json.Marshal(s) }
func DecodeSalePaid(raw []byte) (SalePaid, error) {
var s SalePaid
err := json.Unmarshal(raw, &s)
return s, err
}
// RefundIssued is the payload of an event.RefundIssued envelope: a refund was
// issued against a captured PSP order. The amount and per-rate VAT split are
// pro-rated from the order's captured sale (line-level return mapping is out
// of scope). The ledger books the reversal voucher (clearing C / 304x D /
// 261x D per rate), idempotent per RefundID (the provider refund reference,
// unique per refund). Invoice orders reverse via invoice.credited instead.
type RefundIssued struct {
OrderID string `json:"order_id"`
RefundID string `json:"refund_id"`
AmountIncVat int64 `json:"amount_inc_vat"`
VatBuckets []VatBucket `json:"vat_buckets,omitempty"`
RefundedAtMs int64 `json:"refunded_at_ms"`
}
func (r RefundIssued) Encode() ([]byte, error) { return json.Marshal(r) }
func DecodeRefundIssued(raw []byte) (RefundIssued, error) {
var r RefundIssued
err := json.Unmarshal(raw, &r)
return r, err
}