feat: add telescope

This commit is contained in:
2026-07-05 08:42:42 +03:30
parent c6997477b9
commit 37baef565e
9 changed files with 530 additions and 8 deletions
+33 -1
View File
@@ -95,16 +95,22 @@ func (t *TapsellAdVerifier) Verify(ctx context.Context, userID int64, token stri
return out.Valid && out.Completed, nil
}
// --- مایکت: تأیید سمت‌سرور با X-Access-Token ---
// --- مایکت: تأیید امضای RSA آفلاین (اولویت) یا سمت‌سرور با X-Access-Token ---
//
// افزونه‌ی myket_iap مثلِ کافه‌بازار، purchaseData (JSONِ خام) و signature (base64)
// برمی‌گرداند که با کلیدِ عمومیِ RSA و SHA1withRSA به‌صورتِ آفلاین تأیید می‌شوند —
// بدونِ رفت‌وبرگشتِ شبکه به‌ازای هر خرید. اگر کلیدِ عمومی تنظیم نشده باشد یا امضا
// ارسال نشده باشد، به تأییدِ سمت‌سرور برمی‌گردیم:
// GET https://developer.myket.ir/api/applications/{pkg}/purchases/products/{sku}/tokens/{token}
// هدر: X-Access-Token: <accessToken> . پاسخ purchaseState=0 یعنی معتبر (غیرِ صفر=مسترد).
type MyketVerifier struct {
PackageName string
AccessToken string
pub *rsa.PublicKey // کلیدِ عمومیِ RSA برای تأییدِ آفلاین (اختیاری)
client *http.Client
}
// NewMyketVerifier یک تأییدکننده با access token (سمت‌سرور) می‌سازد.
func NewMyketVerifier(pkg, accessToken string) *MyketVerifier {
return &MyketVerifier{
PackageName: pkg,
@@ -113,7 +119,33 @@ func NewMyketVerifier(pkg, accessToken string) *MyketVerifier {
}
}
// WithRSAKey کلیدِ عمومیِ RSA را برای تأییدِ آفلاین ضمیمه می‌کند (اگر خالی نباشد).
func (v *MyketVerifier) WithRSAKey(publicKey string) (*MyketVerifier, error) {
if strings.TrimSpace(publicKey) == "" {
return v, nil
}
pub, err := parseRSAPublicKey(publicKey)
if err != nil {
return nil, fmt.Errorf("myket rsa: %w", err)
}
v.pub = pub
return v, nil
}
func (v *MyketVerifier) Verify(ctx context.Context, p IAPProof) (bool, error) {
// مسیرِ ترجیحی: تأییدِ امضای آفلاین (اگر کلید و امضا موجود باشند).
if v.pub != nil && p.PurchaseData != "" && p.Signature != "" {
sig, err := base64.StdEncoding.DecodeString(p.Signature)
if err != nil {
return false, fmt.Errorf("myket: bad signature encoding: %w", err)
}
h := sha1.Sum([]byte(p.PurchaseData))
if err := rsa.VerifyPKCS1v15(v.pub, crypto.SHA1, h[:], sig); err != nil {
return false, nil // امضای نامعتبر ⇒ خریدِ نامعتبر
}
return true, nil
}
// مسیرِ جایگزین: تأییدِ سمت‌سرور با access token.
if v.AccessToken == "" || p.Token == "" {
return false, fmt.Errorf("myket: missing access token or purchase token")
}