With the current IAP plugin on iOS, if a Restore Purchase is called and the user fails to enter their credentials then there is no callback fired. I notice in the plugin code that it isn’t implemented, and the link to the Apple API shows lots of deprecated code.
The same behaviour on Buy IAP calls an error callback, so it is likely possible.
Is it still be possible to add a callback for restore errors?
I’ve read that Storekit 2 needs Swift and we can’t use this with Defold extensions?
We do support Swift in extension dependencies but not in the dependencies themselves. If Storekit 2 requires Swift then we need to look into adding support for Swift.
Deprecated code will eventually become a problem but it is not necessarily a problem right now. Could you please create an issue in extension-iap for this?
With the help of ai I’m trying to implement the restore callbacks on iOS. I kinda think I’m getting somewhere - the callbacks are triggered - but there is a crash when I attempt to pass the message to Defold (and I’m pretty sure there is missing code there).
The crash happens in iap_ios.mm on line 451 where I am likely not pushing the correct values.
I wouldn’t be bothered too much, but I fear rejection from Apple (and fellow humans too, but mostly Apple in this case)
Got a working version! Might be of interest to somebody else in future.
Very much written by ChatGPT 5-mini with basic testing by myself. Still using Storekit 1.
For the listener I’m using this…
-- Handle synthetic restore completion/failure sent as transaction events
if type(transaction) == "table" and transaction.state ~= nil then
if transaction.state == iap.TRANS_STATE_RESTORE_FINISHED then
-- restore sequence finished (no per-product data in this synthetic event)
is_working = false
print("DEFOLD IAP > Restore completed")
-- optionally act on restored_products table here
return
elseif transaction.state == iap.TRANS_STATE_RESTORE_FAILED then
-- restore failed, error table is provided as second arg (may be nil)
is_working = false
local err = error or { error = "Unknown error", reason = iap.REASON_UNSPECIFIED }
print("DEFOLD IAP > Restore failed:", err.error, err.reason)
return
end
end
if error == nil then
-- transaction.ident - product identifier ex: com.defold.my_iap_product
-- transaction.state - see below
-- transaction.date - date and time for transaction
-- transaction.trans_ident - transaction identifier, only set when state is either
-- TRANS_STATE_RESTORED
-- TRANS_STATE_UNVERIFIED
-- TRANS_STATE_PURCHASED
-- transaction.receipt - send to server to verify
-- transaction.original_trans - Apple only when state is TRANS_STATE_RESTORED
-- transaction.signature - Google only signature of purchase data
if transaction.state == iap.TRANS_STATE_PURCHASING then
--
elseif transaction.state == iap.TRANS_STATE_PURCHASED then
if owned_products[transaction.ident] == nil then
owned_products[transaction.ident] = transaction
end
if iap.get_provider_id() == iap.PROVIDER_ID_APPLE then -- permanent Apple products must always be finished
iap.finish(transaction)
elseif iap.get_provider_id() == iap.PROVIDER_ID_GOOGLE then
iap.acknowledge(transaction)
end
is_working = false
elseif transaction.state == iap.TRANS_STATE_UNVERIFIED then
-- https://defold.com/manuals/iap/#asynchronous-payments
is_working = false
elseif transaction.state == iap.TRANS_STATE_FAILED then
is_working = false
elseif transaction.state == iap.TRANS_STATE_RESTORED then
restored_products[transaction.ident] = transaction
is_working = false
end
else
-- error.reason can be
-- iap.REASON_UNSPECIFIED
-- iap.REASON_USER_CANCELED
if error.reason == iap.REASON_UNSPECIFIED then
is_working = false
elseif error.reason == iap.REASON_USER_CANCELED then
is_working = false
end
end
end