83 lines
2.7 KiB
Go
83 lines
2.7 KiB
Go
package payment
|
|
|
|
import (
|
|
"time"
|
|
|
|
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
|
|
)
|
|
|
|
type ScheduleStatus int
|
|
|
|
const (
|
|
SCHEDULE_ACTIVE ScheduleStatus = iota
|
|
SCHEDULE_PAUSED // mis en pause manuellement
|
|
SCHEDULE_CANCELLED // résilié
|
|
SCHEDULE_COMPLETED // terminé normalement (abonnement expiré)
|
|
SCHEDULE_FAILED // trop d'échecs consécutifs
|
|
)
|
|
|
|
func (s ScheduleStatus) String() string {
|
|
return [...]string{"active", "paused", "cancelled", "completed", "failed"}[s]
|
|
}
|
|
|
|
// PaymentSchedule pilote la récurrence des paiements d'un abonnement.
|
|
type PaymentSchedule struct {
|
|
SubscriptionID string `json:"subscription_id" bson:"subscription_id"`
|
|
Frequency pricing.PaymentType `json:"frequency" bson:"frequency"` // PAY_EVERY_WEEK / PAY_EVERY_MONTH / PAY_EVERY_YEAR
|
|
Amount float64 `json:"amount" bson:"amount"`
|
|
Currency string `json:"currency" bson:"currency"`
|
|
Status ScheduleStatus `json:"status" bson:"status"`
|
|
NextPaymentDate time.Time `json:"next_payment_date" bson:"next_payment_date"`
|
|
LastExecutedAt *time.Time `json:"last_executed_at,omitempty" bson:"last_executed_at,omitempty"`
|
|
FailureCount int `json:"failure_count" bson:"failure_count"`
|
|
MaxRetries int `json:"max_retries" bson:"max_retries" default:"3"`
|
|
}
|
|
|
|
// nextDate calcule la prochaine date selon la fréquence.
|
|
func (ps *PaymentSchedule) nextDate() time.Time {
|
|
switch ps.Frequency {
|
|
case pricing.PAY_EVERY_WEEK:
|
|
return ps.NextPaymentDate.AddDate(0, 0, 7)
|
|
case pricing.PAY_EVERY_MONTH:
|
|
return ps.NextPaymentDate.AddDate(0, 1, 0)
|
|
case pricing.PAY_EVERY_YEAR:
|
|
return ps.NextPaymentDate.AddDate(1, 0, 0)
|
|
}
|
|
return ps.NextPaymentDate
|
|
}
|
|
|
|
// Advance enregistre l'exécution réussie et avance à la prochaine échéance.
|
|
func (ps *PaymentSchedule) Advance() {
|
|
now := time.Now().UTC()
|
|
ps.LastExecutedAt = &now
|
|
ps.FailureCount = 0
|
|
ps.NextPaymentDate = ps.nextDate()
|
|
}
|
|
|
|
// RecordFailure incrémente le compteur d'échecs et désactive après MaxRetries.
|
|
func (ps *PaymentSchedule) RecordFailure() {
|
|
ps.FailureCount++
|
|
if ps.MaxRetries > 0 && ps.FailureCount >= ps.MaxRetries {
|
|
ps.Status = SCHEDULE_FAILED
|
|
}
|
|
}
|
|
|
|
// IsDue retourne vrai si le paiement est dû maintenant.
|
|
func (ps *PaymentSchedule) IsDue() bool {
|
|
return ps.Status == SCHEDULE_ACTIVE && !time.Now().UTC().Before(ps.NextPaymentDate)
|
|
}
|
|
|
|
// Pause suspend temporairement le calendrier.
|
|
func (ps *PaymentSchedule) Pause() {
|
|
if ps.Status == SCHEDULE_ACTIVE {
|
|
ps.Status = SCHEDULE_PAUSED
|
|
}
|
|
}
|
|
|
|
// Resume réactive un calendrier mis en pause.
|
|
func (ps *PaymentSchedule) Resume() {
|
|
if ps.Status == SCHEDULE_PAUSED {
|
|
ps.Status = SCHEDULE_ACTIVE
|
|
}
|
|
}
|