subscriptions.go raw
1 package neo4j
2
3 import (
4 "encoding/json"
5 "fmt"
6 "time"
7
8 "next.orly.dev/pkg/database"
9 "next.orly.dev/pkg/nostr/encoders/hex"
10 )
11
12 // Subscription and payment methods
13 // Simplified implementation using marker-based storage via Badger
14 // For production graph-based storage, these could use Neo4j nodes with relationships
15
16 // GetSubscription retrieves subscription information for a pubkey
17 func (n *N) GetSubscription(pubkey []byte) (*database.Subscription, error) {
18 key := "sub_" + hex.Enc(pubkey)
19 data, err := n.GetMarker(key)
20 if err != nil {
21 return nil, err
22 }
23
24 var sub database.Subscription
25 if err := json.Unmarshal(data, &sub); err != nil {
26 return nil, fmt.Errorf("failed to unmarshal subscription: %w", err)
27 }
28
29 return &sub, nil
30 }
31
32 // IsSubscriptionActive checks if a pubkey has an active subscription
33 func (n *N) IsSubscriptionActive(pubkey []byte) (bool, error) {
34 sub, err := n.GetSubscription(pubkey)
35 if err != nil {
36 return false, nil // No subscription = not active
37 }
38
39 return sub.PaidUntil.After(time.Now()), nil
40 }
41
42 // ExtendSubscription extends a subscription by the specified number of days
43 func (n *N) ExtendSubscription(pubkey []byte, days int) error {
44 key := "sub_" + hex.Enc(pubkey)
45
46 // Get existing subscription or create new
47 var sub database.Subscription
48 data, err := n.GetMarker(key)
49 if err == nil {
50 if err := json.Unmarshal(data, &sub); err != nil {
51 return fmt.Errorf("failed to unmarshal subscription: %w", err)
52 }
53 } else {
54 // New subscription - set trial period
55 sub.TrialEnd = time.Now()
56 sub.PaidUntil = time.Now()
57 }
58
59 // Extend expiration
60 if sub.PaidUntil.Before(time.Now()) {
61 sub.PaidUntil = time.Now()
62 }
63 sub.PaidUntil = sub.PaidUntil.Add(time.Duration(days) * 24 * time.Hour)
64
65 // Save
66 data, err = json.Marshal(sub)
67 if err != nil {
68 return fmt.Errorf("failed to marshal subscription: %w", err)
69 }
70
71 return n.SetMarker(key, data)
72 }
73
74 // RecordPayment records a payment for subscription extension
75 func (n *N) RecordPayment(
76 pubkey []byte, amount int64, invoice, preimage string,
77 ) error {
78 // Store payment in payments list
79 key := "payments_" + hex.Enc(pubkey)
80
81 var payments []database.Payment
82 data, err := n.GetMarker(key)
83 if err == nil {
84 if err := json.Unmarshal(data, &payments); err != nil {
85 return fmt.Errorf("failed to unmarshal payments: %w", err)
86 }
87 }
88
89 payment := database.Payment{
90 Amount: amount,
91 Timestamp: time.Now(),
92 Invoice: invoice,
93 Preimage: preimage,
94 }
95
96 payments = append(payments, payment)
97
98 data, err = json.Marshal(payments)
99 if err != nil {
100 return fmt.Errorf("failed to marshal payments: %w", err)
101 }
102
103 return n.SetMarker(key, data)
104 }
105
106 // GetPaymentHistory retrieves payment history for a pubkey
107 func (n *N) GetPaymentHistory(pubkey []byte) ([]database.Payment, error) {
108 key := "payments_" + hex.Enc(pubkey)
109
110 data, err := n.GetMarker(key)
111 if err != nil {
112 return nil, nil // No payments = empty list
113 }
114
115 var payments []database.Payment
116 if err := json.Unmarshal(data, &payments); err != nil {
117 return nil, fmt.Errorf("failed to unmarshal payments: %w", err)
118 }
119
120 return payments, nil
121 }
122
123 // ExtendBlossomSubscription extends a Blossom storage subscription
124 func (n *N) ExtendBlossomSubscription(
125 pubkey []byte, tier string, storageMB int64, daysExtended int,
126 ) error {
127 key := "blossom_" + hex.Enc(pubkey)
128
129 // Simple implementation - just store tier and expiry
130 data := map[string]interface{}{
131 "tier": tier,
132 "storageMB": storageMB,
133 "extended": daysExtended,
134 "updated": time.Now(),
135 }
136
137 jsonData, err := json.Marshal(data)
138 if err != nil {
139 return fmt.Errorf("failed to marshal blossom subscription: %w", err)
140 }
141
142 return n.SetMarker(key, jsonData)
143 }
144
145 // GetBlossomStorageQuota retrieves the storage quota for a pubkey
146 func (n *N) GetBlossomStorageQuota(pubkey []byte) (quotaMB int64, err error) {
147 key := "blossom_" + hex.Enc(pubkey)
148
149 data, err := n.GetMarker(key)
150 if err != nil {
151 return 0, nil // No subscription = 0 quota
152 }
153
154 var subData map[string]interface{}
155 if err := json.Unmarshal(data, &subData); err != nil {
156 return 0, fmt.Errorf("failed to unmarshal blossom data: %w", err)
157 }
158
159 if storageMB, ok := subData["storageMB"].(float64); ok {
160 return int64(storageMB), nil
161 }
162
163 return 0, nil
164 }
165
166 // IsFirstTimeUser checks if this is the first time a user is accessing the relay
167 func (n *N) IsFirstTimeUser(pubkey []byte) (bool, error) {
168 key := "first_seen_" + hex.Enc(pubkey)
169
170 // If marker exists, not first time
171 if n.HasMarker(key) {
172 return false, nil
173 }
174
175 // Mark as seen
176 if err := n.SetMarker(key, []byte{1}); err != nil {
177 return true, err
178 }
179
180 return true, nil
181 }
182