in_app_purchase: not getting product details for iOS - ios

I'm using the in_app_purchase package to sell consumables and subscriptions.
It's working fine for android but on iOS I cannot get any product details.
await InAppPurchase.instance.isAvailable() //returns true
Set<String> ids = Set.from({'subs_item_purchase', 'pro_account'});
ProductDetailsResponse response = await InAppPurchase.instance.queryProductDetails(ids);
print(response.productDetails); //returns []
print(response.notFoundIDs); //returns [subs_item_purchase, pro_account]
print(response.error); //returns null
in App Store connect I created the subscription with the id 'pro_account'. it is in status 'Waiting for Review'.
I also created a sandbox user and logged in.
What do I miss?

The problem was that the apple contract for paid apps did expire. I signed a new one and problem solved

Related

Should the purchase of an Auto-Renewable Subscription via Xamarin Forms iOS be set to Autorenewing by default?

I'm using James Montemagno's InAppBilling plugin for Xamarin Forms iOS.
In the App Store I have configured an Auto-Renewable Subscription.
I'm using the following to purchase and restore respectively:
InAppBillingPurchase purchase = await billing.PurchaseAsync(MYSUBSCRIPTION_PRODUCTID, _itemType);
and
List<InAppBillingPurchase> purchases = (await billing.GetPurchasesAsync(ItemType.Subscription))?.ToList();
InAppBillingPurchase purchase = the latest purchase in the purchases list
The Apple system prompt states that
"Plan automatically renews for [price]...[period] until cancelled"
and after the purchase:
"Cancel at any time in Settings at least one day before each renewal
date"
However the purchase that is returned always has the following set:
purchase.AutoRenewing == false;
Why is this the case?
If I can't trust this AutoRenewing flag, is there any other way I can get the AutoRenewing status without the use of a server?

StoreKit 2 - Product request always empty

I'm trying to fetch the products from the App Store Connect using StoreKit 2 but always returns an empty array:
let keys = [
"com.app.suscripcion.mes.1"
]
let storeProducts = try await Product.products(for: keys)
I had added a key on the InApp Purchase section:
What do I have to do to fetch the products from the App Store Connect? If I use a .storekit configuration file it works but with a real scenario doesn't work
To solve this problem, the Account Holder from App Store Connect, inside Agreements, Tax, and Banking, needs to Set Up Tax and Banking to Paid Apps in order to get the InApp Purchase products from App Store Connect:
After that, the Status for the Paid Apps Type need to change to Active in order to make it work.
Check if the StoreKit Configuration value is set to none in the options of the scheme that was executed.

In-app purchases trouble with SwiftyStoreKit and AppStore Connect moderator

I'm newbie with in-app purchases and SwiftyStoreKit.
I made an in-app purchases in my app and sent it for moderation.
My application moderator rejected and wrote the reason:
We found that your in-app purchase products exhibited one or more bugs
when reviewed on iPhone and iPad running iOS 14.1 on Wi-Fi.
Specifically, your app’s In-App Purchases do not show the app’s price.
Also, upon further review, we found that your app does not allow users
to purchase the In-App Purchases.
Next Steps
When validating receipts on your server, your server needs to be able
to handle a production-signed app getting its receipts from Apple’s
test environment. The recommended approach is for your production
server to always validate receipts against the production App Store
first. If validation fails with the error code "Sandbox receipt used
in production," you should validate against the test environment
instead.
The price was not displayed probably because the application received the answer code: "skerrordomain code=0" or something like that. Or because the Apple server returned 0 products.
That is, when ViewController starts, app request the price of the products from the Apple server and then write it down to the buttons.
I don't know exactly what the problem is, because on my local device purchases work with a Sandbox user. In Simulator I get an error: "skerrordomain code=0".
Here is a code sample:
enum RegisteredPurchase: String {
case item1 = "com.app.appname.item1"
case item2 = "com.app.appname.item2"
case item3 = "com.app.appname.item3"
}
purchases: [RegisteredPurchase]
var productList: Set<String> = []
for purchase in purchases {
productList.insert(purchase.rawValue)
}
SwiftyStoreKit.retrieveProductsInfo(productList) { result in
var products: Dictionary<RegisteredPurchase, String> = [:]
for product in result.retrievedProducts {
products[RegisteredPurchase(rawValue: product.productIdentifier)!] = product.localizedPrice
}
if (result.error == nil) {
self.setupPrice(products)
}
}
In the result I get retrievedProducts. On the local device all 3 products come, in Simulator I get 0 and the error.
Fixed by removing all products and creating new ones.

Restore of an in-app non-consumable product in Codenameone

My issue, i really need help with, is that i can't get the logic working which renders the 'Restore Purchase' button conditionally.
I have built my IOS app with an in-app purchase button that allows the user to pay once to unlock extra features - of type "Non-Consumable Product". The purchase feature works fine in TestFlight using my regular home user Apple IDs and some other Test Apple IDs (not sandbox testers). I don't have a Mac and so can't use a Sandbox environment. When the trial period is up i display the Purchase button, this invokes the CN1 Purchase API and PurchaseCallback, and i then use Storage to store the flag that its been purchased.
purchaseApp.addActionListener(e -> {
if (Purchase.getInAppPurchase().wasPurchased("full_product")) {
ToastBar.showMessage("You have already previously purchased this product.", FontImage.MATERIAL_INFO);
...
} else {
Purchase p = Purchase.getInAppPurchase();
p.purchase("full_product");
}
});
(I am not doing anything with receipts or receipt_stores.)
But to comply with Apple i need a 'Restore Purchase' button, so users don't pay again on new devices. The difficulty is getting the logic right, as i only want the Restore button to display IF they have paid for it before, else i just want the Purchase button displayed.
So i created the userHasPreviousPurchased() method below to test for the button to show, and added RestoreCallback interface to my main class as per CN1 documentation. But the method below never returns true on my IPhone using a TestFlight build, so my Restore button never shows. It is that p.getReceipts() always returns an empty List, when i expect 1 row with my SKU in it.
private boolean userHasPreviousPurchased() {
Purchase p = Purchase.getInAppPurchase();
if (p.isRestoreSupported()) {
p.synchronizeReceipts();
List<Receipt> receipts = p.getReceipts();
for (Receipt receipt : receipts) {
if (receipt.getSku().equalsIgnoreCase("full_product")) {
return true;
}
}
}
return false;
}
Please can you point me in the right direction. Using CN1 API's how do you test for a previous purchase using this type of IAP?
I tried creating a sandbox test user and, after signing out on my device, purchasing the IAP using the user (which was accepted). I have debug code in my app with tests the above method and that still didn't return a receipt (i fail to see how it could have as i wasn't signed in as the sandbox user).
Many thanks in advance
If you want to keep track of who has purchased your products already, you need to have a server-side component to store this. The Receipts API depends on this, and requires you to implement a ReceiptStore. Check out these blog posts that cover this in detail.
Introduction to In-App Purchase
Non-renewable Subscriptions - This introduces you to ReceiptStores and how they work.
Auto-renewing Subscriptions in iOS and Android - This includes full sample code for client and server for Android and iOS.

in-app purchase validation - validate purchase as per user

I had successfully implemented in-app purchase in my application.
My query is :
There is One apple id xyz#apple.com through which i am logged in to itunes in ipad or device.
now in my application there are several users. They will purchase products through in-app purchase. my product is non-consumable.
Now, for example there are two user A and B (they logged in to my application so they are application users).
case : user A purchase product using in-app purchase with apple id xyz#apple.com and get user rights of purchased product member (here on success of completion of payment i call web service to make user database update that he/she had purchased product) and logout from my application. Now User B logged in to my application and going to purchase product as well as User A purchased (here Note that the apple id is same xyz#apple.com). But while he trying to purchase product apple says that “you've already purchased this.Tap OK to download it again for free” so user tap OK and this product will again restore as per user and again on success of that method i call web service which make user database update.
so question is how can i differ both user while purchasing product that from this id (i.e. xyz.apple.com) you had already purchased and now user should be different then you have to login using another apple id (may be abc.apple.com).
finally i solved in app purchase issue with the following solution :
1) i get transaction id and update it with the database as per user if transaction id founds duplicate then it will throw error and don’t make user as a paid member.
Means when A user make purchase then i get it's transaction_id and update it to data base as Username : A and transaction_id : XYZ.
and if B user going to purchase from same iTunes id then i will get same transaction_id that is XYZ. So here it will not allow user to be proceed(i.e. from web service don't let user to be paid member and give error). following is code:
-(void)callinApp : (SKPaymentTransaction*) transaction
{
if (transaction.originalTransaction.transactionIdentifier == nil)
{
// send transection.transactionIdentifire
// means it's first time purchasing
NSLog(#"Transection Id : %#",transaction.transactionIdentifier);
UserId = A123;
TransectionID = transaction.transactionIdentifier;
}
else
{
// send transaction.originaltransection.transectionidentifier
// means already purchased one in past
NSLog(#"Original Transection Id:%#",transaction.originalTransaction.transactionIdentifier);
UserId = A123;
TransectionID = transaction.originalTransaction.transactionIdentifier;
}
// call web service and pass TransectionID & UserId
}
2) The error which was coming while doing transaction from another iTunes id the method (updateTransaction) calling multiple time i solved that by removing that apple(iTunes) id from sandbox tester in iTunes connect.
Non-consumable products are purchased once by users and do not expire or decrease with use.
even thought if you are looking for flow that how to match between login user id and in app purchase id. actually i did in App Purchase long times ago so i don't know what parameter sent from apple once in app purchase succeeded. there may be in-app purchase receipt and some other parameter as well. just need to pickup unique parameter that is same for number of time once product downloaded from same user.
So here i am consider "in-app purchase receipt" as unique parameter return once in App purchase succeeded. and once in App purchase succeeded, you can bind in-app purchase receipt with login user id on your sever. so next time when B user going to download same product with same apple user id, in App purchase request will return same in-app purchase receipt so check that weather this receipt id is bind with another login id, if YES then cancel download and show message to user that "This account is already linkup with some other login id".

Resources