728x90
Google 애드몹 광고단위에는 배너형 보상형 등.. 광고들이 있다.
오늘은 그것들 중 보상형 광고 (광고영상, 광고게임) 후, 이것을 서버측에서 검증 하는 방법을 적어보고자 한다.
Problem
"부탁해" 에서는 광고 후,
"마카다미아 2배속", "돼지 먹이주기" 보상을 제공 하는데, 이를 사용자가 실제로 광고를 정상적으로 시청 했는지 검증하는 도구가 필요 했다.
애드몹에서는 이를 위해 SSV 기능을 제공한다.
Solution
https://developers.google.com/admob/android/ssv?hl=ko
서버 측 인증 (SSV) 콜백 확인 | Android | Google for Developers
Android용 Google 모바일 광고 SDK를 사용하여 보상형 광고의 서버 측 확인 (SSV) 콜백을 확인합니다.
developers.google.com
Java Tink 라이브러리를 사용해서 검증을 할 수 있다.
순서
1. 사용자 광고 시청 -> 광고시청완료 ->
2. 클라이언트가 애드몹 서버로 (광고 시청 완료 됨 - 커스텀키 포함) 요청 ->
3. 애드몹이 my서버로 (광고 시청 완료됨 - 커스텀키 포함 ) 요청 ->
4. 클라이언트 - 서버로 광고 완료 요청(커스텀키 포함) ->
5. 2번에서 보낸 커스텀 키를 서버에서 검증 ->
6. 보상지급
코드를 보자
의존성 추가 ㄱㄱ
(필자는 Kotlin 을 사용 중..)
implementation("com.google.crypto.tink:apps-rewardedads:1.14.0")
애드몹 -> my서버로 들어오는 요청 (3번)
private val adDoneKey = "ad:ssv:done:"
fun processReward(fullUrl: String, queryParam: String): Boolean {
try {
//쿼리 스트링 해체
val params = parseQueryString(queryParam)
// 클라이언트에서 애드몹 서버로 보낸 customData 가 여기로 들어옵니다.
val sessionId = params["custom_data"] ?: return false
// url 검증
verifier.verify(fullUrl)
// 중복된 SessionId 가 있다면 그냥 true
val ssvKey = "$adDoneKey$sessionId"
val first = redisTemplate.opsForValue().setIfAbsent(ssvKey, "1", Duration.ofMinutes(5))
if (first != true) {
return true
}
// Redis 를 이용하여 클라이언트에서 애드몹으로 보낸 Session Key 를 저장 해줍니다.
val tempKey = "$adsRewardKey$sessionId"
redisTemplate.opsForValue().set(tempKey, "1", Duration.ofMinutes(30))
return true
} catch (e: Exception) {
log.error("SSV Verification Failed", e)
return false
}
}
// 쿼리 스트링 해체 작업
private fun parseQueryString(query: String): Map<String, String> {
return query.split("&").associate {
val (key, value) = it.split("=").let { pair ->
pair[0] to (pair.getOrNull(1) ?: "")
}
key to java.net.URLDecoder.decode(value, "UTF-8")
}
}
// RewardedAdsVerifier 는 tink 라이브러리로 검증 할 수 있다.
private val verifier = RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build()
클라이언트 -> my 서버로 요청 (4번 단계)
data class RewardRequest(
val sessionId: String
)
@PostMapping
fun pig(@RequestBody dto: RewardRequest) {
service.pigHistoryRegisterV2(dto, getAuthReq())
}
fun pigHistoryRegisterV2(dto: RewardRequest): Int {
verificationManager.claimUserReward(dto.sessionId, customerId)
....
//리워드 지급 로직
return amount
}
private val adsRewardKey = "ad:reward:temp:"
fun claimUserReward(sessionId: String): Boolean {
val key = "$adsRewardKey$sessionId"
// Redis 에 저장된 키를 삭제 해준다. 없으면 false 로 유효하지 않은 Key
val deleted = redisTemplate.delete(key)
if (deleted != true) {
throw NoSuchElementException("유효하지 않거나 이미 사용된 보상입니다.")
}
// 광고 지급 저장 로직...
return true
}
728x90
'개-발 > Java + Spring + Kotlin' 카테고리의 다른 글
| [Spring] Apple 소셜 로그인 구현 (f.Expo) (0) | 2025.12.17 |
|---|---|
| [Spring] chatGpt api 연동 (0) | 2025.09.18 |
| [Spring] itemReader 에서 ReadOnly DB 읽기 (0) | 2025.09.05 |
| [spring] React Native FCM 푸시 알림 구현 (f.Kotlin) (0) | 2025.08.20 |
| [Spring] storekit2 / expo 인 앱 아이템 구매 확인 구현 3 (AOS) (0) | 2025.08.07 |