Mobil SDK: uygulamanızda Adfacten reklamı gösterin
Adfacten mobil SDK'sı Android ve iOS uygulamalarında reklam gösterir. Reklamı ister, ekrana çizer, gösterim ve tıklamayı ölçer, ödüllü reklamda ödülü sunucunuza bildirtir. Bu sayfa yayıncılar (uygulama geliştiricileri) içindir. Uygulama kurulumlarını ölçmek isteyen reklamverenler MMP entegrasyonu sayfasına bakmalıdır.
English version: Mobile SDK.
Kaynak kod ve örnekler: Android (
github.com/adfacten/adfacten-android-sdk), iOS (github.com/adfacten/adfacten-ios-sdk), Flutter (github.com/adfacten/adfacten_ads_flutter), React Native (github.com/adfacten/adfacten-react-native-ads) ve Unity (github.com/adfacten/adfacten-unity-ads). Panelde her yerleşim için hazır kod örneği de gösterilir.
Bu sayfada:
- Genel bakış: biçimler ve ücretlendirme
- Panelde başlangıç: uygulama, app-ads.txt, onay, yayına alma
- Android kurulumu
- iOS kurulumu
- Flutter, React Native ve Unity
- Ödüllü reklamda sunucu tarafı doğrulama
- Gizlilik ve onay (TCF, GPP, COPPA, KVKK)
- Doğrudan REST kullanımı (özel entegrasyon / mediation)
- Sorun giderme
1. Genel bakış
| Biçim | Ne zaman kullanılır |
|---|---|
| Banner | Ekranın bir köşesinde sürekli duran reklam. Boyutlar: 320x50, 320x100, 300x250, 728x90 (yalnız tablet). Görünür olduğu sürece 30–120 saniyede bir yenilenir (yerleşim ayarı; 0 = yenileme yok), ekrandan çıkınca durur. |
| Interstitial (geçiş) | Tam ekran; seviye sonu, ekran geçişi gibi doğal aralarda. |
| Rewarded (ödüllü) | Kullanıcı reklamı sonuna kadar izler, karşılığında ödül alır (ör. 10 jeton). Ödül türü ve miktarı yerleşimde tanımlanır. |
| Native | Reklam parçaları (başlık, metin, görsel, ikon, buton) sizin tasarımınıza yerleşir. |
| App open (açılış) | Uygulama açılırken veya arka plandan dönerken gösterilen tam ekran reklam. |
Ücretlendirme nasıl sayılır
- Gösterim (CPM): Reklam gerçekten ekrana çizildiğinde (
render) sayılır. Yüklenip gösterilmeyen reklam sayılmaz. - Görünür gösterim (vCPM): Reklamın en az %50'si en az 1 saniye ekranda kaldığında (video için 2 saniye) sayılır.
- Tıklama (CPC): Kullanıcı reklama dokunduğunda sayılır.
- Ödüllü / video tamamlama: Video sonuna kadar izlendiğinde (
complete) sayılır; ödül de buna bağlıdır. - Test reklamları hiçbir zaman ücretlendirilmez.
SDK her olayı bir kez gönderir. İnternet yoksa olayları sıraya alır ve 24 saate kadar tekrar dener.
2. Panelde başlangıç
2.1 Uygulamayı ekleyin
Panel → Uygulamalar → Uygulama ekle.
- Platformu seçin (Android veya iOS) ve mağaza adresini girin:
- Google Play:
https://play.google.com/store/apps/details?id=com.firma.uygulama - App Store:
https://apps.apple.com/tr/app/…/id123456789
- Google Play:
- Uygulama henüz mağazada değilse paket adını (bundle id) girin, ör.
com.firma.uygulama. Entegrasyona hemen başlayabilirsiniz; onay için uygulamanın mağazada yayında olması gerekir. - Mağaza bilgileri (ad, ikon, geliştirici, geliştirici web sitesi) otomatik doldurulur.
- Her uygulamaya bir uygulama anahtarı (
apk_…) verilir. Bu anahtar gizli değildir; uygulamanın içine yazılır ve uygulamayı tanıtır. - Aynı uygulama (platform + paket adı) yalnızca bir kez kaydedilebilir. Uygulama sizinse ve başka bir hesapta görünüyorsa destek ekibine yazın.
Sonra uygulamaya yerleşim ekleyin (her reklam alanı için bir tane). Her yerleşimin bir kimliği vardır
(plc_…) ve tek bir biçimi olur. Banner yerleşiminde boyut(lar)ı ve yenileme süresini, ödüllü yerleşimde
ödül türünü ve miktarını seçersiniz. İsterseniz cihaz başına günlük gösterim sınırı da koyabilirsiniz.
2.2 Çocuklara yönelik uygulamalar desteklenmez
Adfacten reklamları çocuklara yönelik uygulamalarda gösterilmez (ABD COPPA ve Google Play Aileler
politikası). Uygulama çocuklara yönelik işaretlenirse eklenmez; sonradan işaretlenirse tüm reklam
istekleri reddedilir (coppa). Bu işareti yalnızca ağ ekibi kaldırabilir.
2.3 Test modu ve test reklamları
Yeni uygulamalar test modunda başlar. Test reklamları gerçek reklamlar gibi davranır (gösterim,
görünürlük ve tıklama olayları çalışır, tıklama https://adfacten.com/ adresine gider) ama
ücretlendirilmez ve üzerinde "Test reklamı" yazar.
Sunucu şu durumların herhangi birinde test reklamı döner:
| Durum | Nasıl düzelir |
|---|---|
| Uygulama onay bekliyor | Ağ ekibinin onayı (2.5) |
| app-ads.txt satırı bulunamadı | Satırı ekleyin (2.4) |
| Uygulamanın test modu açık | Panelde test modunu kapatın (2.6) |
SDK'da testMode açık (istekte test=true) |
Yayın sürümünde testMode kapalı olmalı |
| Cihaz emülatör / simülatör | Gerçek cihazda deneyin |
Test reklamlarının içeriği sabittir: banner istenen boyutta görsel, interstitial HTML, ödüllü reklam 5 saniyelik video (ödüllü), native hazır parçalar, açılış reklamı 320x480 görsel.
2.4 app-ads.txt satırı
app-ads.txt, uygulamanızın gerçekten size ait olduğunu ve Adfacten'in envanterinizi satmaya yetkili olduğunu gösterir (IAB app-ads.txt standardı). Gerçek reklamlar bu satır doğrulanınca gösterilir.
Nereye: Uygulamanın mağaza sayfasında yazan geliştirici web sitesinin kök dizinine:
https://<geliştirici alan adı>/app-ads.txt. Alan adının başındaki www., m. ve mobile. yok sayılır
(https://www.firma.com → https://firma.com/app-ads.txt). Önce HTTPS, sonra HTTP denenir.
Satır (panelde uygulamanızın sayfasında hazır gösterilir, kopyalayın):
adfacten.com, <yayıncı kimliğiniz>, DIRECT
<yayıncı kimliğiniz>sizin satıcı kimliğinizdir; sellers.json listesindekiseller_idile aynıdır ve hiç değişmez.- Dosya düz metin olmalı (
text/plain). HTML sayfası (ör. "bulunamadı" sayfası veya ana sayfa) dosya sayılmaz. - Dosyada başka ağların satırları, yorumlar (
#) vecontact=gibi değişkenler olabilir; sorun değil.
Doğrulama: Uygulamayı eklediğinizde dosya hemen kontrol edilir, sonra yaklaşık 6 saatte bir yeniden kontrol edilir. Panelde Tekrar kontrol et ile dakikada bir kez elle kontrol ettirebilirsiniz.
| Durum | Anlamı |
|---|---|
found |
Satır bulundu. Gerçek reklam gösterilebilir (onay ve test modu kapalıysa). |
wrong_id |
adfacten.com satırı var ama yayıncı kimliği sizin değil (başka yerden kopyalanmış). |
no_line |
Dosya var ama adfacten.com satırı yok. |
missing |
Sitede app-ads.txt yok (404 veya dosya yerine HTML sayfa dönüyor). |
no_website |
Mağaza sayfasında geliştirici web sitesi yazmıyor. Mağaza konsolundan ekleyin. |
unreachable |
Site yanıt vermedi. Daha sonra yeniden denenir; son iyi sonuç korunur. |
unknown |
Henüz kontrol edilmedi (ör. uygulama mağazada bulunamadı). |
Mağaza sayfasındaki geliştirici web sitesini değiştirdiyseniz, mağazanın değişikliği yayınlaması zaman alabilir. Sonra Tekrar kontrol et'e basın.
2.5 Ağ onayı
Uygulamanız Adfacten ağ ekibinin onay kuyruğuna düşer. Onay için uygulamanın mağazada yayında olması
gerekir. Onaylanınca veya reddedilince bildirim alırsınız; ret gerekçesi uygulamanın sayfasında görünür.
Reddedilen uygulamalara reklam gösterilmez (app_not_approved).
2.6 Yayına alma
Gerçek reklamlar ancak şu dördü birlikte sağlanınca gösterilir:
- Uygulama onaylı,
- app-ads.txt durumu
found, - panelde uygulamanın test modu kapalı,
- uygulamanızın yayın sürümünde SDK'nın
testModeayarı kapalı.
Panelde uygulamanın sayfasındaki Yayın durumu neyin eksik olduğunu gösterir (live, test veya
blocked ve nedeni).
2.7 sellers.json
Adfacten'in yetkili satıcı listesi: https://adfacten.com/sellers.json
(IAB sellers.json). En az bir onaylı uygulaması olan yayıncılar listelenir. Şirketler adı ve alan adıyla
görünür; şahıslar KVKK gereği gizli (is_confidential) listelenir, adınız yayımlanmaz.
3. Android
Gereksinim: Android 5.0 (API 21) ve üstü. Paket: com.adfacten.ads. Kaynak kod ve örnek uygulama:
https://github.com/adfacten/adfacten-android-sdk (Apache 2.0).
3.1 Kurulum
settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://cdn.adfacten.com/maven/") {
content { includeGroup("com.adfacten") }
}
}
}
Uygulama modülünün build.gradle.kts dosyası:
dependencies {
implementation("com.adfacten:ads:1.0.1")
// İsteğe bağlı: reklam kimliği (GAID) ve App Set ID
implementation("com.adfacten:ads-gaid:1.0.1")
// İsteğe bağlı: video reklamlar (Media3 ExoPlayer). Ödüllü reklam kullanıyorsanız önerilir.
implementation("com.adfacten:ads-video:1.0.1")
}
- Ana paket (
ads) Google Play Hizmetleri'ne bağlı değildir; yalnızcaINTERNETveACCESS_NETWORK_STATEizinlerini ekler. ads-gaideklenirse reklam kimliği okunur vecom.google.android.gms.permission.AD_IDizni eklenir. Eklenmezse SDK kendi kurulum kimliğini kullanır; reklamlar yine gösterilir, ama sıklık sınırı ve kurulum ölçümü daha az isabetli olur.ads-videoeklenmezse interstitial ve ödüllü yerleşimlere yalnızca görsel/HTML reklam istenir.
3.2 Başlatma
Application.onCreate içinde bir kez çağırın. Ana iş parçacığını yormaz.
class App : Application() {
override fun onCreate() {
super.onCreate()
Adfacten.initialize(
this,
"apk_XXXXXXXXXXXXXXXXXXXXXXXX",
AdfactenConfig.Builder()
.setTestMode(BuildConfig.DEBUG) // yayın sürümünde false
.setLogLevel(LogLevel.WARN)
.setChildDirected(false)
.build()
)
}
}
Kullanıcı giriş yapınca (ödüllü reklam için) kullanıcı kimliğinizi verin:
Adfacten.setUserId("kullanici-123") // sunucu ödül bildiriminde user_id olarak gelir
3.3 Onay (consent)
SDK, uygulamanızdaki IAB uyumlu onay aracının (CMP) yazdığı değerleri kendisi okur
(IABTCF_gdprApplies, IABTCF_TCString, IABTCF_PurposeConsents, IABGPP_HDR_GppString,
IABUSPrivacy_String). Ayrıca bir şey yapmanız gerekmez; onay aracını reklam yüklemeden önce
çalıştırın. Ayrıntılar: bölüm 6.
3.4 Banner
XML ile:
<com.adfacten.ads.AdfactenBannerView
android:id="@+id/banner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:adf_placementId="plc_XXXXXXXX"
app:adf_adSize="banner" />
adf_adSize: banner (320x50), large_banner (320x100), medium_rectangle (300x250), leaderboard
(728x90, tablet).
Kod ile:
val banner = AdfactenBannerView(context).apply {
placementId = "plc_XXXXXXXX"
adSize = AdSize.BANNER
listener = object : BannerAdListener {
override fun onLoaded() {}
override fun onFailed(error: AdError) { Log.w("ads", error.toString()) }
}
}
container.addView(banner)
banner.load()
// Activity/Fragment yaşam döngüsü:
override fun onPause() { banner.pause(); super.onPause() }
override fun onResume() { super.onResume(); banner.resume() }
override fun onDestroy() { banner.destroy(); super.onDestroy() }
Banner görünür olduğu sürece yerleşimdeki süreye göre kendini yeniler.
3.5 Interstitial
private var interstitial: InterstitialAd? = null
fun loadInterstitial() {
InterstitialAd.load(context, "plc_XXXXXXXX", object : AdLoadCallback<InterstitialAd> {
override fun onLoaded(ad: InterstitialAd) { interstitial = ad }
override fun onFailed(error: AdError) { interstitial = null }
})
}
fun showInterstitial(activity: Activity) {
val ad = interstitial ?: return
if (ad.isExpired) { loadInterstitial(); return }
ad.listener = object : FullScreenAdListener {
override fun onClosed() { loadInterstitial() } // bir sonrakini hazırlayın
}
ad.show(activity)
interstitial = null // bir reklam yalnızca bir kez gösterilir
}
3.6 Rewarded (ödüllü)
RewardedAd.load(context, "plc_XXXXXXXX", object : AdLoadCallback<RewardedAd> {
override fun onLoaded(ad: RewardedAd) { rewarded = ad }
override fun onFailed(error: AdError) { rewarded = null }
}, customData = "level=12") // isteğe bağlı, ≤ 256 karakter, sunucu bildiriminde 'custom' olarak gelir
rewarded?.show(activity) { reward ->
// Yalnızca arayüz için (ör. "Ödülünüz yolda" mesajı).
// Ödülü SUNUCUNUZA gelen bildirime göre verin (bölüm 6).
}
3.7 Native
Kendi tasarımınızı NativeAdView içine koyun ve hangi görünümün hangi parçayı göstereceğini belirtin:
<com.adfacten.ads.NativeAdView
android:id="@+id/native_ad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:adf_titleView="@id/ad_title"
app:adf_bodyView="@id/ad_body"
app:adf_iconView="@id/ad_icon"
app:adf_imageView="@id/ad_image"
app:adf_callToActionView="@id/ad_cta"
app:adf_adLabelView="@id/ad_label">
<!-- kendi TextView / ImageView / Button görünümleriniz -->
</com.adfacten.ads.NativeAdView>
NativeAd.load(context, "plc_XXXXXXXX", object : AdLoadCallback<NativeAd> {
override fun onLoaded(ad: NativeAd) { nativeAdView.setNativeAd(ad) }
override fun onFailed(error: AdError) { nativeAdView.visibility = View.GONE }
})
NativeAdView gösterimi, görünürlüğü ve tıklamayı kendisi ölçer. "Reklam" etiketini her zaman görünür
bırakın. Görünüm kaldırılınca ad.destroy() çağırın.
3.8 App open (açılış)
AppOpenAd.load(context, "plc_XXXXXXXX", object : AdLoadCallback<AppOpenAd> {
override fun onLoaded(ad: AppOpenAd) { appOpen = ad }
override fun onFailed(error: AdError) {}
})
// Uygulama ön plana gelince:
appOpen?.takeIf { !it.isExpired }?.show(activity)
Açılış reklamını ilk açılışta açılış ekranı (splash) bittikten sonra veya uygulama arka plandan döndüğünde gösterin; kullanıcının işini bölmeyin.
3.9 Google Play "Veri güvenliği" formu
SDK'nın topladığı ve Adfacten'e gönderdiği veriler (formu doldururken kendi uygulamanızın verileriyle birleştirin):
| Veri | Ne zaman | Amaç |
|---|---|---|
| Cihaz veya diğer kimlikler: SDK kurulum kimliği | Her zaman | Reklam, dolandırıcılık önleme |
| Cihaz veya diğer kimlikler: reklam kimliği (GAID), App Set ID | Yalnız ads-gaid eklenirse ve izin varsa |
Reklam, analiz |
| Uygulama etkileşimleri (reklam gösterimi, tıklama) | Her zaman | Reklam, analiz |
Kullanıcı kimliği (userId) |
Yalnız siz verirseniz (ödüllü reklam) | Uygulama işlevi (ödül) |
| IP adresinden türetilen yaklaşık konum (ülke) | Sunucuda | Reklam hedefleme, dolandırıcılık önleme |
Veriler aktarım sırasında şifrelenir (HTTPS) ve üçüncü tarafla (Adfacten) paylaşılır.
4. iOS
Gereksinim: iOS 15 ve üstü. Modül: AdfactenAds. Üçüncü taraf bağımlılığı yoktur; video için AVPlayer
kullanılır.
4.1 Kurulum
Kaynak kod: https://github.com/adfacten/adfacten-ios-sdk (Apache 2.0). Üç yoldan birini seçin:
Swift Package Manager: Xcode → File → Add Package Dependencies… → https://github.com/adfacten/adfacten-ios-sdk
adresini girin ve AdfactenAds ürününü ekleyin.
// Package.swift kullanan projeler:
.package(url: "https://github.com/adfacten/adfacten-ios-sdk.git", from: "1.0.0")
CocoaPods:
pod 'AdfactenAds', :git => 'https://github.com/adfacten/adfacten-ios-sdk.git', :tag => '1.0.0'
Elle (XCFramework): https://cdn.adfacten.com/ios/AdfactenAds-1.0.0.xcframework.zip dosyasını indirin,
açın ve Xcode'da hedefinize Embed & Sign olarak ekleyin. SHA-256: 6ab9883dc8d92597f2f0c7391c4b2434c15950548e7e465e16367539290b883f
Hemen denemek için depodaki örnek uygulamayı çalıştırın: Adfacten'in herkese açık test uygulamasının anahtarlarıyla gelir ve ayar gerektirmeden (ücretlendirilmeyen) test reklamları gösterir.
4.2 Başlatma
Uygulama açılışında, ana iş parçacığında bir kez çağırın:
import AdfactenAds
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let config = AdfactenConfig()
#if DEBUG
config.testMode = true // yayın sürümünde false
#endif
config.childDirected = false
Adfacten.initialize(appKey: "apk_XXXXXXXXXXXXXXXXXXXXXXXX", config: config)
return true
}
// Kullanıcı giriş yapınca (ödüllü reklam için):
Adfacten.setUserId("kullanici-123")
4.3 App Tracking Transparency (ATT) ve gizlilik bildirimi
- SDK ATT iznini kendisi istemez; yalnızca mevcut durumu okur. İzin istemek isterseniz
ATTrackingManager.requestTrackingAuthorizationçağrısını veNSUserTrackingUsageDescriptionmetnini siz eklersiniz. - İzin yoksa IDFA kullanılmaz; SDK IDFV'yi veya kendi kurulum kimliğini kullanır. Reklamlar yine gösterilir.
- SDK kendi
PrivacyInfo.xcprivacydosyasıyla gelir (toplanan veri türleri: cihaz kimliği, reklam verisi, ürün etkileşimi, kullanıcı kimliği; erişilen API'ler: UserDefaults, sistem açılış süresi). Xcode bunu uygulamanızın gizlilik raporuna otomatik ekler. App Store Connect'teki "App Privacy" yanıtlarınızı buna göre güncelleyin.
4.4 Banner
let banner = AdfactenBannerView(placementId: "plc_XXXXXXXX", size: .banner)
banner.rootViewController = self
banner.delegate = self // AdfactenBannerViewDelegate (isteğe bağlı)
view.addSubview(banner) // boyutu kendiliğinden bilir (intrinsicContentSize)
banner.load()
Boyutlar: .banner (320x50), .largeBanner (320x100), .mediumRectangle (300x250), .leaderboard
(728x90, tablet). SwiftUI için:
AdfactenBanner(placementId: "plc_XXXXXXXX", size: .banner)
4.5 Interstitial
var interstitial: InterstitialAd?
func loadInterstitial() {
InterstitialAd.load(placementId: "plc_XXXXXXXX") { [weak self] ad, error in
self?.interstitial = ad
}
}
func showInterstitial() {
guard let ad = interstitial, ad.isReady else { loadInterstitial(); return }
ad.onDismissed = { [weak self] in self?.loadInterstitial() }
ad.present(from: self)
interstitial = nil
}
async sürümü de vardır: let ad = try await InterstitialAd.load(placementId: "plc_…").
4.6 Rewarded (ödüllü)
RewardedAd.load(placementId: "plc_XXXXXXXX", customData: "level=12") { [weak self] ad, error in
self?.rewarded = ad
}
rewarded?.present(from: self) { reward in
// Yalnızca arayüz için. Ödülü sunucunuza gelen bildirime göre verin (bölüm 6).
}
4.7 Native
NativeAdView alt sınıfınızda (kodda veya Interface Builder'da) şu bağlantıları kurun:
titleLabel, bodyLabel, brandLabel, callToActionButton, mediaView, iconView, adLabel.
NativeAd.load(placementId: "plc_XXXXXXXX") { [weak self] ad, error in
self?.nativeAdView.nativeAd = ad // bağlar, gösterimi ve tıklamayı ölçer
}
Hücre yeniden kullanılırken nativeAdView.nativeAd = nil yapın. adLabel bağlamazsanız SDK kendi
"Reklam" etiketini ekler.
4.8 App open (açılış)
AppOpenAd.load(placementId: "plc_XXXXXXXX") { [weak self] ad, error in
self?.appOpen = ad
}
// Uygulama ön plana gelince:
if let ad = appOpen, ad.isReady { ad.present() }
5. Flutter, React Native ve Unity
Android ve iOS kütüphanelerinin üstüne yazılmış resmi paketler; reklam türleri, test modu ve ödül bildirimi yukarıdaki bölümlerle aynıdır.
5.1 Flutter
Kaynak kod ve örnek uygulama: https://github.com/adfacten/adfacten_ads_flutter (Apache 2.0).
pubspec.yaml:
dependencies:
adfacten_ads:
git:
url: https://github.com/adfacten/adfacten_ads_flutter
ref: 1.0.1
await Adfacten.initialize('apk_XXXXXXXXXXXXXXXXXXXXXXXX');
AdfactenBanner(placementId: 'plc_XXXXXXXX', size: AdSize.banner);
final ad = await RewardedAd.load('plc_XXXXXXXX');
await ad.show(onUserEarnedReward: (reward) => grant(reward));
Native reklamlar kütüphanenin hazır şablonlarıyla (küçük satır, orta kart) gösterilir; gösterim ve tıklama yalnız böyle doğru sayılır. Ayrıntılar paketin README dosyasında.
5.2 React Native ve Expo
Kaynak kod ve örnek uygulama: https://github.com/adfacten/adfacten-react-native-ads (Apache 2.0).
React Native 0.80 ve üstü (New Architecture).
npm install github:adfacten/adfacten-react-native-ads#1.0.1
cd ios && pod install
import { Adfacten, AdfactenBanner, RewardedAd } from '@adfacten/react-native-ads';
Adfacten.initialize('apk_XXXXXXXXXXXXXXXXXXXXXXXX');
<AdfactenBanner placementId="plc_XXXXXXXX" size="banner" />;
Expo: app.json içinde "plugins": ["@adfacten/react-native-ads"] ekleyip npx expo prebuild veya
geliştirme derlemesi (dev client) kullanın. Expo Go yerel modül yükleyemediği için desteklenmez.
Her iki paketin örnek uygulaması Adfacten'in herkese açık test uygulamasının anahtarlarıyla gelir ve ayar gerektirmeden (ücretlendirilmeyen) test reklamları gösterir.
5.3 Unity
Kaynak kod ve örnek sahne: https://github.com/adfacten/adfacten-unity-ads (Apache 2.0). Unity 2021.3 LTS ve
üstü. Window › Package Manager › + › Add package from git URL ile şu adresi ekleyin:
https://github.com/adfacten/adfacten-unity-ads.git#1.0.0
Android ve iOS bağımlılıkları External Dependency Manager (EDM4U) ile kurulur; uygulama anahtarlarını Project Settings › Adfacten ekranına yazın.
Adfacten.Initialize("apk_XXXXXXXXXXXXXXXXXXXXXXXX");
var banner = new BannerView("plc_XXXXXXXX", AdSize.Banner, AdPosition.Bottom);
banner.Load();
RewardedAd.Load("plc_XXXXXXXX", (ad, error) => {
if (ad != null) ad.Show(reward => { /* yalnız arayüz; ödülü sunucu bildirimiyle verin */ });
});
Editörde gerçek reklam yerine yer tutucu reklamlar görünür; böylece oyunu telefon olmadan geliştirebilirsiniz.
6. Ödüllü reklamda sunucu tarafı doğrulama
Ödülü yalnızca sunucunuza gelen Adfacten bildirimine göre verin. Uygulamadaki
onUserEarnedReward geri çağrısı yalnızca arayüz içindir; değiştirilmiş bir uygulama onu sahte
çağırabilir.
6.1 Kurulum
Panel → Uygulamalar → uygulamanız → Ödül bildirimi:
- Bildirim adresi: sunucunuzdaki adres, ör.
https://api.oyununuz.com/adfacten/reward. Canlıdahttpsolmalı. Adreste kendi parametreleriniz olabilir (ör.?env=prod), amasigparametresi olamaz. Özel ağ (private IP) adreslerine bildirim gönderilmez. - Gizli anahtar (
rws_…): ilk kez adres girdiğinizde oluşur. Panelde yalnızca bir kez gösterilir; sunucunuzda gizli bir ortam değişkeninde saklayın. Uygulamanın içine asla koymayın.
6.2 Bildirim
Kullanıcı ödüllü reklamı geçerli biçimde tamamlayınca Adfacten sunucunuza bir GET isteği gönderir:
GET https://api.oyununuz.com/adfacten/reward?env=prod&user_id=kullanici-123&custom=level%3D12&reward_type=coins&reward_amount=10&transaction_id=imp_…&placement=plc_XXXXXXXX&ts=1794160000&sig=5f1c…e9
| Parametre | Değer |
|---|---|
| (sizin parametreleriniz) | Bildirim adresinde yazdıklarınız, en başta |
user_id |
Adfacten.setUserId / userId ile verdiğiniz kimlik (en çok 128 karakter; yoksa boş) |
custom |
Reklamı yüklerken verdiğiniz customData (en çok 256 karakter; yoksa boş) |
reward_type |
Yerleşimdeki ödül türü, ör. coins |
reward_amount |
Yerleşimdeki ödül miktarı (tam sayı) |
transaction_id |
Bu gösterimin tekil kimliği. Her ödül için bir tane. |
placement |
Yerleşim kimliği (plc_…) |
ts |
Ödülün oluştuğu an (Unix saniye). Tekrar denemelerde değişmez. |
test |
Yalnızca test reklamında: 1 |
sig |
İmza, her zaman en sonda |
Parametreler bu sırayla gelir ve değerler URL kodlamalıdır (ör. boşluk %20).
6.3 İmza nasıl doğrulanır
sig = hex( HMAC-SHA256( gizli_anahtar, ham_sorgu ) )
ham_sorgu, isteğin olduğu gibi gelen sorgu metnidir: ? işaretinden sonraki her şey, sondaki
&sig=… kısmı hariç. Kendi parametreleriniz (varsa) ve test=1 (varsa) imzaya dahildir.
- Sorguyu çözmeden ve yeniden sıralamadan kullanın. Çatınızın (framework) çözülmüş parametrelerinden metni yeniden kurmayın; ham metni alın.
- Karşılaştırmayı sabit zamanlı yapın.
- İmza geçerliyse parametreleri çözüp kullanın.
Node.js (Express)
import crypto from "node:crypto";
const SECRETS = [process.env.ADFACTEN_REWARD_SECRET, process.env.ADFACTEN_REWARD_SECRET_OLD].filter(Boolean);
function verifyAdfacten(rawQuery) {
const i = rawQuery.lastIndexOf("&sig=");
if (i < 0) return false;
const signed = rawQuery.slice(0, i);
const sig = rawQuery.slice(i + 5);
if (!/^[0-9a-f]{64}$/.test(sig)) return false;
const given = Buffer.from(sig, "hex");
return SECRETS.some((s) =>
crypto.timingSafeEqual(crypto.createHmac("sha256", s).update(signed, "utf8").digest(), given));
}
app.get("/adfacten/reward", async (req, res) => {
const url = req.originalUrl;
const raw = url.includes("?") ? url.slice(url.indexOf("?") + 1) : "";
if (!verifyAdfacten(raw)) return res.sendStatus(403);
const q = new URLSearchParams(raw);
await grantOnce(q.get("transaction_id"), q.get("user_id"), q.get("reward_type"),
Number(q.get("reward_amount")), q.get("test") === "1");
res.sendStatus(200);
});
Python (FastAPI / Starlette)
import hashlib, hmac, os, re
from urllib.parse import parse_qs
from fastapi import FastAPI, Request, Response
SECRETS = [s for s in (os.environ.get("ADFACTEN_REWARD_SECRET"), os.environ.get("ADFACTEN_REWARD_SECRET_OLD")) if s]
def verify_adfacten(raw_query: str) -> bool:
i = raw_query.rfind("&sig=")
if i < 0:
return False
signed, sig = raw_query[:i], raw_query[i + 5:]
if not re.fullmatch(r"[0-9a-f]{64}", sig):
return False
return any(
hmac.compare_digest(hmac.new(s.encode(), signed.encode(), hashlib.sha256).hexdigest(), sig)
for s in SECRETS
)
app = FastAPI()
@app.get("/adfacten/reward")
async def reward(request: Request) -> Response:
raw = request.scope["query_string"].decode("latin-1") # Flask: request.query_string.decode()
if not verify_adfacten(raw):
return Response(status_code=403)
q = {k: v[0] for k, v in parse_qs(raw, keep_blank_values=True).items()}
await grant_once(q["transaction_id"], q["user_id"], q["reward_type"], int(q["reward_amount"]), q.get("test") == "1")
return Response(status_code=200)
PHP
<?php
function verify_adfacten(string $raw, array $secrets): bool {
$i = strrpos($raw, '&sig=');
if ($i === false) return false;
$signed = substr($raw, 0, $i);
$sig = substr($raw, $i + 5);
if (!preg_match('/^[0-9a-f]{64}$/', $sig)) return false;
foreach ($secrets as $s) {
if (hash_equals(hash_hmac('sha256', $signed, $s), $sig)) return true;
}
return false;
}
$uri = $_SERVER['REQUEST_URI']; // ham adres (QUERY_STRING yeniden yazılmış olabilir)
$raw = ($p = strpos($uri, '?')) === false ? '' : substr($uri, $p + 1);
$secrets = array_filter([getenv('ADFACTEN_REWARD_SECRET'), getenv('ADFACTEN_REWARD_SECRET_OLD')]);
if (!verify_adfacten($raw, $secrets)) { http_response_code(403); exit; }
parse_str($raw, $q);
grant_once($q['transaction_id'], $q['user_id'], $q['reward_type'], (int)$q['reward_amount'], ($q['test'] ?? '') === '1');
http_response_code(200);
Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"net/http"
"net/url"
"os"
"strings"
)
var secrets = nonEmpty(os.Getenv("ADFACTEN_REWARD_SECRET"), os.Getenv("ADFACTEN_REWARD_SECRET_OLD"))
func verifyAdfacten(raw string) bool {
i := strings.LastIndex(raw, "&sig=")
if i < 0 {
return false
}
signed, given := raw[:i], raw[i+5:]
want, err := hex.DecodeString(given)
if err != nil || len(want) != sha256.Size {
return false
}
for _, s := range secrets {
m := hmac.New(sha256.New, []byte(s))
m.Write([]byte(signed))
if hmac.Equal(m.Sum(nil), want) {
return true
}
}
return false
}
func rewardHandler(w http.ResponseWriter, r *http.Request) {
raw := r.URL.RawQuery
if !verifyAdfacten(raw) {
w.WriteHeader(http.StatusForbidden)
return
}
q, _ := url.ParseQuery(raw)
if err := grantOnce(r.Context(), q.Get("transaction_id"), q.Get("user_id"), q.Get("reward_type"),
q.Get("reward_amount"), q.Get("test") == "1"); err != nil {
w.WriteHeader(http.StatusInternalServerError) // geçici hata: Adfacten tekrar dener
return
}
w.WriteHeader(http.StatusOK)
}
6.4 Aynı ödülü iki kez vermeyin
- Bir bildirim birden fazla kez gelebilir (ağ hatası, zaman aşımı, elle yeniden gönderim).
transaction_idher ödül için tekildir. Veritabanınızda bu alana tekil (unique) kısıt koyun ve ödülü aynı işlemde yazın. Daha önce işlenmiş birtransaction_idgelirse ödül vermeden 200 dönün.tsalanı tekrar denemelerde değişmez; bildirimler 72 saate kadar gecikebilir. Bu yüzden kısa bir zaman penceresiyle reddetmeyin; tekrarlarıtransaction_idile yakalayın.- İsterseniz
placementvereward_typedeğerlerinin beklediğiniz değerler olduğunu da kontrol edin.
6.5 Yanıt, tekrar deneme, kayıt
- Herhangi bir 2xx yanıt "teslim edildi" sayılır; yanıt gövdesi okunmaz. Bekleme süresi 10 saniyedir.
- 2xx dışı yanıtta veya zaman aşımında tekrar denenir: yaklaşık 1 dk, 5 dk, 15 dk, 1 sa, 3 sa, 6 sa, 12 sa, 24 sa, 24 sa sonra (toplam 10 deneme, yaklaşık 72 saat). Sonra bildirim dead (vazgeçildi) olur.
- Panelde uygulamanın Ödül bildirimleri kaydı her denemeyi (durum kodu, hata, sonraki deneme) gösterir;
oradan bir bildirimi elle yeniden gönderebilirsiniz (aynı
transaction_idile). - İmza geçersizse 403 dönün; gizli anahtarınızı düzeltip kayıttan yeniden gönderin.
6.6 Test reklamları
Test reklamının ödül bildiriminde test=1 gelir. Bu bildirimler entegrasyonu denemek içindir: gerçek
değer vermeyin (ya da yalnızca test ortamında verin).
6.7 Gizli anahtarı yenileme
Panel → Ödül bildirimi → Yeni gizli anahtar. Yeni anahtar hemen geçerli olur ve bir kez gösterilir; bekleyen tekrar denemeler de yeni anahtarla imzalanır. Kesintisiz geçiş için:
- Yeni anahtarı alın ve sunucunuza eskisiyle birlikte ekleyin (yukarıdaki örneklerde
…_OLD). - Birkaç saat sonra eski anahtarı silin.
Geçiş sırasında imzası tutmayan bildirimler kaybolmaz, tekrar denenir.
7. Gizlilik ve onay
| Durum | SDK / sunucu ne yapar |
|---|---|
GDPR / TCF: CMP IABTCF_gdprApplies=1 yazmış |
Reklam kimliği (GAID/IDFA) kullanılmaz; sıklık ve eşleştirme SDK kurulum kimliğiyle yapılır; tıklama adreslerinde cihaz kimliği makroları doldurulmaz. 1. amaç (cihazda bilgi saklama) onayı yoksa SDK reklam kimliğini ve IDFV'yi hiç göndermez. |
GPP: IABGPP_HDR_GppString dolu |
Reklam kimliği kullanılmaz, cihaz kimliği makroları doldurulmaz. |
ABD gizliliği: IABUSPrivacy_String 3. karakteri Y (satış itirazı) |
Aynı şekilde: reklam kimliği kullanılmaz. |
| Reklam takibi sınırlı (LMT): Android'de reklam kimliği silinmiş / sınırlı, iOS'ta ATT izni yok | Reklam kimliği kullanılmaz; reklamlar gösterilmeye devam eder. |
COPPA: childDirected = true veya uygulama panelde çocuklara yönelik |
Reklam gösterilmez; sunucu 403 coppa döner. |
- Onay aracı (CMP) değerleri cihazdaki standart IAB anahtarlarına yazmalıdır (Android SharedPreferences, iOS UserDefaults). SDK bunları her istekte okur.
- Bu durumların hiçbirinde reklam engellenmez (COPPA hariç); yalnızca kimlik kullanılmaz. Bu, gelirin bir miktar düşmesine yol açabilir.
- Çocuklar: Uygulamanız çocuklara yönelikse veya Google Play Aileler programındaysa Adfacten SDK'sını eklemeyin.
- KVKK: SDK cihaz kimlikleri, IP adresi ve reklam etkileşimi gibi kişisel veri sayılabilecek bilgileri işler. Uygulamanızın aydınlatma metninde ve gizlilik politikasında Adfacten'i reklam hizmet sağlayıcısı olarak belirtin. Gerekli hukuki dayanağı (gerekiyorsa açık rıza) yayıncı olarak siz sağlarsınız; emin değilseniz hukuk danışmanınıza başvurun.
8. Doğrudan REST kullanımı
SDK'yı kullanamıyorsanız (ör. kendi mediation katmanınız, oyun motoru eklentisi) aynı uçları doğrudan çağırabilirsiniz. Bu bir herkese açık, geriye dönük uyumlu arayüzdür: alanlar yalnızca eklenir, bilinmeyen alanları yok sayın.
8.1 Reklam isteği
POST https://trk.adfacten.com/m/v1/ad
Content-Type: application/json
User-Agent: AdfactenSDK/1.0.0 (android 15; SM-A546B) com.firma.uygulama/1.2.3
X-Adfacten-SDK: android/1.0.0
{
"v": 1,
"app_key": "apk_XXXXXXXXXXXXXXXXXXXXXXXX",
"placement": "plc_XXXXXXXX",
"format": "banner",
"sizes": ["320x50"],
"device": {
"os": "android", "osv": "15", "make": "Samsung", "model": "SM-A546B",
"sdk_id": "3f6c0a52-8f7e-4d1b-9a0c-1b2c3d4e5f60",
"ifa": "", "ifa_type": "sdk", "lmt": false,
"lang": "tr", "w": 1080, "h": 2340, "pxratio": 2.75,
"connection": "wifi", "carrier": "", "tz_offset_min": 180
},
"app": { "bundle": "com.firma.uygulama", "version": "1.2.3", "sdk_version": "1.0.0" },
"regs": { "coppa": false, "gdpr": 0, "tcf": "", "gpp": "", "us_privacy": "" },
"user": { "id": "kullanici-123", "custom": "level=12" },
"test": false
}
- Zorunlu alanlar:
app_key,placement,format,device.os,device.sdk_id,app.bundle.vyazılmazsa 1 sayılır. device.sdk_id: kuruluma özgü, kalıcı bir UUID (8–64 karakter). Uygulama ilk açıldığında üretip saklayın.device.ifa/ifa_type:gaid,idfa,idfv,appsetveyasdk. Reklam kimliği yoksa veyalmt=trueise boş bırakın.regs.coppaveregs.gdprtrue/falseveya0/1olabilir.useryalnızca ödüllü reklam için gerekir.- Gövde en çok 16 KB;
Content-Encoding: gzipile gönderilebilir (açılmış hali en çok 64 KB). placementyerine yerleşiminzon_…kimliği de kabul edilir.
8.2 Yanıt
204 No Content: reklam yok (envanter yok, yerleşim durdurulmuş veya istek reddedildi). Aynı yerleşim için en az 30 saniye (rastgele sapmalı) bekleyip tekrar isteyin.200:
{
"ad": {
"id": "opak, imzalı gösterim kimliği",
"format": "banner",
"kind": "image",
"test": false,
"w": 320, "h": 50,
"image": { "url": "https://cdn.adfacten.com/….webp", "url_2x": "…", "fallback": "….jpg" },
"click_url": "https://trk.adfacten.com/a/click?imp=…",
"trackers": {
"render": "https://trk.adfacten.com/i?imp=…&e=render",
"view": "https://trk.adfacten.com/i?imp=…&e=view",
"complete": "https://trk.adfacten.com/i?imp=…&e=complete",
"close": "https://trk.adfacten.com/i?imp=…&e=close"
},
"view_rule": { "min_ratio": 0.5, "min_ms": 1000 },
"close_after_s": 0,
"reward": { "type": "coins", "amount": 10 },
"label": "Reklam",
"expires_at": 1794160000
}
}
kind değerine göre içerik:
kind |
Alan | Nasıl çizilir |
|---|---|---|
image |
image |
Görsel; tıklanınca click_url |
html |
html |
Korumalı (sandbox) WebView, JavaScript köprüsü yok. WebView'in kendisi gezinmemeli: her gezinmeyi ve window.open'ı yakalayıp dışarıda (tarayıcıda) açın. |
video |
vast |
VAST 4.1 (interstitial / ödüllü). |
native |
native |
title, body, brand, cta, image, icon parçaları |
label("Reklam" / test reklamında "Test reklamı") her zaman görünür olmalı.close_after_s: kapatma düğmesi bu kadar saniye sonra görünür.- Geçerlilik: reklam
expires_atanına (Unix saniye, yaklaşık 55 dakika) kadar geçerlidir. Sonra göstermeyin, atın ve yenisini isteyin.
8.3 Olay bildirimleri (beacon)
Her tracker adresine sonuna &sid=<device.sdk_id>&ts=<milisaniye> ekleyip GET (veya boş gövdeli
POST) gönderin. sid, reklamı isteyen sdk_id ile aynı olmalıdır; başka bir sid ile gelen
olaylar ücretlendirilmez.
| Olay | Ne zaman |
|---|---|
render |
Reklam ekrana gerçekten çizildiğinde. Ücretlendirilen gösterim budur. |
view |
view_rule sağlanınca (en az %50 görünür, en az min_ms; video için 2 saniye) |
complete |
Video sonuna kadar izlenince. Video olmayan ödüllü reklamda: reklam ekrandayken close_after_s geçince. Ödül buna bağlıdır; erken gelen complete yok sayılır. |
close |
Kullanıcı kapatınca (kaydedilir, ücretlendirilmez) |
- Her olayı bir kez gönderin. Bağlantı yoksa sıraya alıp 24 saate kadar tekrar deneyin; yinelenenler sunucuda zaten yok sayılır.
kind=videoreklamlarda VAST'ın kendi Impression / Tracking adresleri de aynı uçlardır; VAST olaylarını veyatrackersalanını kullanabilirsiniz.- Tıklama:
click_urladresini sistem tarayıcısında veya ilgili uygulamada (ör. Play Store) açın; mağaza bağlantılarını uygulama içi WebView'de açmayın. Adres mağazaya veya hedef sayfaya yönlendirir.
8.4 Hata kodları
Hatalı isteklerde gövde {"code": "...", "message": "..."} olur. Bu hataları tekrar denemeyin;
yalnızca 5xx ve ağ hatalarında geri çekilerek tekrar deneyin.
| HTTP | code |
Anlamı |
|---|---|---|
| 400 | invalid_request |
Gövde okunamadı veya zorunlu alan eksik |
| 400 | placement_mismatch |
Yerleşim bulunamadı, başka bir uygulamanın, ya da format yerleşimin biçimiyle aynı değil |
| 403 | unknown_app |
Uygulama anahtarı yok |
| 403 | app_not_approved |
Uygulama reddedildi |
| 403 | platform_mismatch |
Anahtar diğer platformun (ör. iOS anahtarı Android'de) |
| 403 | bundle_mismatch |
app.bundle kayıtlı paket adıyla aynı değil |
| 403 | coppa |
Uygulama çocuklara yönelik veya regs.coppa açık |
| 403 | test_only |
İleride kullanım için ayrıldı (v1'de gönderilmez) |
9. Sorun giderme
SDK'da hata nesnesinin serverCode alanı yukarıdaki sunucu kodunu taşır. Ayrıntılı kayıt için
logLevel'ı geçici olarak Android'de LogLevel.DEBUG, iOS'ta .debug yapın (Android Logcat etiketi: Adfacten).
| Belirti | Neden | Çözüm |
|---|---|---|
unknown_app |
Uygulama anahtarı yanlış yazılmış veya başka ortamın anahtarı | Panelden apk_… anahtarını kopyalayın |
bundle_mismatch |
Uygulamanın paket adı (ör. .debug son ekli derleme) kayıtlı paket adından farklı |
Kayıtlı paket adıyla derleyin; debug derlemesinde applicationIdSuffix kullanmayın |
platform_mismatch |
Android anahtarı iOS'ta (ya da tersi) | Her platform için ayrı uygulama ekleyin, kendi anahtarını kullanın |
placement_mismatch |
Yerleşim kimliği yanlış, başka uygulamanın, arşivlenmiş ya da biçim tutmuyor (ör. banner yerleşimi RewardedAd ile yüklenmiş) |
Doğru plc_… kimliğini ve doğru reklam sınıfını kullanın |
app_not_approved |
Uygulama reddedildi | Ret gerekçesini panelde okuyun, düzeltip destek ekibine yazın |
coppa |
childDirected açık veya uygulama çocuklara yönelik işaretli |
Çocuklara yönelik uygulamalarda Adfacten kullanılamaz |
| Hep test reklamı geliyor | Bölüm 2.3'teki koşullardan biri | Paneldeki Yayın durumu'na bakın: onay bekliyor → onayı bekleyin; app-ads.txt → satırı ekleyin, Tekrar kontrol et; test modu → kapatın; SDK'da testMode açık → kapatın; emülatör → gerçek cihaz |
Reklam gelmiyor (204; Android NO_FILL, iOS .noFill) |
O an uygun reklam yok, yerleşim durdurulmuş, günlük sıklık sınırı dolmuş veya trafik bot/veri merkezi sayılmış | Normaldir; SDK 30 saniyeden önce tekrar istemez. Yerleşimin aktif olduğunu kontrol edin, VPN kullanmadan gerçek cihazda deneyin |
VIDEO_NOT_SUPPORTED (Android) |
Video reklam geldi ama ads-video eklenmemiş |
com.adfacten:ads-video bağımlılığını ekleyin |
Android AD_EXPIRED, iOS .expired |
Reklam yüklendikten sonra yaklaşık 55 dakika içinde gösterilmedi | Göstermeden kısa süre önce yükleyin; kapatınca yenisini yükleyin |
| Ödül bildirimi gelmiyor | Bildirim adresi girilmemiş, adres https değil, sunucunuz 2xx dönmüyor ya da imza doğrulaması hatalı |
Paneldeki ödül bildirimleri kaydında durum kodunu ve hatayı okuyun |