-
-
Notifications
You must be signed in to change notification settings - Fork 535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
iOS - "Missing additionalData.appStore.discount when ordering a discount offer" #1600
Comments
getOffer() just returns the first offer in the list of offers (which on iOS is generally the only one). Google Play allows multiple offers for the same product. On iOS there will be a single offer. A discount offer is handled a bit differently, it requires a server that generates signed token that gives access to discount offers. I'm not really sure what you're trying to achieve, can you share your full source code (related to in-app purchases) and log outputs of a purchase session with verbosity set to debug in the plugin. |
I am not doing anything particularly exotic. In fact this code worked fine for more than a year but out of the blue it started returning a different offer. // Setup (This is React)
useEffect(() => {
if (!isNative()) {
return;
}
(async () => {
try {
CdvPurchase.store.verbosity = CdvPurchase.LogLevel.DEBUG;
IAP_PRODUCT_IDS.forEach((productId) => {
const p: CdvPurchase.IRegisterProduct = {
id: productId,
platform: CdvPurchase.Platform.APPLE_APPSTORE,
type: CdvPurchase.ProductType.PAID_SUBSCRIPTION,
};
CdvPurchase.store.register(p);
const p2: CdvPurchase.IRegisterProduct = {
id: productId,
platform: CdvPurchase.Platform.GOOGLE_PLAY,
type: CdvPurchase.ProductType.PAID_SUBSCRIPTION,
};
CdvPurchase.store.register(p2);
});
CdvPurchase.store.error((err) => {
console.log("Store error", err);
errorSender.current.sendError({
name: "CdvPurchase Error",
message: err.message,
stack: "",
time: new Date(),
context: {
code: err.code,
isError: err.isError,
},
});
});
CdvPurchase.store.validator = `${Env.ApiPrefix}/validate-iap-receipt`;
CdvPurchase.store.verbosity = CdvPurchase.LogLevel.DEBUG;
await CdvPurchase.store.initialize([
CdvPurchase.Platform.APPLE_APPSTORE,
CdvPurchase.Platform.GOOGLE_PLAY,
]);
// Load products
const newProducts = new Map(
CdvPurchase.store.products.map((p) => [p.id, p])
);
setProducts(newProducts);
// End load products
// Very important to reload currencies/etc when store is initialized
setLanguageByCode(localStorage.language ?? getBrowserLanguage(), true);
CdvPurchase.store.when().approved(async (transaction) => {
setOrderProcessing(true);
console.log(
`GlobalContextProvider - Transaction approved - transactionId: ${transaction.transactionId}`,
transaction
);
transaction.verify();
});
CdvPurchase.store.when().unverified(async () => {
setOrderProcessing(false);
});
CdvPurchase.store.when().verified(async (verifiedReceipt) => {
setOrderProcessing(false);
const receipt = verifiedReceipt.sourceReceipt;
// A lot of code to verify receipt, etc
});
} catch (e) {
if (e instanceof Error) {
errorSender.current.sendError({
name: e.name,
message: e.message,
stack: e.stack,
time: new Date(),
context: {},
});
}
}
})();
}, []);
// When they click sign up
const product = CdvPurchase.store.get(getPlanAlias(plan));
const offer = await product?.getOffer(
product.platform == CdvPurchase.Platform.APPLE_APPSTORE ? "$" : undefined
); // DEFAULT_OFFER_ID
if (!offer) {
global.trackError({
name: "Native store error",
message: "Cannot find products or offers",
stack: "",
context: {
product,
products: CdvPurchase.store.products,
},
time: new Date(),
});
setError(global.translate("cannot_start_purchase"));
setProcessing(false);
return;
}
console.log("offer.order()");
const purchaseResult = await offer.order();
console.log("offer.order() after");
if (purchaseResult?.isError) {
if (purchaseResult.message === "USER_CANCELED") {
setError(global.translate("purchase_aborted"));
} else {
setError(purchaseResult.message);
}
setProcessing(false);
} else {
setError("");
setProcessing(false);
}
}; |
First thing I noticed: you shouldn't initialize before setting up the events handlers (store.when goes before store.initialize) -- I wonder how that works because, IIRC, the "initialize" promise should only resolve when pending transactions in the queue have been processed (finished, verified or unverified). At the very least, you might be missing some events. This is the code in the plugin that emits that particular error: const discountId = offer.id !== DEFAULT_OFFER_ID ? offer.id : undefined;
const discount = additionalData?.appStore?.discount;
if (discountId && !discount) {
return callResolve(appStoreError(ErrorCode.MISSING_OFFER_PARAMS, 'Missing additionalData.appStore.discount when ordering a discount offer', offer.productId));
} With (Side note, you can access that constant as So somehow |
I will move Thank you for responding. |
I made this nasty workaround, but it was working for me.
|
Observed behavior
From July 14 to Aug 14 we had zero iOS sign ups. I checked my log and everyone had been getting this error:
It was not related to a new version of our app.
It seemed like the order of the offers was wrong. It wasn't getting the default offer. I fixed it this way:
Old:
New:
But people still get this error once in a while, only on iOS, confirmed to be the new version of the app with my fix applied.
Any suggestions?
The text was updated successfully, but these errors were encountered: