recurring.gno
2.83 Kb ยท 105 lines
1package recurring
2
3import (
4 "chain/banker"
5 "chain/runtime"
6 "time"
7
8 "gno.land/p/nt/avl"
9 "gno.land/p/nt/ownable"
10)
11
12// RecurringSubscription represents a subscription that requires periodic payments.
13// It includes the duration of the subscription and the amount required per period.
14type RecurringSubscription struct {
15 ownable.Ownable
16 duration time.Duration
17 amount int64
18 subs *avl.Tree // chain.Address -> time.Time
19}
20
21// NewRecurringSubscription creates and returns a new recurring subscription.
22func NewRecurringSubscription(duration time.Duration, amount int64) *RecurringSubscription {
23 return &RecurringSubscription{
24 Ownable: *ownable.New(),
25 duration: duration,
26 amount: amount,
27 subs: avl.NewTree(),
28 }
29}
30
31// HasValidSubscription verifies if the caller has an active recurring subscription.
32func (rs *RecurringSubscription) HasValidSubscription(addr address) error {
33 expTime, exists := rs.subs.Get(addr.String())
34 if !exists {
35 return ErrNoSub
36 }
37
38 if time.Now().After(expTime.(time.Time)) {
39 return ErrSubExpired
40 }
41
42 return nil
43}
44
45// processSubscription processes the payment for a given receiver and renews or adds their subscription.
46func (rs *RecurringSubscription) processSubscription(receiver address) error {
47 amount := banker.OriginSend()
48
49 if amount.AmountOf("ugnot") != rs.amount {
50 return ErrAmt
51 }
52
53 expTime, exists := rs.subs.Get(receiver.String())
54
55 // If the user is already a subscriber but his subscription has expired, authorize renewal
56 if exists {
57 expiration := expTime.(time.Time)
58 if time.Now().Before(expiration) {
59 return ErrAlreadySub
60 }
61 }
62
63 // Renew or add subscription
64 newExpiration := time.Now().Add(rs.duration)
65 rs.subs.Set(receiver.String(), newExpiration)
66
67 return nil
68}
69
70// Subscribe handles the payment for the caller's subscription.
71func (rs *RecurringSubscription) Subscribe() error {
72 caller := runtime.CurrentRealm().Address()
73
74 return rs.processSubscription(caller)
75}
76
77// GiftSubscription allows the user to pay for a subscription for another user (receiver).
78func (rs *RecurringSubscription) GiftSubscription(receiver address) error {
79 return rs.processSubscription(receiver)
80}
81
82// GetExpiration returns the expiration date of the recurring subscription for a given caller.
83func (rs *RecurringSubscription) GetExpiration(addr address) (time.Time, error) {
84 expTime, exists := rs.subs.Get(addr.String())
85 if !exists {
86 return time.Time{}, ErrNoSub
87 }
88
89 return expTime.(time.Time), nil
90}
91
92// UpdateAmount allows the owner of the subscription contract to change the required subscription amount.
93func (rs *RecurringSubscription) UpdateAmount(newAmount int64) error {
94 if !rs.OwnedByCurrent() {
95 return ErrNotAuthorized
96 }
97
98 rs.amount = newAmount
99 return nil
100}
101
102// GetAmount returns the current amount required for each subscription period.
103func (rs *RecurringSubscription) GetAmount() int64 {
104 return rs.amount
105}