diff --git a/apps/apps.go b/apps/apps.go
index 9751a96b..2db3d0fa 100644
--- a/apps/apps.go
+++ b/apps/apps.go
@@ -248,14 +248,30 @@ func Preview() string {
limit = len(public)
}
for _, a := range public[:limit] {
- sb.WriteString(fmt.Sprintf(`
%s — %s
`,
+ priceTag := `Free`
+ if a.Price > 0 {
+ priceTag = fmt.Sprintf(`%d credits`, a.Price)
+ }
+ sb.WriteString(fmt.Sprintf(`
%s — %s %s
`,
htmlpkg.EscapeString(a.Slug),
htmlpkg.EscapeString(a.Slug),
htmlpkg.EscapeString(a.Name),
- htmlpkg.EscapeString(truncate(a.Description, 60)),
+ htmlpkg.EscapeString(truncate(a.Description, 50)),
+ priceTag,
))
}
- sb.WriteString(fmt.Sprintf(`%d apps available · Build new
`, len(public)))
+ paidCount := 0
+ for _, a := range public {
+ if a.Price > 0 {
+ paidCount++
+ }
+ }
+ sb.WriteString(fmt.Sprintf(`%d apps available`, len(public)))
+ if paidCount > 0 {
+ sb.WriteString(fmt.Sprintf(` · %d paid`, paidCount))
+ }
+ sb.WriteString(` · Build new
`)
+ sb.WriteString(`Build apps, set your price, earn 90%% of every sale.
`)
return sb.String()
}
@@ -381,7 +397,10 @@ func handleList(w http.ResponseWriter, r *http.Request) {
// HTML
var sb strings.Builder
- sb.WriteString(`Small, useful apps that do one thing well. No ads, no tracking, no bloat.
`)
+ sb.WriteString(`Small, useful apps that do one thing well. Build apps, set your price, earn 90% of every sale.
`)
+
+ // Pricing filter
+ pricing := r.URL.Query().Get("pricing")
// Tag filter
tag := r.URL.Query().Get("tag")
@@ -408,6 +427,24 @@ func handleList(w http.ResponseWriter, r *http.Request) {
sb.WriteString(``)
}
+ // Pricing filter pills
+ hasPaid := false
+ hasFree := false
+ for _, a := range list {
+ if a.Price > 0 {
+ hasPaid = true
+ } else {
+ hasFree = true
+ }
+ }
+ if hasPaid && hasFree {
+ sb.WriteString(``)
+ sb.WriteString(fmt.Sprintf(`
All`, pillStyle(pricing == "")))
+ sb.WriteString(fmt.Sprintf(`
Free`, pillStyle(pricing == "free")))
+ sb.WriteString(fmt.Sprintf(`
Paid`, pillStyle(pricing == "paid")))
+ sb.WriteString(`
`)
+ }
+
// Filter by tag
if tag != "" {
var filtered []*App
@@ -419,6 +456,25 @@ func handleList(w http.ResponseWriter, r *http.Request) {
list = filtered
}
+ // Filter by pricing
+ if pricing == "free" {
+ var filtered []*App
+ for _, a := range list {
+ if a.Price == 0 {
+ filtered = append(filtered, a)
+ }
+ }
+ list = filtered
+ } else if pricing == "paid" {
+ var filtered []*App
+ for _, a := range list {
+ if a.Price > 0 {
+ filtered = append(filtered, a)
+ }
+ }
+ list = filtered
+ }
+
if len(list) == 0 {
sb.WriteString(`No apps yet. Create the first one.
`)
} else {
diff --git a/wallet/handlers.go b/wallet/handlers.go
index ff7edfc4..33d51103 100644
--- a/wallet/handlers.go
+++ b/wallet/handlers.go
@@ -38,6 +38,21 @@ func WalletPage(userID string) string {
sb.WriteString(`Add Credits → · Transfer →
`)
sb.WriteString(``)
+ // App earnings summary
+ var totalEarnings int
+ for _, tx := range transactions {
+ if tx.Operation == OpAppRevenue {
+ totalEarnings += tx.Amount
+ }
+ }
+ if totalEarnings > 0 {
+ sb.WriteString(``)
+ sb.WriteString(`
App Earnings
`)
+ sb.WriteString(fmt.Sprintf(`
%d credits earned from your apps (recent)
`, totalEarnings))
+ sb.WriteString(`
You keep 90%% of every sale. Manage your apps →
`)
+ sb.WriteString(`
`)
+ }
+
if !isAdmin {
// Self-hosting note
sb.WriteString(``)
@@ -80,7 +95,19 @@ func WalletPage(userID string) string {
for _, tx := range transactions {
typeLabel := tx.Operation
- if tx.Type == TxTopup {
+ if tx.Operation == OpAppUse {
+ if appSlug, ok := tx.Metadata["app"].(string); ok {
+ typeLabel = "App: " + appSlug
+ } else {
+ typeLabel = "App usage"
+ }
+ } else if tx.Operation == OpAppRevenue {
+ if appSlug, ok := tx.Metadata["app"].(string); ok {
+ typeLabel = "Earned: " + appSlug
+ } else {
+ typeLabel = "App revenue"
+ }
+ } else if tx.Type == TxTopup {
typeLabel = "Deposit"
} else if tx.Type == TxTransfer {
if tx.Amount > 0 {
@@ -626,8 +653,23 @@ func handlePricing(w http.ResponseWriter, r *http.Request) {
sb.WriteString(fmt.Sprintf(`
| %s | %d |
`, item.Description, item.Cost))
}
sb.WriteString(``)
+ sb.WriteString(``)
+
+ // App marketplace pricing
+ sb.WriteString(``)
+ sb.WriteString(`
App Marketplace
`)
+ sb.WriteString(`
Build apps and charge per use. You set the price, you earn the revenue.
`)
+ sb.WriteString(`
`)
+ sb.WriteString(`| Price range | 0–1000 credits per use |
`)
+ sb.WriteString(`| Creator share | 90% |
`)
+ sb.WriteString(`| Platform fee | 10% |
`)
+ sb.WriteString(`| Free apps | No charge |
`)
+ sb.WriteString(`
`)
+ sb.WriteString(`
Build an app →
`)
+ sb.WriteString(`
`)
+
sb.WriteString(`JSON: curl -H "Accept: application/json" /wallet/pricing
`)
- sb.WriteString(``)
+ sb.WriteString(``)
html := app.RenderHTMLForRequest("Pricing", "Platform pricing and costs", sb.String(), r)
w.Write([]byte(html))