package apple import ( "context" "crypto/sha256" "encoding/hex" "fmt" "github.com/perfect-panel/server/pkg/cache" "github.com/redis/go-redis/v9" "gorm.io/gorm" ) type Model interface { Insert(ctx context.Context, data *Transaction, tx ...*gorm.DB) error FindByOriginalId(ctx context.Context, originalId string) (*Transaction, error) FindByUserAndProduct(ctx context.Context, userId int64, productId string) (*Transaction, error) } type defaultModel struct { cache.CachedConn table string } type customModel struct { *defaultModel } func NewModel(db *gorm.DB, c *redis.Client) Model { return &customModel{ defaultModel: &defaultModel{ CachedConn: cache.NewConn(db, c), table: "`apple_iap_transactions`", }, } } func (m *defaultModel) jwsKey(jws string) string { sum := sha256.Sum256([]byte(jws)) return fmt.Sprintf("cache:iap:jws:%s", hex.EncodeToString(sum[:])) } func (m *customModel) Insert(ctx context.Context, data *Transaction, tx ...*gorm.DB) error { return m.ExecCtx(ctx, func(conn *gorm.DB) error { if len(tx) > 0 { conn = tx[0] } return conn.Model(&Transaction{}).Create(data).Error }, m.jwsKey(data.JWSHash)) } func (m *customModel) FindByOriginalId(ctx context.Context, originalId string) (*Transaction, error) { var data Transaction key := fmt.Sprintf("cache:iap:original:%s", originalId) err := m.QueryCtx(ctx, &data, key, func(conn *gorm.DB, v interface{}) error { return conn.Model(&Transaction{}).Where("original_transaction_id = ?", originalId).First(&data).Error }) return &data, err } func (m *customModel) FindByUserAndProduct(ctx context.Context, userId int64, productId string) (*Transaction, error) { var data Transaction err := m.QueryNoCacheCtx(ctx, &data, func(conn *gorm.DB, v interface{}) error { return conn.Model(&Transaction{}).Where("user_id = ? AND product_id = ?", userId, productId).Order("purchase_at DESC").First(&data).Error }) return &data, err }