Skip to main content

iOS Receipt Validation (StoreKit 2)

Validates the signed transaction (Signed Transaction, JWS) of Unity IAP 5.0 (StoreKit 2).

StoreKit 2 (JWS) Method

This method validates the StoreKit 2 signed transaction (JWS) of Unity IAP 5.0 (iOS 15.0 or later). If you use the Base64 receipt of Unity IAP 4.x (StoreKit 1), refer to iOS Receipt Validation.

Call Information

IAP.IOS_JWS(string jwsTransaction, string productID, PlayNANOODelegate callback) {}
IAP.IOS_JWS(string jwsTransaction, string productID, bool isDuplicateAllow, PlayNANOODelegate callback) {}

Call Details

ParameterDescriptionType
jwsTransactionSigned transaction (JWS). Unity IAP 5.0's pendingOrder.Info.Apple?.jwsRepresentationstring
productIDPayment product IDstring
isDuplicateAllowAllow duplicate receipt validationboolean
callbackCommunication resultPlayNANOODelegate
Currency/Amount Parameters Not Required

Unlike the legacy IOS(), currency and price are not passed. The server extracts the currency and amount directly from the JWS payload to prevent forgery or tampering.

Example

When using Unity IAP Service (Unity IAP 5.0)

using PlayNANOO;
using UnityEngine.Purchasing;

public class PlayNANOOExample : MonoBehaviour
{
Plugin plugin;

void Awake()
{
plugin = Plugin.GetInstance();
}

// Unity IAP 5.0 - PendingOrder 처리
public void OnPurchasePending(PendingOrder pendingOrder)
{
string jws = pendingOrder.Info.Apple?.jwsRepresentation;
string productID = pendingOrder.CartOrdered.Items()[0].Product.definition.id;

plugin.IAP.IOS_JWS(jws, productID, (status, errorMessage, jsonString, values) => {
if (status.Equals(Configure.PN_API_STATE_SUCCESS))
{
Debug.Log(values["UserID"]);
Debug.Log(values["PackageName"]);
Debug.Log(values["OrderID"]);
Debug.Log(values["ProductID"]);
Debug.Log(values["Currency"]);
Debug.Log(values["Quantity"]);
Debug.Log(values["Price"]);
Debug.Log(values["PurchaseState"]);

// 검증 성공 시 거래 확정
storeController.ConfirmPurchase(pendingOrder);
}
else
{
Debug.Log("Fail");
}
});
}
}

Response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"UserID" : "String",
"PackageName" : "String",
"OrderID" : "String",
"ProductID" : "String",
"Quantity" : "Integer",
"Currency" : "String",
"Price" : "Integer",
"PurchaseState" : "String"
}

Response Details

Data KeyDescriptionType
UserIDUser identifier IDstring
PackageNameApp package namestring
OrderIDOrder ID (transactionId)string
ProductIDPayment product IDstring
QuantityPurchase quantityinteger
CurrencyPayment currency codestring
PriceProduct priceinteger
PurchaseStateValidation result (purchase successful payment / wait processing in progress / cancel validation failed)string
Refund Detection

Games that have migrated to StoreKit 2 can automatically detect refunds through App Store Server Notifications (Webhook). For the registration procedure, refer to the Refund Webhook Registration section at the bottom of this page.

Error Information

When receipt validation fails ErrorCode : 20000
ErrorMessage : ValidationFailException

When the application package name does not match ErrorCode : 20002
ErrorMessage : NotMatchPackageNameException

When the receipt has already been used ErrorCode : 20005
ErrorMessage : ExistsReceiptException

Refund Webhook Registration (App Store Server Notifications)

To receive events such as refunds (REFUND) in real time, register the following Webhook URL with App Store Server Notifications (Version 2). The Webhook operates on a per-game (per-app) basis, so registering the URL applies regardless of the SDK version, and refunds are automatically reflected in the payment log.

Webhook URL

https://service-api.playnanoo.com/webhook/apple/{gameId}

{gameId} is the game channel ID. Copy the actual URL auto-generated per game from Console > Settings > API & Platform Settings > iOS using the copy button.

App Store Connect Registration Steps

StepDescription
1Go to App Store Connect > Select app > App Information > App Store Server Notifications
2Enter the Webhook URL above in Production Server URL
3Enter the same URL in Sandbox Server URL
4Select "Version 2" for Notification Version and save
No separate authentication key (.p8) required

Webhook receipt verification is also performed using only the JWS signature (Apple certificate chain), so no separate authentication key is required. The Production / Sandbox distinction is determined automatically by the environment field in the payload sent by Apple.