IsValidateOneStorePurchase
public BackendReturnObject IsValidateOneStorePurchase(bool isGlobal, string productId, string purchaseToken, string receiptDescription);
public BackendReturnObject IsValidateOneStorePurchase(bool isGlobal, string productId, string purchaseToken, string receiptDescription, string iapPrice, string iapCurrency);
화폐 숫자 표현에서 .(소수점)을 ,(쉼표)로 사용하는 일부 국가들이 존재합니다.
이러한 국가에서는 각 스토어에서 제공하는 iapPrice 값도 ,가 포함된 형태로 전달됩니다.
뒤끝의 영수증 검증은 숫자 데이터만 처리할 수 있기 때문에, ,가 포함된 값을 iapPrice 파라미터로 그대로 사용할 경우 금액 정보가 정상적으로 반영되지 않고 0으로 처리됩니다.
, 가 포함된 iapPrice 데이터는 .로 치환하여 사용해 주세요.  
사용 불가능한 특수문자
| 특수문자 | 한글명 | 
|---|---|
| ' | 작은 따옴표 | 
| \ | 역슬래시 | 
사용 가능한 특수문자
| 특수문자 | 한글명 | 
|---|---|
| . | 마침표 | 
| , | 쉼표 | 
| " | 큰따옴표 | 
| () | 괄호 | 
| ! | 느낌표 | 
| ? | 물음표 | 
| ~ | 물결 | 
| @ | 골뱅이 | 
| * | 곱하기 | 
| + | 더하기 | 
| - | 빼기 | 
| / | 슬래시 | 
표기가 되지 않은 특수문자를 사용할 경우에는 영수증 검증이 정상적으로 진행되는지 확인 후 적용해주세요.
파라미터
| Value | Type | Description | 
|---|---|---|
| isGlobal | bool | 결제가 국외에서 발생했을 경우 true, 국내에서 발생했을 경우 false | 
| productId | string | OneStore.Purchasing.PurchaseData.purchase.ProductId | 
| purchaseToken | string | OneStore.Purchasing.PurchaseData.purchase.PurchaseToken | 
| receiptDescription | string | 추가로 저장하고자 하는 내용 | 
| iapPrice | string | 해당 아이템의 가격 | 
| iapCurrency | string | 해당 아이템의 통화 | 
설명
원스토어에서 지원하는 인앱 결제 서비스의 IPurchaseCallback.OnConsumeSucceeded()에서 구매한 상품에 대한 영수증을 받아 뒤끝 서버를 통해 영수증 검증을 받을 수 있습니다.
- 뒤끝은 영수증 자체의 유효성과, 구매한 productId를 검증합니다.
- 뒤끝 로그인 없이 뒤끝 영수증 검증 기능을 사용하는 것은 불가능합니다.
금액 표시(선택 사항)
뒤끝 콘솔의 영수증 검증 항목에서 해당 구매 내역의 금액을 표시하고자 할 경우, 인자값 iapPrice와 iapCurrency를 추가해야합니다.
PurchaseClientImpl클래스의 QueryProductDetails 호출 시, 등록된 IPurchaseCallback의 핸들러에서 OnProductDetailsSucceeded가 호출되며 금액을 확인할 수 있습니다.
public class YourCallback: IPurchaseCallback
{
    public void OnProductDetailsSucceeded(List<ProductDetail> productDetails)
    {
        OneStoreReceiptTest.productDetailList = productDetails;
        Debug.Log("OnProductDetailsSucceeded ");
        foreach (var pro in productDetails)
        {
            Debug.Log($"{pro.title} / {pro.type} / {pro.price} / {pro.promotionPrice} / {pro.productId}");
        }
    }
    public void OnPurchaseSucceeded(List<PurchaseData> purchases)
    {
        Debug.Log("OnPurchaseSucceeded ");
        foreach (var pro in purchases)
        {
            OneStoreReceiptTest.purchaseClient.ConsumePurchase(pro);
        }
    }
    public void OnConsumeSucceeded(PurchaseData purchase)
    {
        Debug.Log("OnConsumeSucceeded ");
        string productPrice = "";
        string productCurrency = "";
        OneStoreReceiptTest.productDetailList.ForEach(productDetail =>
        {
            if (productDetail.productId == purchase.ProductId)
            {
                productPrice = productDetail.price;
                productCurrency = productDetail.priceCurrencyCode;
            }
        });
        var bro = Backend.Receipt.IsValidateOneStorePurchase(false, purchase.ProductId, purchase.PurchaseToken, data, productPrice, productCurrency);
        if (bro.IsSuccess())
        {
            Debug.Log("ValidateOneStoreReceipt : " + bro);
        }
        else
        {
            Debug.LogError("ValidateOneStoreReceipt : " + bro);
        }
    }
}
public class OldOneStoreReceiptTest : MonoBehavior
{
    // Start is called before the first frame update
    public static PurchaseClientImpl purchaseClient = null;
    public static List<ProductDetail> productDetailList = new List<ProductDetail>();
    public static List<string> productList = new List<string>();
    public void InitializeOneStore()
    {
        purchaseClient = new PurchaseClientImpl("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ~~");
        purchaseClient.Initialize(new YourCallback());
        purchaseClient.LaunchUpdateOrInstallFlow(action =>
        {
            Debug.Log("LaunchUpdateOrInstallFlow " + action.ToString());
        }); 
        
        productList.Add("onestore_gamepoint");
        purchaseClient.QueryProductDetails(productList.AsReadOnly(), ProductType.ALL);
    }
    private void Purchase()
    {
        if (purchaseClient == null)
        {
            Debug.LogError("purchaseClient is null");
            return;
        }
        
        Debug.Log("원스토어 로그인 성공");
        var builder = new PurchaseFlowParams.Builder();
        builder.SetProductId(productList[0]);
     
        purchaseClient.Purchase(builder.Build());
        new OneStoreAuthClientImpl().LaunchSignInFlow(signInResult =>
        {
            if (signInResult.IsSuccessful())
            {
                Debug.Log("LaunchSignInFlow Success");
            }
            else
            {
                Debug.LogError("LaunchSignInFlow Fail");
            }
        });
    }    
}
Example
동기
    List<ProductDetail> productDetailList;
    public void BuyButton(int i)
    {
        string productId = "gold";
        // 결제 로직
        ProductType productType = ProductType.INAPP;
        var purchaseFlowParams = new PurchaseFlowParams.Builder()
            .SetProductId(productId)                // 추가 필수
            .SetProductType(productType)            // 추가 필수
            .SetDeveloperPayload("따로 메모할만한 정보 여기에 넣기")  // 선택사항
            .Build();
        purchaseClient.Purchase(purchaseFlowParams);
    }
    public void OnPurchaseSucceeded(List<PurchaseData> purchases)
    {
        bool isGlobal = false; // 원스토어에 국내에서만 앱을 출시하였을 경우
        /*
        뒤끝 영수증 검증 처리
        */
        for(int i = 0; i <  purchases.Count; i++) {
            BackendReturnObject validation = Backend.Receipt.IsValidateOneStorePurchase(isGlobal, purchases[i].ProductId, purchases[i].PurchaseToken, "receiptDescription");
            // 영수증 검증에 성공한 경우
            if (validation.IsSuccess()) {
                Debug.Log($"ProcessPurchase: PASS. Product: {purchases[i].ProductId}");
                if (purchases[i].ProductId == "gold") {
                    // 골드 지급
                }
            } else {
                Debug.Log($"ProcessPurchase: FAIL. Unrecognized product: {purchases[i].ProductId}");
            }
        }
    }
비동기
    public void BuyButton(int i)
    {
        string productId = "gold";
        // 결제 로직
        ProductType productType = ProductType.INAPP;
        var purchaseFlowParams = new PurchaseFlowParams.Builder()
            .SetProductId(productId)                // 추가 필수
            .SetProductType(productType)            // 추가 필수
            .SetDeveloperPayload("따로 메모할만한 정보 여기에 넣기")  // 선택사항
            .Build();
        purchaseClient.Purchase(purchaseFlowParams);
    }
    public void OnPurchaseSucceeded(List<PurchaseData> purchases)
    {
        bool isGlobal = false; // 원스토어에 국내에서만 앱을 출시하였을 경우
        /*
        뒤끝 영수증 검증 처리
        */
        for(int i = 0; i <  purchases.Count; i++) {
            
            Backend.Receipt.IsValidateOneStorePurchase(isGlobal, purchases[i].ProductId, purchases[i].PurchaseToken, "receiptDescription", (callback) =>
            {
                // 영수증 검증에 성공한 경우
                if (callback.IsSuccess()) {
                    Debug.Log($"ProcessPurchase: PASS. Product: {purchases[i].ProductId}");
                    if (purchases[i].ProductId == "gold") {
                        // 골드 지급
                    }
                } else {
                    Debug.Log($"ProcessPurchase: FAIL. Unrecognized product: {purchases[i].ProductId}");
                }
            });
        }
    }
ReturnCase
Success cases
성공한 경우
statusCode : 201
message : Success
returnValue : {"usedDate":"2018-10-15T05:17:49Z"}  
Error cases
콘솔에 원스토어 정보가 올바르지 않는 경우
statusCode : 400
errorCode : UndefinedParameterException
message : undefined onestore client_id, onestore client_id을(를) 확인할 수 없습니다
영수증 검증이 유효하지 않을 경우
statusCode : 400
errorCode : BadParameterException
message : bad token, 잘못된 token 입니다
영수증 토큰이 string.Empty일 경우
statusCode : 400
errorCode : BadParameterException
message : undefined token, token을(를) 확인할 수 없습니다
productId가 string.Empty일 경우
statusCode : 400
errorCode : BadParameterException
message : undefined productId, productId을(를) 확인할 수 없습니다
위변조된 영수증 토큰
statusCode : 400
errorCode : BadParameterException
message : bad token, 잘못된 token 입니다
이미 사용한 영수증 토큰
statusCode : 409
errorCode : UsedReceipt
message : This receipt has already been used. usedDate: 2018-02-15T04:01:50.000Z