고객님의 문의에 답변하는 직원은 고객 여러분의 가족 중 한 사람일 수 있습니다.
고객의 언어폭력(비하, 조롱, 욕설, 협박, 성희롱 등)으로부터 직원을 보호하기 위해
관련 법에 따라 수사기관에 필요한 조치를 요구할 수 있으며, 형법에 의해 처벌 대상이 될 수 있습니다.
커뮤니티 이용 정책에 위배되는 게시물을 작성할 경우, 별도 안내 없이 게시물 삭제 또는 커뮤니티 이용이 제한될 수 있습니다.
문의 응대 : 평일 오전 10시 ~ 오후 6시
문의를 남기실 경우 다음 항목을 작성해 주세요.
정보가 부족하거나 응대시간 외 문의하는 경우 확인 및 답변이 지연될 수 있습니다.
- 베이스/채팅/데이터베이스/월드 SDK 버전 :
- 프로젝트명 :
- 스테이터스 코드 :
- 에러 코드 :
- 에러 메시지 :
뒤끝 개발팀을 통한 별도의 기술지원이 작업이 필요한 요청은 help@backnd.com 으로 문의해주시기 바랍니다.
#else
//뒤끝 영수처리O
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
var product = args.purchasedProduct;
string productId = product.definition.id;
Debug.Log($"[IAP] 구매 진행: {productId}");
StopPurchaseTimeout();
if (!product.hasReceipt || string.IsNullOrEmpty(product.receipt))
{
Debug.LogWarning($"[IAP] receipt 없음: {productId}");
UIManager.Instance.HideLoading();
return PurchaseProcessingResult.Complete;
}
// 여기서 뒤끝 영수증 검증 시작 (비동기)
ValidateReceiptAndGrant(product);
// 검증이 끝난 뒤 ConfirmPendingPurchase를 호출해야 하므로 Pending
return PurchaseProcessingResult.Pending;
}
private void ValidateReceiptAndGrant(Product product)
{
string productId = product.definition.id;
string receiptJson = product.receipt;
decimal iapPrice = product.metadata.localizedPrice;
string iapCurrency = product.metadata.isoCurrencyCode;
#if UNITY_ANDROID
SendQueue.Enqueue(
Backend.Receipt.IsValidateGooglePurchase,
receiptJson,
"IAP Verify",
false,
iapPrice,
iapCurrency,
callback => OnReceiptValidated(callback, product)
);
#elif UNITY_IOS
SendQueue.Enqueue(
Backend.Receipt.IsValidateApplePurchase,
receiptJson,
"IAP Verify",
iapPrice,
iapCurrency,
callback => OnReceiptValidated(callback, product)
);
#else
// 에디터 등: 필요하면 바로 성공 처리
OnReceiptValidated(null, product, forceSuccess:true);
#endif
}
#endif
private void OnReceiptValidated(BackendReturnObject callback, Product product, bool forceSuccess = false)
{
string productId = product.definition.id;
string transactionId = product.transactionID ?? "";
bool ok = forceSuccess || (callback != null && callback.IsSuccess());
Debug.Log(callback);
if (!ok)
{
Debug.LogWarning($"[IAP] 영수증 검증 실패: {productId} / {callback?.GetMessage()}");
UIManager.Instance.HideLoading();
UIManager.Instance.AlertBoxInstant("결제 검증에 실패했습니다.", true);
return; // 실패면 보상 지급 X, ConfirmPendingPurchase도 X
}
// 여기부터는 '검증 성공' 케이스만
decimal iapPrice = product.metadata.localizedPrice;
string iapCurrency = product.metadata.isoCurrencyCode;
Debug.Log($"[IAP] 영수증 검증 성공: {productId}");
Param logParam = new Param
{
{ "productId", productId },
{ "price", iapPrice.ToString() },
{ "currency", iapCurrency },
{ "transactionId", transactionId },
{ "utc", DateTime.UtcNow.ToString("o") }
};
Backend.GameLog.InsertLogV2("IAP_Purchase", logParam, (logCb) =>
{
if (!logCb.IsSuccess())
Debug.LogWarning("[IAP] 구매로그 저장 실패: " + logCb.GetMessage());
});
// ---- 기존 보상 지급 로직을 "그대로" 여기로 이동 ----
if (productId == ProductADRemoval || productId == productADRemova_iOS)
{
if (!myChar.IsAdRemoved)
{
myChar.IsAdRemoved = true;
RewardManager.Instance.PurchaseReward(productId);
}
else
{
Debug.Log($"[{productId}] 이미 광고 제거 처리됨. 중복 지급 안 함.");
}
}
else if (productId == ProductAutoOverclockPackage || productId == ProductAutoOverclockPackage_iOS )
{
if (!myChar.AutoOverClock)
{
myChar.AutoOverClock = true;
RewardManager.Instance.PurchaseReward(productId);
}
else
{
Debug.Log($"[{productId}] 이미 오버쿨럭 적용됨. 중복 지급 안 함.");
}
}
else
{
RewardManager.Instance.PurchaseReward(productId);
}
// Pending 구매 확정 (중요)
storeController.ConfirmPendingPurchase(product);
UIManager.Instance.HideLoading();
#if UNITY_ANDROID
firebaseManager.ShopProductPurchase(productId, (double)iapPrice, iapCurrency, transactionId);
if (productId == "character_22000" || productId == "character_17000")
{
var PurchaseName = CostumeManager.Instance.CostumeDataSO.CostumeList[myChar.SelectPurchaseCostumeNum].costumeType.ToString();
firebaseManager.CostumePurchase(PurchaseName);
}
#elif UNITY_IOS
firebaseManager.ShopProductPurchase(productId, (double)iapPrice, iapCurrency, transactionId);
if(productId == "character_22000" || productId == "character_17000")
{
var PurchaseName = CostumeManager.Instance.CostumeDataSO.CostumeList[myChar.SelectPurchaseCostumeNum].costumeType.ToString();
firebaseManager.CostumePurchase(PurchaseName);
}
#endif
}
3월까지는 위코드로 영수증검증을 잘 받고있었는데 최근에 iOS에서 영수증검증을 실행할때 400에러가 뜨면서 영수증검증에 실패하는데 이유를 모르겠습니다.
#elif UNITY_IOS
SendQueue.Enqueue(
Backend.Receipt.IsValidateApplePurchase,
receiptJson,
"IAP Verify",
iapPrice,
iapCurrency,
callback => OnReceiptValidated(callback, product)
);
이부분이 수정되야할까요?