package models import ( "time" "github.com/google/uuid" ) // CustomerWalletTransaction is one ledger row as the customer app shows it // (docs/prd-point-coin.md F6). type CustomerWalletTransaction struct { ID uuid.UUID `json:"id"` Currency string `json:"currency"` Type string `json:"type"` // Signed: positive added to the balance, negative taken from it. Amount int64 `json:"amount"` BalanceAfter int64 `json:"balance_after"` Description string `json:"description"` // Where the value came from, set on additions. Source *CustomerWalletTransactionRef `json:"source,omitempty"` // Where the value went, set on deductions. Destination *CustomerWalletTransactionRef `json:"destination,omitempty"` OutletID *uuid.UUID `json:"outlet_id,omitempty"` ReversesTransactionID *uuid.UUID `json:"reverses_transaction_id,omitempty"` // Shared by the two rows of an exchange or a transfer. GroupID *uuid.UUID `json:"group_id,omitempty"` // Additions only: the earliest expiry among the lots it created, nil when none of // them expire, and the lots themselves. ExpiresAt *time.Time `json:"expires_at,omitempty"` Lots []CustomerWalletLot `json:"lots,omitempty"` CreatedAt time.Time `json:"created_at"` } // CustomerWalletTransactionRef points at what a ledger row came from or went to, as // listed in ยง8.1: ORDER, PAYMENT, WALLET_TX, GAME_PLAY, LOT, USER and so on. type CustomerWalletTransactionRef struct { Type string `json:"type"` ID uuid.UUID `json:"id"` } type CustomerWalletLot struct { Amount int64 `json:"amount"` Remaining int64 `json:"remaining"` ExpiresAt *time.Time `json:"expires_at"` } // CustomerWalletExpiring is how much expires on one day. type CustomerWalletExpiring struct { Amount int64 `json:"amount"` // YYYY-MM-DD, Asia/Jakarta. Date string `json:"date"` } // CustomerWalletNearestExpiring is the next day each currency loses balance, nil when // nothing is due to expire. type CustomerWalletNearestExpiring struct { Point *CustomerWalletExpiring `json:"point"` Coin *CustomerWalletExpiring `json:"coin"` } // ListCustomerWalletTransactionsQuery is GET /customer/wallet/transactions. type ListCustomerWalletTransactionsQuery struct { Page int `form:"page"` Limit int `form:"limit"` Currency string `form:"currency"` // One type, or several separated by commas. Type string `form:"type"` // Inclusive calendar dates, YYYY-MM-DD, Asia/Jakarta. From string `form:"from"` To string `form:"to"` } // AdminCustomerWallet is GET /marketing/customers/:id/wallet (docs/prd-point-coin.md // F7). Unlike the customer's own view it shows the raw balances next to the spendable // ones, every lot that still holds something, and the real names behind each row. type AdminCustomerWallet struct { Customer AdminWalletCustomer `json:"customer"` // Balances as the ledger has them. PointBalance int64 `json:"point_balance"` CoinBalance int64 `json:"coin_balance"` // What can be spent now. Lower than the ledger balance only while lots that have // expired wait for the expiry job. SpendablePointBalance int64 `json:"spendable_point_balance"` SpendableCoinBalance int64 `json:"spendable_coin_balance"` Lots []AdminWalletLot `json:"lots"` Transactions PaginatedResponse[AdminWalletTransaction] `json:"transactions"` } type AdminWalletCustomer struct { ID uuid.UUID `json:"id"` Name string `json:"name"` Phone *string `json:"phone,omitempty"` } type AdminWalletLot struct { ID uuid.UUID `json:"id"` Currency string `json:"currency"` OriginalAmount int64 `json:"original_amount"` RemainingAmount int64 `json:"remaining_amount"` ExpiresAt *time.Time `json:"expires_at"` Expired bool `json:"expired"` SourceTransactionID uuid.UUID `json:"source_transaction_id"` OriginLotID *uuid.UUID `json:"origin_lot_id,omitempty"` CreatedAt time.Time `json:"created_at"` } // AdminWalletTransaction is a ledger row with the names the customer does not see: // the real counterparty of a transfer, the admin behind an adjustment, the cashier who // took a payment, and the outlet. type AdminWalletTransaction struct { CustomerWalletTransaction Counterparty *AdminWalletNamedRef `json:"counterparty,omitempty"` CreatedBy *AdminWalletNamedRef `json:"created_by,omitempty"` Outlet *AdminWalletNamedRef `json:"outlet,omitempty"` Reason *string `json:"reason,omitempty"` Metadata map[string]any `json:"metadata,omitempty"` } type AdminWalletNamedRef struct { ID uuid.UUID `json:"id"` Name string `json:"name"` } // WalletAdjustment is a manual correction by an admin. type WalletAdjustment struct { Currency string // Signed: positive adds, negative takes away. Amount int64 Reason string IdempotencyKey string } // AdminWalletAdjustmentResult is what POST /marketing/customers/:id/wallet/adjust returns. type AdminWalletAdjustmentResult struct { Transaction AdminWalletTransaction `json:"transaction"` SpendablePointBalance int64 `json:"spendable_point_balance"` SpendableCoinBalance int64 `json:"spendable_coin_balance"` // True when the idempotency key had been used before and nothing changed. Replayed bool `json:"replayed"` } // CustomerWalletExpiringList is GET /customer/wallet/expiring (docs/prd-point-coin.md // F6): everything that will expire, per currency and day, soonest first. type CustomerWalletExpiringList struct { Point []CustomerWalletExpiring `json:"point"` Coin []CustomerWalletExpiring `json:"coin"` }