paymentQueue always going for .restored option - swift

I'm facing a very strange behaviour, As the code was not changed, It seems to me like a version specific issue, as it comes from nowhere
I'm testing on sandbox environment
the scenario is when I tries to buy product using
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
the apple default purchase or any other popup didn't show
and the control goes directly into restored transactionState
public func paymentQueue(_: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
DispatchQueue.main.async {
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
self.complete(transaction: transaction)
break
case .failed:
hideLoader()
self.fail(transaction: transaction)
break
case .restored:
hideLoader()
self.restore(transaction: transaction)
break
case .deferred:
hideLoader()
break
case .purchasing:
showLoader()
break
#unknown default:
break
}
}
}
}
I'm not sure why it is going there, as apple need to show any popup or any information related to the process
After this function we are validating reciept and reciept returns
"pending_renewal_info": [
{
"expiration_intent": "1",
"auto_renew_product_id": "BUNDLE_ID",
"original_transaction_id": "transaction_id",
"is_in_billing_retry_period": "0",
"product_id": "PRODUCT ID",
"auto_renew_status": "0"
}
]
I'm not sure why the expiration_intent is coming 1 in this case

I had the same issue. Problem was that iTunes/Store account is separated from sandbox testing account.
Settings > iTunes & App Stores > at the bottom there should be SANDBOX ACCOUNT section. Change that account to some other that doesn't have the purchase on it.

Related

StoreKit 2 unexpected behavior with consumable items

I'm trying to implement iAP with StoreKit 2. Maybe my code isn't very elegant but seems to work ;-)
But the following behavior I can#t understand:
If I first start my app in simulator or on my test device I can bought a consumable item. If I want to buy it again (how consumables should work) the app automatically restores the purchase and don't ask for purchase.
Is this a specific behavior in my test environment or do I understand consumables wrong?
Because StoreKit 2 is quite new I haven't found a tutorial in the internet that's fits for me.
Any hints are very wellcome :-)
Maybe I solved my problem. Can please someone approve it if I'm right?
I added await transaction.finish() to my purchase()
func purchaseTSBooster() {
self.boughtItem = true
Task.init {
guard let tsProduct = tsBoosterProducts.first else {return}
do {
let result = try await tsProduct.purchase()
switch result {
case .success(let verification):
switch verification {
case .verified(let transaction):
DispatchQueue.main.async {
self.tsBoosterID.append(transaction.productID)
}
await transaction.finish()
case .unverified(_, _):
break
}
case .userCancelled:
buyaborted = true
case .pending:
break
#unknown default:
break
}
}
catch {
print(error)
}
}
}

Swift In-App purchases sandbox tester error

I am implementing the In-App purchases function today, and I just followed the tutorial step by step, created sandbox testers, wrote the code, and it says
<SKPaymentQueue: 0x282e50860>: Payment completed with error: Error Domain=ASDServerErrorDomain Code=3502 "This item is not available." UserInfo={NSLocalizedDescription=This item is not available.
Why is "This item is not available."? I searched the relevant information online, but there is no answer for it.
Here is my code
#IBAction func purchaseButtonPressed(_ sender: UIButton) {
print("PRESSED")
purchaseApp()
}
func purchaseApp() {
let productID = "com.crazycat.Reborn.FullFuctionalities"
if SKPaymentQueue.canMakePayments() {
let paymentRequest = SKMutablePayment()
paymentRequest.productIdentifier = productID
SKPaymentQueue.default().add(paymentRequest)
} else {
print("Can't make payments")
}
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
if transaction.transactionState == .purchased {
print("Thanks for shopping")
} else if transaction.transactionState == .failed {
print("purchase Failed")
}
}
}
I had the same issue.
Please make sure your paid application agreement in Appstore connect is Active and not Expired. Check if there are any warnings in the App Store connect. Complete all of the bank, tax, and contact information on your App Store Connect Paid Apps Agreements.
Then relaunch the app from Xcode on your physical device.
The transaction should be successful then.
Check below points
Use the same test account you specified in developer console.
Make sure the In-App product shows a status of Ready to Submit on the developer console.
Make sure the In-App product id matches what your using in your app.

When purchasing an IAP subscription, the transaction shows the wrong state

I'm porting our iOS app to MacOs and have experienced a strange problem. When I purchase an auto-renewable subscription, the transaction state of the given transaction isn't .purchased, but .restored. That makes no sense.
#objc func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchasing:
handlePurchasingState(for: transaction, in: queue)
case .purchased:
handlePurchasedState(for: transaction, in: queue)
case .restored:
handleRestoredState(for: transaction, in: queue)
case .failed:
handleFailedState(for: transaction, in: queue)
case .deferred:
handleDeferredState(for: transaction, in: queue)
#unknown default:
SwiftyBeaver.debug("Payment: unknown error")
}
}
}
This leads wrongly to the following function:
func handleRestoredState(for transaction: SKPaymentTransaction, in queue: SKPaymentQueue) {
SKPaymentQueue.default().finishTransaction(transaction)
}
But because there is nothing to be restored the final function isn't called either:
#objc func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
// once all items are restored, this gets called here:
verifyWithTegantAPI()
}
Hence our Api isn't called. The user has now to go and click on Restore Purchases again to get the purchase activated, which is anything but ideal.
If the transaction had the correct state, it would have led to here:
func handlePurchasedState(for transaction: SKPaymentTransaction, in queue: SKPaymentQueue) {
SKPaymentQueue.default().finishTransaction(transaction)
verifyWithServerAPI()
}
The purchase itself happens like this:
func purchase(subscription: Subscription) {
SwiftyBeaver.debug(subscription.product.productIdentifier)
let payment = SKPayment(product: subscription.product)
SKPaymentQueue.default().add(payment)
}
What am I missing please?

How to add state tracking AWS iOS?

I am having trouble using this code from AWS documentation to check the user state. No matter where I place it prints nothing. I also have properly set up my project with the AWS iOS SDK. I have placed in the AppDelegate and in different view controller's viewDidLoad and viewDidAppear however it still prints nothing.
AWSMobileClient.default().addUserStateListener(self) { (userState, info) in
switch (userState) {
case .guest:
print("user is in guest mode.")
case .signedOut:
print("user signed out")
case .signedIn:
print("user is signed in.")
case .signedOutUserPoolsTokenInvalid:
print("need to login again.")
case .signedOutFederatedTokensInvalid:
print("user logged in via federation, but currently needs new tokens")
default:
print("unsupported")
}
}

SKPaymentQueue does not finish SKPaymentTransaction for an auto-renewable subscription product

I'm trying to implement Auto-renewable subscription product. The problem is that SKPaymentQueue cannot finish SKPaymentTransaction by calling SKPaymentQueue.defaultQueue().finishTransaction(transaction).
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
if let transactions = transactions as? [SKPaymentTransaction] {
for transaction in transactions {
switch transaction.transactionState {
case .Purchasing:
break
case .Purchased:
queue.finishTransaction(transaction) // finish transaction
self.purchasedWithTranasction(transaction)
case .Failed:
queue.finishTransaction(transaction) // finish transaction
self.failedWithTransaction(transaction)
case .Restored:
queue.finishTransaction(transaction) // finish transaction
self.restoredWithTransaction(transaction)
case .Deferred:
queue.finishTransaction(transaction) // finish transaction
}
}
}
}
As the above, In paymentQueue:updatedTransactions: method, queue.finishTransaction(transaction) are called. Normally, the transactions are finished and will not stay anymore.
But when I run the app again, that transactions still remain not finished. By the way, I set the transaction observer in the AppDelegate like the below. So, when the app launched, remaining transactions start being processed by calling paymentQueue:updatedTransactions:. It's not like my expectation.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// add transaction observer
SKPaymentQueue.defaultQueue().addTransactionObserver(CargoBay.sharedManager())
....
return true
}
Do you guys have any idea about this problem? Is there any case that SKPaymentQueue.defaultQueue().finishTransaction(transaction) does not finish transaction?
Is it possible that you're not seeing the same transaction again, but instead a new auto-renewal transaction from the iTunes sandbox?
When testing auto-renewing subscriptions in the iTunes sandbox, they artificially speed up the rate of renewals. This causes a new transaction to appear every few minutes.
From Testing Your App and In-App Purchase Products:
When testing auto-renewable subscriptions in the test environment, keep in mind that the duration times are compressed. Additionally, test subscriptions only auto-renew a maximum of six times.
1 week : 3 minutes
1 month : 5 minutes
2 months : 10 minutes
3 months : 15 minutes
6 months : 30 minutes
1 year : 1 hour