Closed nayan-dhabarde closed 2 weeks ago
@nayan-dhabarde Not all app receipts have a transaction id within them, are you confident this one does?
@alexanderjordanbaker Wait, can you point me to some resources which can help me understand what kind of appReceipt won't have a transactionId.
I was under the assumption that if a person made a transaction and generated the receipt using above code it will have the id. which I can send to my server and then use getTransactionInfo with this transaction id. This helps me verify the purchase and save it against my userId.
I am doing this specially to bind my userId and the transaction on the server, I am getting the notification and i am able to decode it successfully, but the notification can only use userId in UUID format in appAccountToken whereas my userId is a Long right now.
Why won't it have a transaction id?
@nayan-dhabarde Transaction ids are for in-app purchases. This is an app receipt, if the user has no in-app purchases then there will be no transaction ids.
@alexanderjordanbaker I see, I am sending the receipt only after user has made the purchase to auto-renewable subscription. This is indeed an in-app purchase.
Here is my complete code, if it helps:
func purchase(_ product: Product) async throws -> Transaction? {
guard let userId = AppUserDefaults.shared.user?.id else {
throw NSError()
}
// Begin purchasing the `Product` the user selects.
let options = Product.PurchaseOption.custom(key: "userId", value: "\(userId)")
let result = try await product.purchase(options: [options])
fetchingProducts = true
switch result {
case .success(let verification):
let receiptURL = Bundle.main.appStoreReceiptURL
if let url = receiptURL, let data = try? Data(contentsOf: url) {
let base64Receipt = data.base64EncodedString(options: [])
do {
let result = try await repository.validateApplePurchase(userId: AppUserDefaults.shared.user?.id, purchaseToken: base64Receipt).data
if result == false {
fetchingProducts = false
return nil
}
}
catch {
print("Couldn't read receipt data with error: " + error.localizedDescription)
}
}
// Check whether the transaction is verified. If it isn't,
// this function rethrows the verification error.
let transaction = try checkVerified(verification)
// The transaction is verified. Deliver content to the user.
await updateCustomerProductStatus()
// Always finish a transaction.
await transaction.finish()
fetchingProducts = false
return transaction
case .userCancelled, .pending:
fetchingProducts = false
return nil
default:
fetchingProducts = false
return nil
}
}
@nayan-dhabarde This looks to be a StoreKit 2 purchase, is there any reason you aren't sending the transaction directly to your server and verifying it with a SignedDataVerifier? This would be much simpler and faster
That would be
case .success(let verification):
let signedTransaction = verification.jwsRepresentation
If you are using StoreKit 2 to perform the purchase, there should be no reason to use the deprecated app receipt and associated functions, ReceiptUtility
is only around to support use cases that need to support clients using deprecated StoreKit 1, which doesn't appear to be the case here
@alexanderjordanbaker I see, thanks for clarifying that. I will give it a try
Closing due to no comments, please create a new issue if any further questions are needed
Hello team,
I have tested the code to extract transaction id from app receipt on sandbox, it was working fine.
When I deployed it on production it is failing to extractTransactionIdFromAppReceipt
Server side build gradle