Since verifyReceipt is marked as deprecated, there is appstore server library; e.g. this is the java version: https://github.com/apple/app-store-server-library-java/tree/main
According to https://developer.apple.com/videos/play/wwdc2023/10143/, client (game) sends receipt to server for validation and server extracts transactionId with ReceiptUtility and asks for transaction history to appstore to receive signed transactions. During this request, server can filter to receive only consumables; e.g:
TransactionHistoryRequest request = new TransactionHistoryRequest()
.sort(TransactionHistoryRequest.Order.DESCENDING)
.revoked(false)
.productTypes(List.of(
TransactionHistoryRequest.ProductType.CONSUMABLE));
Working in sanbox env, everything seems fine, I can make a successful in app purchase; but I receive below from transaction history request:
APIException{httpStatusCode=404, apiError=4040010, apiErrorMessage='Transaction id not found.'}
But when I use getTransactionInfo with the same extracted transactionId, I receive a successful response like below:
TransactionInfoResponse{signedTransactionInfo='eyJ....
I have 2 questions here:
1- why am i getting error in TransactionHistoryRequest
2- what is the correct way to validate an in app purchase server side without verifyReceipt?
Thanks in advance
PS: Here are sample codes for TransactionHistoryRequest and getTransactionInfo:
TransactionHistoryRequest
String encodedKey = Files.readString(filePath);
Environment environment = Environment.SANDBOX;
AppStoreServerAPIClient client =
new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment);
String receipt = "MIIUW......";
ReceiptUtility receiptUtil = new ReceiptUtility();
String transactionId = receiptUtil.extractTransactionIdFromAppReceipt(receipt);
System.out.println("extracted txId is " + transactionId);
if (transactionId != null) {
TransactionHistoryRequest request = new TransactionHistoryRequest()
.sort(TransactionHistoryRequest.Order.DESCENDING)
.revoked(false)
.productTypes(List.of(TransactionHistoryRequest.ProductType.CONSUMABLE));
HistoryResponse response = null;
List<String> transactions = new LinkedList<>();
do {
String revision = response != null ? response.getRevision() : null;
System.out.println("revision is " + revision);
response = client.getTransactionHistory(transactionId, revision, request);
transactions.addAll(response.getSignedTransactions());
} while (response.getHasMore());
System.out.println(transactions);
}
getTransactionInfo
String encodedKey = Files.readString(filePath);
System.out.println("encoded key is " + encodedKey);
String receipt = "MIIUW...";
ReceiptUtility receiptUtil = new ReceiptUtility();
String txId = receiptUtil.extractTransactionIdFromAppReceipt(receipt);
Environment environment = Environment.SANDBOX;
AppStoreServerAPIClient client =
new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment);
try {
TransactionInfoResponse response = client.getTransactionInfo(txId);
String signed = response.getSignedTransactionInfo();
System.out.println(">>>" + signed.split("\\.").length);
String jws_payload = signed.split("\\.")[1];
String payload = new String(Base64.decodeBase64(jws_payload));
System.out.println(response);
System.out.println("payload is " + payload);
} catch (APIException | IOException e) {
e.printStackTrace();
}