The application has changed from paid purchase to free use. We need to obtain the previous player's purchase records to unlock the paid content.
//Here is the code for the client to obtain the player's payment information:
NSURL * receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]){
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
NSString *receiptUrlString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSLog(@"requestAppStoreReceipt receiptUrlString = %@", receiptUrlString);
} else {
// 如果凭证为空,则再发一次凭证请求
SKReceiptRefreshRequest *refreshReceiptRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:@{}];
refreshReceiptRequest.delegate = self;
[refreshReceiptRequest start];
NSLog(@"requestAppStoreReceipt 如果凭证为空,则再发一次凭证请求!");
}
//The following is the server-side parsing code:
Path filePath = Path.of("SubscriptionKey_ABCDEFGHIJ.p8");
String encodedKey = Files.readString(filePath);
Environment environment = Environment.SANDBOX;
AppStoreServerAPIClient client = new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment);
String appReceipt = "MIIcs...";
ReceiptUtility receiptUtil = new ReceiptUtility();
String transactionId = receiptUtil.extractTransactionIdFromTransactionReceipt(transactionReceipt);
System.out.println(transactionId);
if (transactionId != null) {
long now = System.currentTimeMillis();
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;
response = client.getTransactionHistory(transactionId, revision, request, GetTransactionHistoryVersion.V2);
transactions.addAll(response.getSignedTransactions());
} while (response.getHasMore());
Set<InputStream> rootCAs = Set.of(
new FileInputStream("AppleComputerRootCertificate.cer"),
new FileInputStream("AppleIncRootCertificate.cer"),
new FileInputStream("AppleRootCA-G2.cer"),
new FileInputStream("AppleRootCA-G3.cer")
);
Long appAppleId = 1234567899L; // appAppleId must be provided for the Production environment
System.out.println(transactions.size());
SignedDataVerifier signedPayloadVerifier = new SignedDataVerifier(rootCAs, bundleId, appAppleId, environment, true);
for (String notificationPayload : transactions) {
try {
AppTransaction payload = signedPayloadVerifier.verifyAndDecodeAppTransaction(notificationPayload);
System.out.println(payload);
} catch (VerificationException e) {
e.printStackTrace();
}
}
}
//Return result analysis:
JWSTransactionDecodedPayload{originalTransactionId='2000000641683476', transactionId='2000000641683476', webOrderLineItemId='null', bundleId='', productId='', subscriptionGroupIdentifier='null', purchaseDate=1719566962000, originalPurchaseDate=1719566962000, expiresDate=null, quantity=1, type='Consumable', appAccountToken=null, inAppOwnershipType='PURCHASED', signedDate=1728885967093, revocationReason=null, revocationDate=null, isUpgraded=null, offerType=null, offerIdentifier='null', environment='Sandbox', storefront='CHN', storefrontId='143465', transactionReason='PURCHASE', price=6000, currency='CNY', offerDiscountType='null', unknownFields=null}
We have develop according to the following document on our end:
https://developer.apple.com/documentation/foundation/nsbundle/1407276-appstorereceipturl#4098404
https://developer.apple.com/documentation/appstoreserverapi/get_transaction_info/
https://developer.apple.com/documentation/appstoreserverapi/data_types
We would like to know if the solutions in the document can be used to solve the problems we encountered?
Is there a problem with our method of parsing bills that prevents us from obtaining the necessary information?