You can use console.log()
in the app to dump debug info. You can do this even when running on the device; console.log()
output gets sent to the device console in XCode.
I’m using Capacitor with React; I wrapped the plugin in a class and added a debug info method. Here are the relevant methods from the class:
public initialize(
state: SubscriptionStoreState,
dispatch: React.Dispatch<StoreStateReducerAction>,
) {
// Register all products for use in the app.
this.store.register(classicProducts);
// Configure the validator.
this.store.validator = iaptic.validator;
// Set debug message verbosity.
if (isEnvEdoDebugOn()) {
this.store.verbosity = CdvPurchase.LogLevel.DEBUG;
} else {
// Default is quiet.
this.store.verbosity = CdvPurchase.LogLevel.QUIET;
}
// Track all store errors.
this.store.error((error) => {
if (error.code === CdvPurchase.ErrorCode.PAYMENT_CANCELLED) {
debugLog('The user cancelled the purchase flow.');
return;
}
debugLog('Store Error', JSON.stringify(error));
dispatch({
type: StoreState.HasError,
payload: {
error: error.message,
},
});
});
// Declare store event listeners.
this.store
.when()
.receiptsVerified(() => {
this.dumpDebugInfo('RECEIPTS VERIFIED!');
})
.receiptsReady(() => {
this.dumpDebugInfo('RECEIPTS READY!');
})
.productUpdated((product) => {
debugLog('Product updated', product);
})
.approved((transaction) => {
debugLog('Transaction approved', transaction);
// Upon approval, verify the receipt.
// If we are getting ready, do not change the state.
if (this.store.isReady && !state.isInitializing()) {
dispatch({ type: StoreState.Verifying });
}
// setIsVerifying(true);
transaction
.verify()
.catch((reason: unknown) =>
catchPromiseError(reason, 'Failed to verify transaction!'),
);
})
// Upon receipt validation, mark the subscription as owned.
.verified((receipt) => {
debugLog('Receipt verified', receipt);
// Once the receipt is verified, we may have an active sub.
invalidateQuery(this.queryClient, queryKeyIapActiveSub).catch(
(reason: unknown) =>
catchPromiseError(reason, 'Failed to invalidate ASQ!'),
);
receipt
.finish()
.catch((reason: unknown) =>
catchPromiseError(reason, 'Failed to finish receipt!'),
);
})
.unverified((receipt) => {
debugLog('Receipt not verified', receipt);
if (state.isMakingPurchase()) {
dispatch({ type: StoreState.Idle });
}
})
// Upon the subscription becoming owned.
.finished((processedTransaction) => {
debugLog('Transaction complete', processedTransaction);
invalidateQuery(this.queryClient, queryKeyUseUser)
// .then(() => setShowSuccessAlert(true))
.catch((reason: unknown) =>
catchPromiseError(reason, 'Failed to invalidate! sWF'),
);
});
/**
* Prints debug info to the console to try to figure out what the heck is going on.
*/
private dumpDebugInfo = (state: string): void => {
if (isEnvEdoDebugOn()) {
const { products } = this.store;
// eslint-disable-next-line no-console
console.log(
state,
this.store,
'vP',
this.store.verifiedPurchases,
'vR',
this.store.verifiedReceipts,
'iR',
this.store.isReady,
'lR',
this.store.localReceipts,
'lT',
this.store.localTransactions,
'products',
products,
'expired sub',
this.getFirstExpiredSubscription(),
'IAP SUB INFO',
this.getIAPSubscriptionInfo(),
);
}
};