본문으로 건너뛰기

S2S Rewarded Callbacks

S2S(Server-to-Server) 리워드 콜백은 서버 측에서 리워드 검증을 수행하는 기능입니다.

8.5.0 버전부터 NAM SDK에서 S2S 리워드 콜백 기능을 제공합니다.

이 가이드에서는 S2S 리워드 콜백을 통합하는 방법을 설명합니다.

시작하기 앞서

  • S2S 리워드 콜백 기능은 NAM SDK 8.5.0 버전부터 지원됩니다.
  • NAM Admin에서 S2S 콜백 관련 설정이 완료되어 있어야 합니다.

[Step 1] NAM SDK 적용 완료

S2S 리워드 콜백 기능을 사용하려면 NAM SDK 8.5.0 이상이 필요합니다.

보상형 광고 시작하기를 참고해 주세요.

이하 내용은 NAM SDK 적용이 완료된 상태를 가정하고 진행합니다.


[Step 2] S2S 리워드 콜백 파라미터 설정 (선택사항)

S2S 리워드 콜백 시 추가 정보를 전달하려면 다음 파라미터들을 설정할 수 있습니다. 자세한 광고 요청 정보는 광고 요청 정보 페이지를 참고해주세요.

사용자 토큰 설정

setUserToken 메서드를 사용하여 리워드 콜백에 사용될 사용자 토큰을 설정할 수 있습니다.

val adParam = AdParam.Builder()
.setAdUnitId("YOUR_AD_UNIT_ID")
// 리워드 콜백용 사용자 토큰 설정
.setUserToken("your_user_token_here")
.build()

커스텀 파라미터 추가

addRewardCustomParam 메서드를 사용하여 리워드 콜백에 커스텀 파라미터를 추가할 수 있습니다.

주의

이 기능을 사용하기 전에 NAM 관리자와 상의해주세요.

val adParam = AdParam.Builder()
.setAdUnitId("YOUR_AD_UNIT_ID")
.setUserToken("your_user_token_here")
// 리워드 콜백용 커스텀 파라미터 추가
.addRewardCustomParam("level", "5")
.addRewardCustomParam("stage", "boss_battle")
.build()

[Step 3] RewardedAdListener 설정

S2S 리워드 콜백 지원을 위해 RewardedAdListener 인터페이스가 개선되었습니다. Kotlin으로 적용되면서 interface로 변경되었으며, 새로운 콜백 메서드들이 추가되었습니다.

새로운 콜백 메서드

  • onFailedServerRewardVerification: 리워드 검증이 실패했고, 재시도해도 실패하는 케이스입니다.
  • onReceiveErrorServerRewardVerification: timeout 등 검증 과정 중 에러가 발생한 케이스로, 서비스 정책에 맞춰 필요시 재시도해야 합니다.
  • onSuccessServerRewardVerification: 사용자가 리워드를 받을 수 있는 상태입니다.
rewardedAdManager.setAdListener(
object : RewardedAdListener {
override fun onAdLoaded(ad: GfpRewardedAd) {
// 광고 로드 완료 - 광고를 보여줄 준비 완료
}

override fun onAdStarted(ad: GfpRewardedAd) {
// 광고 시작됨
}

override fun onAdClicked(ad: GfpRewardedAd) {
// 광고 클릭됨
}

override fun onAdClosed(ad: GfpRewardedAd) {
// 광고 닫힘
}

override fun onAdCompleted(ad: GfpRewardedAd) {
// 광고 시청 완료
}

override fun onError(ad: GfpRewardedAd, error: GfpError) {
// 광고 로드 또는 노출 중 에러 발생
}

override fun onFailedServerRewardVerification(ad: GfpRewardedAd, error: GfpError) {
// 리워드 검증이 서버에 의해 거부됨 - 재시도는 무의미함
// 사용자에게 리워드 지급 불가 안내
}

override fun onReceiveErrorServerRewardVerification(ad: GfpRewardedAd) {
// 리워드 검증 중 에러 발생 - 서비스 정책에 따라 재시도 필요
// 사용자에게 재시도 여부를 묻거나 자동 재시도 수행
}

override fun onSuccessServerRewardVerification(ad: GfpRewardedAd) {
// 서버 리워드 검증 성공 - 사용자가 리워드를 받을 수 있음
// 사용자에게 리워드 지급
}
}
)

[Step 4] 서버 검증 활성화 여부 확인

광고가 로드된 후 isUseServerVerification 메서드를 사용하여 해당 광고에서 서버 사이드 검증이 활성화되어 있는지 확인할 수 있습니다.

override fun onAdLoaded(ad: GfpRewardedAd) {
// 서버 검증 활성화 여부 확인
val isServerVerificationEnabled = rewardedAdManager.isUseServerVerification()

if (isServerVerificationEnabled) {
logTextView.append("서버 사이드 검증이 활성화되었습니다.\n")
// S2S 리워드 콜백을 사용하는 로직
} else {
logTextView.append("클라이언트 사이드 검증을 사용합니다.\n")
// 기존 onAdCompleted 콜백을 사용하는 로직
}

showButton.isEnabled = true
}

[Step 5] 재시도 API 사용

onReceiveErrorServerRewardVerification 콜백이 발생했을 때 재시도가 필요하다면 GfpRewardedAdManagerrequestServerVerification() 메서드를 사용할 수 있습니다.

override fun onReceiveErrorServerRewardVerification(ad: GfpRewardedAd) {
// 서비스 정책에 따라 재시도 수행
// 예: 사용자에게 재시도 확인 후 재시도
showRetryDialog { shouldRetry ->
if (shouldRetry) {
rewardedAdManager.requestServerVerification()
} else {
// 광고 닫기
rewardedAdManager.close()
}
}
}

[Step 6] 광고 닫기 API 사용

재시도가 불가하거나 의미 없는 상황에서는 GfpRewardedAdManagerclose() 메서드를 통해 광고를 닫을 수 있습니다.

override fun onFailedServerRewardVerification(ad: GfpRewardedAd, error: GfpError) {
// 검증 실패 - 재시도 불가
// 사용자에게 안내 후 광고 닫기
showRewardFailureMessage()

rewardedAdManager.close()
}

[선택사항] Dialog 형태의 재시도 팝업 구현

Dialog 형태의 Activity로 재시도 팝업을 구현하려면, 예시로 제공되는 NdaRewardCallbackBroadcastReceiver를 사용할 수 있습니다.

전체 구현 예시

class RewardedFragment : Fragment() {
private var rewardedAdManager: GfpRewardedAdManager? = null
private lateinit var binding: FragmentRewardedBinding
private lateinit var logTextView: TextView
private lateinit var showButton: Button
private var ndaRewardCallbackBroadcastReceiver: NdaRewardCallbackBroadcastReceiver? = null

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentRewardedBinding.inflate(inflater)
logTextView = binding.logTextView
showButton = binding.showButton.apply {
isEnabled = false
setOnClickListener {
if (rewardedAdManager?.isAdInvalidated != false) {
logTextView.append("AD is not valid.\n")
return@setOnClickListener
}
rewardedAdManager?.showAd(requireActivity())
}
}

val adParam = AdParam.Builder()
.setAdUnitId(AD_UNIT_ID)
.build()
rewardedAdManager = GfpRewardedAdManager(requireActivity(), adParam)

rewardedAdManager?.setAdListener(
object : RewardedAdListener {
override fun onAdLoaded(ad: GfpRewardedAd) {
showButton.isEnabled = true
logTextView.append("Ad loaded\n")
}

override fun onAdStarted(ad: GfpRewardedAd) {
logTextView.append("Ad started\n")
}

override fun onAdCompleted(ad: GfpRewardedAd) {
logTextView.append("Ad completed\n")
}

override fun onAdClosed(ad: GfpRewardedAd) {
logTextView.append("Ad closed\n")
showButton.isEnabled = false
}

override fun onError(ad: GfpRewardedAd, error: GfpError) {
logTextView.append("Error: ${error.message}\n")
showButton.isEnabled = false
}

override fun onFailedServerRewardVerification(ad: GfpRewardedAd, error: GfpError) {
logTextView.append("Reward verification failed\n")
// 재시도는 무의미함
}

override fun onReceiveErrorServerRewardVerification(ad: GfpRewardedAd) {
logTextView.append("Reward verification error - showing retry dialog\n")
registerReceiver()
val intent = Intent(requireActivity(), RewardCallbackDialogActivity::class.java)
startActivity(intent)
}

override fun onSuccessServerRewardVerification(ad: GfpRewardedAd) {
logTextView.append("Reward verification success\n")
// 사용자에게 리워드 지급
}
}
)
rewardedAdManager?.loadAd()
return binding.root
}

override fun onDestroyView() {
super.onDestroyView()
rewardedAdManager?.destroy()
}

override fun onDestroy() {
super.onDestroy()
ndaRewardCallbackBroadcastReceiver?.let {
requireActivity().unregisterReceiver(it)
}
}

private fun registerReceiver() {
if (ndaRewardCallbackBroadcastReceiver == null) {
ndaRewardCallbackBroadcastReceiver =
NdaRewardCallbackBroadcastReceiver(
object : NdaRewardCallbackListener {
override fun onNdaRewardCallbackRetry() {
logTextView.append("Retry reward verification\n")
rewardedAdManager?.requestServerVerification()
}

override fun onNdaRewardCallbackCancel() {
logTextView.append("Close rewarded ad\n")
rewardedAdManager?.close()
}
},
RESULT_KEY
).apply {
registerReceiver(requireActivity(), this)
}
}
}

companion object {
private const val AD_UNIT_ID = "YOUR_AD_UNIT_ID"
private const val RESULT_KEY = "reward_result_key"
}
}

Broadcast Receiver의 Action

샘플에서 사용된 RewardCallbackDialogActivity에서는 다음 2가지 action을 보낸다고 가정합니다:

  • NdaRewardCallbackBroadcastReceiver.ACTION_NDA_REWARD_CALLBACK_RETRY: 재시도
  • NdaRewardCallbackBroadcastReceiver.ACTION_NDA_REWARD_CALLBACK_CANCEL: 취소

주의사항

서버 설정 필요

S2S 리워드 콜백을 사용하려면 NAM Admin에서 콜백 URL 설정이 완료되어 있어야 합니다. 관련 설정은 NAM 관리자에게 문의해주세요.

네트워크 연결

S2S 리워드 검증은 네트워크 통신을 통해 이루어지므로, 네트워크 연결 상태가 불안정할 경우 onReceiveErrorServerRewardVerification 콜백이 발생할 수 있습니다.

재시도 정책

onReceiveErrorServerRewardVerification 발생 시 무한 재시도를 방지하기 위해 적절한 재시도 횟수 제한을 두는 것이 좋습니다.