package economy import ( "context" "encoding/json" "fmt" "net/http" "time" ) // AdVerifier تأیید می‌کند که کاربر یک تبلیغ rewarded را کامل دیده است. type AdVerifier interface { Verify(ctx context.Context, userID int64, token string) (bool, error) } // IAPVerifier تأیید می‌کند که یک خرید درون‌برنامه‌ای معتبر و پرداخت‌شده است. type IAPVerifier interface { Verify(ctx context.Context, store, productID, purchaseToken string) (bool, error) } // --- استاب توسعه: هر توکن غیرخالی را معتبر می‌داند (فقط برای محیط dev/تست) --- type DevAdVerifier struct{} func (DevAdVerifier) Verify(_ context.Context, _ int64, token string) (bool, error) { return token != "", nil } type DevIAPVerifier struct{} func (DevIAPVerifier) Verify(_ context.Context, _ string, _ string, token string) (bool, error) { return token != "", nil } // --- آداپتر تپسل (تأیید سمت‌سرور تبلیغ rewarded) --- // // تپسل پس از پایان کامل تبلیغ، یک توکن/شناسه می‌دهد که سرور باید آن را // نزد تپسل اعتبارسنجی کند. آدرس و قالب دقیق بسته به پنل تپسل تنظیم می‌شود؛ // اینجا اسکلت فراخوانی با کلید قرار داده شده است. type TapsellAdVerifier struct { APIKey string BaseURL string // پیش‌فرض اگر خالی باشد ست می‌شود client *http.Client } func NewTapsellAdVerifier(apiKey string) *TapsellAdVerifier { return &TapsellAdVerifier{ APIKey: apiKey, BaseURL: "https://api.tapsell.ir", client: &http.Client{Timeout: 5 * time.Second}, } } func (t *TapsellAdVerifier) Verify(ctx context.Context, userID int64, token string) (bool, error) { if t.APIKey == "" || token == "" { return false, fmt.Errorf("tapsell: missing api key or token") } // TODO: مسیر دقیق اعتبارسنجی تپسل را مطابق مستندات پنل تنظیم کنید. url := fmt.Sprintf("%s/rewarded/verify?token=%s", t.BaseURL, token) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return false, err } req.Header.Set("Authorization", t.APIKey) resp, err := t.client.Do(req) if err != nil { return false, err } defer resp.Body.Close() var out struct { Valid bool `json:"valid"` Completed bool `json:"completed"` } if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { return false, err } return out.Valid && out.Completed, nil } // --- آداپتر IAP کافه‌بازار / مایکت --- // // هر دو فروشگاه API شبیه Google Play دارند: با access_token (که از refresh_token // گرفته می‌شود) وضعیت یک purchaseToken برای یک محصول را اعتبارسنجی می‌کنیم. type StoreIAPVerifier struct { // قالب آدرس اعتبارسنجی؛ %s ها به ترتیب: package, product, token ValidateURLFmt string PackageName string AccessToken string // در عمل باید از refresh_token تازه‌سازی شود client *http.Client } // NewBazaarVerifier آداپتر کافه‌بازار. func NewBazaarVerifier(pkg, accessToken string) *StoreIAPVerifier { return &StoreIAPVerifier{ ValidateURLFmt: "https://pardakht.cafebazaar.ir/devapi/v2/api/validate/%s/inapp/%s/purchases/%s/", PackageName: pkg, AccessToken: accessToken, client: &http.Client{Timeout: 6 * time.Second}, } } // NewMyketVerifier آداپتر مایکت. func NewMyketVerifier(pkg, accessToken string) *StoreIAPVerifier { return &StoreIAPVerifier{ ValidateURLFmt: "https://developer.myket.ir/api/applications/%s/purchases/products/%s/tokens/%s/", PackageName: pkg, AccessToken: accessToken, client: &http.Client{Timeout: 6 * time.Second}, } } func (v *StoreIAPVerifier) Verify(ctx context.Context, _ string, productID, purchaseToken string) (bool, error) { if v.AccessToken == "" || purchaseToken == "" { return false, fmt.Errorf("iap: missing access token or purchase token") } url := fmt.Sprintf(v.ValidateURLFmt, v.PackageName, productID, purchaseToken) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return false, err } req.Header.Set("Authorization", "Bearer "+v.AccessToken) resp, err := v.client.Do(req) if err != nil { return false, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return false, nil } var out struct { // هر دو فروشگاه فیلدی شبیه purchaseState برمی‌گردانند (۰ = خریداری‌شده) PurchaseState int `json:"purchaseState"` ConsumptionState int `json:"consumptionState"` } if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { return false, err } return out.PurchaseState == 0, nil }