mirror of
https://github.com/hanzoai/commerce.git
synced 2026-10-30 15:08:42 +00:00
POST /v1/billing/subscribe/card takes an optional coupon code. The coupon is a models/coupon row (Filter "plan", Type percent) in the reserved platform namespace "admin", beside the platform promo. It must be enabled, inside [StartDate, EndDate], under Limit and cover the plan; an org and a card each redeem a code once (models/couponredemption rows), and the use is reserved under the code's lock before the card is charged and given back on a decline. Any refusal is a 400 or 409 before the card is touched. The percent comes off the first charge only, through engine.Discounted, the same arithmetic the first invoice uses, so charge == AmountDue. The first invoice names the coupon; the subscription never carries it, so renewals bill the catalog price (less only a promo the row carries). GET /v1/billing/plans?coupon=CODE quotes couponCode, couponPercent, couponFirstCents and couponEnds on covered paid rows and couponError (unknown, expired, exhausted, not_applicable) on the rest. Without the parameter the response is unchanged. SuperAdmin: GET/PUT /v1/platform/coupons/:code (RequirePlatformAdmin), audited as coupon.created/coupon.updated billing events naming the actor. middleware.Actor is the one actor-naming rule, moved from billing's minter. Co-authored-by: Hanzo Dev <[email protected]>
32 lines
977 B
Go
32 lines
977 B
Go
// Copyright (c) 2014-present Hanzo AI, Inc.
|
|
// Licensed under MIT OR Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.
|
|
|
|
package middleware
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/zap-proto/zip"
|
|
|
|
"github.com/hanzoai/commerce/middleware/iammiddleware"
|
|
)
|
|
|
|
// Actor names the principal behind a privileged act — a mint, a comp, a balance
|
|
// adjustment, a platform coupon edit — for the row or audit record it writes.
|
|
// Every such route admits only the platform principal (MayMintMoney /
|
|
// RequirePlatformAdmin: owner "admin"), whose validated claims name them: the
|
|
// email, else owner/name, else the subject, else the owner.
|
|
func Actor(c *zip.Ctx) string {
|
|
claims := iammiddleware.GetIAMClaims(c)
|
|
if e := strings.TrimSpace(claims.Email); e != "" {
|
|
return e
|
|
}
|
|
if n := strings.TrimSpace(claims.Name); n != "" {
|
|
return strings.TrimSpace(claims.Owner) + "/" + n
|
|
}
|
|
if s := strings.TrimSpace(claims.Subject); s != "" {
|
|
return s
|
|
}
|
|
return strings.TrimSpace(claims.Owner)
|
|
}
|