Files
commerce/api/checkout/wire/credit.go
zeekay 1943f1c093 Hanzo Commerce
Multi-tenant commerce and billing: orders, subscriptions, invoices, plans,
usage metering and a credit ledger. One Go binary. MIT OR Apache-2.0.

Claude-Session: https://claude.ai/code/session_01SAtaDFm8VDiWrhYHNNtxMw
2026-09-23 21:52:16 -07:00

80 lines
2.3 KiB
Go

package wire
import (
"errors"
"github.com/zap-proto/zip"
"github.com/hanzoai/commerce/datastore"
"github.com/hanzoai/commerce/log"
"github.com/hanzoai/commerce/middleware"
"github.com/hanzoai/commerce/models/order"
"github.com/hanzoai/commerce/util/json/http"
)
type wireCreditRequest struct {
OrderID string `json:"orderId"`
Amount float64 `json:"amount"`
Currency string `json:"currency"`
Reference string `json:"reference"`
Notes string `json:"notes"`
}
type wireCreditResponse struct {
OrderID string `json:"orderId"`
Status string `json:"status"`
Reference string `json:"reference"`
}
// Credit manually credits an account when a wire transfer is received.
// POST /v1/checkout/wire/credit
// Admin-only endpoint. Marks the pending wire payment as completed.
func Credit(c *zip.Ctx) error {
// Money move (manually marks an order paid): admin-only, enforced inside the
// handler because the route middleware no-ops on the IAM path (Red HIGH-4).
if !middleware.RequireAdmin(c) {
return nil // RequireAdmin already wrote the 403 response
}
var req wireCreditRequest
if err := c.Bind(&req); err != nil {
return http.Fail(c, 400, "Invalid request", err)
}
if req.OrderID == "" {
return http.Fail(c, 400, "orderId is required", errors.New("orderId is required"))
}
if req.Amount <= 0 {
return http.Fail(c, 400, "amount must be positive", errors.New("amount must be positive"))
}
org := middleware.GetOrganization(c)
// Set up the db with the namespaced context. NewNamespaced isolates the
// order in the caller org's own store (Red CRIT-2).
ctx := org.Namespaced(c.Context())
db := datastore.NewNamespaced(ctx)
// Get the order
ord := order.New(db)
if err := ord.GetById(req.OrderID); err != nil {
return http.Fail(c, 404, "Order not found", err)
}
// Mark order as paid via wire credit
ord.Status = order.Open
if err := ord.Put(); err != nil {
log.Error("Failed to save order after wire credit: %v", err, c)
return http.Fail(c, 500, "Failed to update order", err)
}
log.Info("Wire credit applied: order=%s amount=%.2f currency=%s ref=%s",
req.OrderID, req.Amount, req.Currency, req.Reference, c)
return http.Render(c, 200, wireCreditResponse{
OrderID: req.OrderID,
Status: "credited",
Reference: req.Reference,
})
}