Reputation: 11
I am facing an issue while reviewing the In-App Purchase discount promo code process for iOS. It is working well on Android.
We have integrated the In-App Purchase mechanism in our Flutter app and now want to verify it on iOS. On iOS, we display the PromoCodeRedemptionSheet
(provided by Apple’s In-App Purchase Kit framework) where the user can enter a promo code.
After entering the code and successfully applying it, once the redemption sheet is dismissed, we are not receiving the callback in the listener for the PurchaseUpdated
event.
It worked fine the first time, but the issue occurs when we attempt to review the process again.
Note: To verify the promo code mechanism during debugging, we added a StoreKit
file in Xcode and are using that StoreKit configuration to simulate the purchase process. When the redemption sheet opens, all the promo codes created in the Apple Developer Console are displayed, allowing the user to select one. The selected offer code is applied, and the user sees the sandbox payment sheet. After a successful payment, we see the "Thank You" screen with a "Close" button. However, after tapping the "Close" button and dismissing the redemption sheet, the callback is not received at the app level in the purchase stream.
class OfferCodeScreenForIosState extends State<OfferCodeScreenForIos> {
final InAppPurchase _iap = InAppPurchase.instance;
late StreamSubscription<List<PurchaseDetails>> _subscription;
bool _isAvailable = false;
List<ProductDetails> _products = [];
bool _loading = true;
@override
void initState() {
super.initState();
printf("Offer_Code_ScreenFor_iOS > initState");
// Set up a listener for purchases
final purchaseUpdated = _iap.purchaseStream;
_subscription = purchaseUpdated.listen((purchases) {
**//// THIS IS NOT WORKING WHEN PURCHASING THE DIFFERENT IN-APP SECOND TIME**
printf("Offer_Code_ScreenFor_iOS > purchaseUpdated.listen");
_handlePurchaseUpdates(purchases);
}, onError: (error) {
// Handle error
printf("Purchase Error: $error");
});
// Initialize In-App Purchases
_initializeStore();
}
Future<void> _initializeStore() async {
// Check availability of the store
_isAvailable = await _iap.isAvailable();
if (_isAvailable) {
const Set<String> ids = {
'{{INAPP_PURCHASE_PRODUCT_ID}}',
};
final ProductDetailsResponse response =
await _iap.queryProductDetails(ids);
if (response.notFoundIDs.isNotEmpty) {
printf("Not found IDs: ${response.notFoundIDs}");
}
setState(() {
_products = response.productDetails;
_loading = false;
});
}
}
Future<void> _redeemOfferCode() async {
if (await SKPaymentQueueWrapper.canMakePayments()) {
try {
final iapStoreKitPlatformAddition =
_iap.getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
await iapStoreKitPlatformAddition.presentCodeRedemptionSheet();
} catch (e) {
printf("Error presenting Offer Code Redemption Sheet: $e");
}
} else {
printf("In-App Purchases are disabled on this device.");
}
}
// Handle purchase updates
void _handlePurchaseUpdates(List<PurchaseDetails> purchases) {
for (var purchase in purchases) {
if (purchase.status == PurchaseStatus.purchased) {
printf("Purchase successful: ${purchase.productID}");
//// COMPLETING PURCHASE FOR TESTING BUT WE WILL NEED TO VERIFY SERVER VERIFICATION DATA HERE
_iap.completePurchase(purchase);
} else if (purchase.status == PurchaseStatus.pending) {
printf(
"Purchase status pending purchaseID: ${purchase.purchaseID}");
} else if (purchase.status == PurchaseStatus.restored) {
printf("Purchase Restored: ${purchase.purchaseID}");
} else if (purchase.status == PurchaseStatus.canceled) {
printf("Purchase Cancelled: ${purchase.purchaseID}");
} else if (purchase.status == PurchaseStatus.error) {
printf("Purchase error: ${purchase.error?.message}");
}
}
}
}
Upvotes: 1
Views: 146