expo-iap 2.3.3 → 2.3.4-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,7 +37,10 @@ export declare const latestTransaction: (sku: string) => Promise<ProductPurchase
37
37
  type RefundRequestStatus = 'success' | 'userCancelled';
38
38
  export declare const beginRefundRequest: (sku: string) => Promise<RefundRequestStatus>;
39
39
  /**
40
- *
40
+ * Shows the system UI for managing subscriptions.
41
+ * When the user changes subscription renewal status, the system will emit events to
42
+ * purchaseUpdatedListener and transactionUpdatedIos listeners.
43
+ * @returns {Promise<null>}
41
44
  */
42
45
  export declare const showManageSubscriptions: () => Promise<null>;
43
46
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"ios.d.ts","sourceRoot":"","sources":["../../src/modules/ios.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,eAAe,EAAE,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAGhE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAGF,eAAO,MAAM,qBAAqB,aACtB,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,6CAO5C,CAAC;AAGF,wBAAgB,YAAY,CAAC,CAAC,SAAS;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAC,EACxD,IAAI,EAAE,OAAO,GACZ,IAAI,IAAI,CAAC,GAAG;IAAC,QAAQ,EAAE,KAAK,CAAA;CAAC,CAO/B;AAGD;;;GAGG;AACH,eAAO,MAAM,IAAI,QAAO,QAAQ,IAAI,CAAyB,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,uBAAuB,YAAa,MAAM,KAAG,QAAQ,OAAO,CACzB,CAAC;AAEjD;;GAEG;AAEH,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,QAAQ,gBAAgB,EAAE,CACpC,CAAC;AAExC;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,QAAQ,eAAe,CACjC,CAAC;AAExC;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAAS,MAAM,KAAG,QAAQ,eAAe,CACjC,CAAC;AAEvC;;GAEG;AACH,KAAK,mBAAmB,GAAG,SAAS,GAAG,eAAe,CAAC;AACvD,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,QAAQ,mBAAmB,CACrC,CAAC;AAExC;;GAEG;AACH,eAAO,MAAM,uBAAuB,QAAO,QAAQ,IAAI,CACd,CAAC"}
1
+ {"version":3,"file":"ios.d.ts","sourceRoot":"","sources":["../../src/modules/ios.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,eAAe,EAAE,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAGhE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAGF,eAAO,MAAM,qBAAqB,aACtB,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,6CAO5C,CAAC;AAGF,wBAAgB,YAAY,CAAC,CAAC,SAAS;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAC,EACxD,IAAI,EAAE,OAAO,GACZ,IAAI,IAAI,CAAC,GAAG;IAAC,QAAQ,EAAE,KAAK,CAAA;CAAC,CAO/B;AAGD;;;GAGG;AACH,eAAO,MAAM,IAAI,QAAO,QAAQ,IAAI,CAAyB,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,uBAAuB,YAAa,MAAM,KAAG,QAAQ,OAAO,CACzB,CAAC;AAEjD;;GAEG;AAEH,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,QAAQ,gBAAgB,EAAE,CACpC,CAAC;AAExC;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,QAAQ,eAAe,CACjC,CAAC;AAExC;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAAS,MAAM,KAAG,QAAQ,eAAe,CACjC,CAAC;AAEvC;;GAEG;AACH,KAAK,mBAAmB,GAAG,SAAS,GAAG,eAAe,CAAC;AACvD,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,QAAQ,mBAAmB,CACrC,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,QAAO,QAAQ,IAAI,CACd,CAAC"}
@@ -39,7 +39,10 @@ export const currentEntitlement = (sku) => ExpoIapModule.currentEntitlement(sku)
39
39
  export const latestTransaction = (sku) => ExpoIapModule.latestTransaction(sku);
40
40
  export const beginRefundRequest = (sku) => ExpoIapModule.beginRefundRequest(sku);
41
41
  /**
42
- *
42
+ * Shows the system UI for managing subscriptions.
43
+ * When the user changes subscription renewal status, the system will emit events to
44
+ * purchaseUpdatedListener and transactionUpdatedIos listeners.
45
+ * @returns {Promise<null>}
43
46
  */
44
47
  export const showManageSubscriptions = () => ExpoIapModule.showManageSubscriptions();
45
48
  //# sourceMappingURL=ios.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ios.js","sourceRoot":"","sources":["../../src/modules/ios.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AACtC,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,IAAI,CAAC;AAGrC,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAO7C,YAAY;AACZ,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,QAA2C,EAC3C,EAAE;IACF,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC;AACvE,CAAC,CAAC;AAEF,cAAc;AACd,MAAM,UAAU,YAAY,CAC1B,IAAa;IAEb,OAAO,CACL,IAAI,IAAI,IAAI;QACZ,OAAO,IAAI,KAAK,QAAQ;QACxB,UAAU,IAAI,IAAI;QAClB,IAAI,CAAC,QAAQ,KAAK,KAAK,CACxB,CAAC;AACJ,CAAC;AAED,YAAY;AACZ;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,GAAkB,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;AAE9D;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,OAAe,EAAoB,EAAE,CAC3E,aAAa,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;AAEjD;;GAEG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAA+B,EAAE,CAC7E,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;AAExC;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAA4B,EAAE,CAC1E,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;AAExC;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAAW,EAA4B,EAAE,CACzE,aAAa,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;AAMvC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAgC,EAAE,CAC9E,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;AAExC;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAkB,EAAE,CACzD,aAAa,CAAC,uBAAuB,EAAE,CAAC","sourcesContent":["import {Platform} from 'react-native';\nimport {emitter, IapEvent} from '..';\nimport {ProductPurchase, PurchaseError} from '../ExpoIap.types';\nimport type {ProductStatusIos} from '../types/ExpoIapIos.types';\nimport ExpoIapModule from '../ExpoIapModule';\n\nexport type TransactionEvent = {\n transaction?: ProductPurchase;\n error?: PurchaseError;\n};\n\n// Listeners\nexport const transactionUpdatedIos = (\n listener: (event: TransactionEvent) => void,\n) => {\n if (Platform.OS !== 'ios') {\n throw new Error('This method is only available on iOS');\n }\n\n return emitter.addListener(IapEvent.TransactionIapUpdated, listener);\n};\n\n// Type guards\nexport function isProductIos<T extends {platform?: string}>(\n item: unknown,\n): item is T & {platform: 'ios'} {\n return (\n item != null &&\n typeof item === 'object' &&\n 'platform' in item &&\n item.platform === 'ios'\n );\n}\n\n// Functions\n/**\n * Sync state with Appstore (iOS only)\n * https://developer.apple.com/documentation/storekit/appstore/3791906-sync\n */\nexport const sync = (): Promise<null> => ExpoIapModule.sync();\n\n/**\n *\n */\nexport const isEligibleForIntroOffer = (groupID: string): Promise<boolean> =>\n ExpoIapModule.isEligibleForIntroOffer(groupID);\n\n/**\n *\n */\n\nexport const subscriptionStatus = (sku: string): Promise<ProductStatusIos[]> =>\n ExpoIapModule.subscriptionStatus(sku);\n\n/**\n *\n */\nexport const currentEntitlement = (sku: string): Promise<ProductPurchase> =>\n ExpoIapModule.currentEntitlement(sku);\n\n/**\n *\n */\nexport const latestTransaction = (sku: string): Promise<ProductPurchase> =>\n ExpoIapModule.latestTransaction(sku);\n\n/**\n *\n */\ntype RefundRequestStatus = 'success' | 'userCancelled';\nexport const beginRefundRequest = (sku: string): Promise<RefundRequestStatus> =>\n ExpoIapModule.beginRefundRequest(sku);\n\n/**\n *\n */\nexport const showManageSubscriptions = (): Promise<null> =>\n ExpoIapModule.showManageSubscriptions();\n"]}
1
+ {"version":3,"file":"ios.js","sourceRoot":"","sources":["../../src/modules/ios.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AACtC,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,IAAI,CAAC;AAGrC,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAO7C,YAAY;AACZ,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,QAA2C,EAC3C,EAAE;IACF,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC;AACvE,CAAC,CAAC;AAEF,cAAc;AACd,MAAM,UAAU,YAAY,CAC1B,IAAa;IAEb,OAAO,CACL,IAAI,IAAI,IAAI;QACZ,OAAO,IAAI,KAAK,QAAQ;QACxB,UAAU,IAAI,IAAI;QAClB,IAAI,CAAC,QAAQ,KAAK,KAAK,CACxB,CAAC;AACJ,CAAC;AAED,YAAY;AACZ;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,GAAkB,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;AAE9D;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,OAAe,EAAoB,EAAE,CAC3E,aAAa,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;AAEjD;;GAEG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAA+B,EAAE,CAC7E,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;AAExC;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAA4B,EAAE,CAC1E,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;AAExC;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAAW,EAA4B,EAAE,CACzE,aAAa,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;AAMvC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAgC,EAAE,CAC9E,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;AAExC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAkB,EAAE,CACzD,aAAa,CAAC,uBAAuB,EAAE,CAAC","sourcesContent":["import {Platform} from 'react-native';\nimport {emitter, IapEvent} from '..';\nimport {ProductPurchase, PurchaseError} from '../ExpoIap.types';\nimport type {ProductStatusIos} from '../types/ExpoIapIos.types';\nimport ExpoIapModule from '../ExpoIapModule';\n\nexport type TransactionEvent = {\n transaction?: ProductPurchase;\n error?: PurchaseError;\n};\n\n// Listeners\nexport const transactionUpdatedIos = (\n listener: (event: TransactionEvent) => void,\n) => {\n if (Platform.OS !== 'ios') {\n throw new Error('This method is only available on iOS');\n }\n\n return emitter.addListener(IapEvent.TransactionIapUpdated, listener);\n};\n\n// Type guards\nexport function isProductIos<T extends {platform?: string}>(\n item: unknown,\n): item is T & {platform: 'ios'} {\n return (\n item != null &&\n typeof item === 'object' &&\n 'platform' in item &&\n item.platform === 'ios'\n );\n}\n\n// Functions\n/**\n * Sync state with Appstore (iOS only)\n * https://developer.apple.com/documentation/storekit/appstore/3791906-sync\n */\nexport const sync = (): Promise<null> => ExpoIapModule.sync();\n\n/**\n *\n */\nexport const isEligibleForIntroOffer = (groupID: string): Promise<boolean> =>\n ExpoIapModule.isEligibleForIntroOffer(groupID);\n\n/**\n *\n */\n\nexport const subscriptionStatus = (sku: string): Promise<ProductStatusIos[]> =>\n ExpoIapModule.subscriptionStatus(sku);\n\n/**\n *\n */\nexport const currentEntitlement = (sku: string): Promise<ProductPurchase> =>\n ExpoIapModule.currentEntitlement(sku);\n\n/**\n *\n */\nexport const latestTransaction = (sku: string): Promise<ProductPurchase> =>\n ExpoIapModule.latestTransaction(sku);\n\n/**\n *\n */\ntype RefundRequestStatus = 'success' | 'userCancelled';\nexport const beginRefundRequest = (sku: string): Promise<RefundRequestStatus> =>\n ExpoIapModule.beginRefundRequest(sku);\n\n/**\n * Shows the system UI for managing subscriptions.\n * When the user changes subscription renewal status, the system will emit events to \n * purchaseUpdatedListener and transactionUpdatedIos listeners.\n * @returns {Promise<null>}\n */\nexport const showManageSubscriptions = (): Promise<null> =>\n ExpoIapModule.showManageSubscriptions();\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"useIap.d.ts","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"AAAA,OAAO,EAWL,eAAe,IAAI,uBAAuB,EAC3C,MAAM,IAAI,CAAC;AAEZ,OAAO,EACL,OAAO,EACP,eAAe,EACf,QAAQ,EACR,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAKzB,KAAK,MAAM,GAAG;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,mBAAmB,EAAE,eAAe,EAAE,CAAC;IACvC,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,kBAAkB,EAAE,eAAe,EAAE,CAAC;IACtC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,aAAa,CAAC;IACrC,oBAAoB,EAAE,MAAM,IAAI,CAAC;IACjC,yBAAyB,EAAE,MAAM,IAAI,CAAC;IACtC,iBAAiB,EAAE,CAAC,EAClB,QAAQ,EACR,YAAY,GACb,EAAE;QACD,QAAQ,EAAE,QAAQ,CAAC;QACnB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,KAAK,OAAO,CAAC,MAAM,GAAG,OAAO,GAAG,cAAc,GAAG,IAAI,CAAC,CAAC;IACxD,qBAAqB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,oBAAoB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,eAAe,EAAE,OAAO,uBAAuB,CAAC;CACjD,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,iBAAiB,CAAC,EAAE,CAClB,QAAQ,EAAE,eAAe,GAAG,oBAAoB,KAC7C,IAAI,CAAC;IACV,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAClD;AAED,wBAAgB,MAAM,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAiLtD"}
1
+ {"version":3,"file":"useIap.d.ts","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"AAAA,OAAO,EAWL,eAAe,IAAI,uBAAuB,EAE3C,MAAM,IAAI,CAAC;AAEZ,OAAO,EACL,OAAO,EACP,eAAe,EACf,QAAQ,EACR,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAKzB,KAAK,MAAM,GAAG;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,mBAAmB,EAAE,eAAe,EAAE,CAAC;IACvC,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,kBAAkB,EAAE,eAAe,EAAE,CAAC;IACtC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,aAAa,CAAC;IACrC,oBAAoB,EAAE,MAAM,IAAI,CAAC;IACjC,yBAAyB,EAAE,MAAM,IAAI,CAAC;IACtC,iBAAiB,EAAE,CAAC,EAClB,QAAQ,EACR,YAAY,GACb,EAAE;QACD,QAAQ,EAAE,QAAQ,CAAC;QACnB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,KAAK,OAAO,CAAC,MAAM,GAAG,OAAO,GAAG,cAAc,GAAG,IAAI,CAAC,CAAC;IACxD,qBAAqB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,oBAAoB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,eAAe,EAAE,OAAO,uBAAuB,CAAC;CACjD,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,GAAG,oBAAoB,KAAK,IAAI,CAAC;IAC/E,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAClD;AAED,wBAAgB,MAAM,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAqMtD"}
package/build/useIap.js CHANGED
@@ -1,4 +1,4 @@
1
- import { endConnection, initConnection, purchaseErrorListener, purchaseUpdatedListener, transactionUpdatedIos, getProducts, getAvailablePurchases, getPurchaseHistory, finishTransaction as finishTransactionInternal, getSubscriptions, requestPurchase as requestPurchaseInternal, } from './';
1
+ import { endConnection, initConnection, purchaseErrorListener, purchaseUpdatedListener, transactionUpdatedIos, getProducts, getAvailablePurchases, getPurchaseHistory, finishTransaction as finishTransactionInternal, getSubscriptions, requestPurchase as requestPurchaseInternal, sync, } from './';
2
2
  import { useCallback, useEffect, useState, useRef } from 'react';
3
3
  import { Platform } from 'react-native';
4
4
  export function useIAP(options) {
@@ -51,12 +51,7 @@ export function useIAP(options) {
51
51
  clearCurrentPurchaseError();
52
52
  }
53
53
  }
54
- }, [
55
- currentPurchase?.id,
56
- currentPurchaseError?.productId,
57
- clearCurrentPurchase,
58
- clearCurrentPurchaseError,
59
- ]);
54
+ }, [currentPurchase?.id, currentPurchaseError?.productId, clearCurrentPurchase, clearCurrentPurchaseError]);
60
55
  const requestPurchaseWithReset = useCallback(async (requestObj) => {
61
56
  clearCurrentPurchase();
62
57
  clearCurrentPurchaseError();
@@ -67,6 +62,22 @@ export function useIAP(options) {
67
62
  throw error;
68
63
  }
69
64
  }, [clearCurrentPurchase, clearCurrentPurchaseError]);
65
+ const refreshSubscriptionStatus = useCallback(async (productId) => {
66
+ try {
67
+ if (Platform.OS === 'ios') {
68
+ await sync().catch(() => {
69
+ // Ignore errors as sync might require user password
70
+ });
71
+ }
72
+ if (subscriptions.some(sub => sub.id === productId)) {
73
+ await getSubscriptionsInternal([productId]);
74
+ await getAvailablePurchasesInternal();
75
+ }
76
+ }
77
+ catch (error) {
78
+ console.warn('Failed to refresh subscription status:', error);
79
+ }
80
+ }, [getSubscriptionsInternal, getAvailablePurchasesInternal, subscriptions]);
70
81
  const initIapWithSubscriptions = useCallback(async () => {
71
82
  const result = await initConnection();
72
83
  setConnected(result);
@@ -74,6 +85,9 @@ export function useIAP(options) {
74
85
  subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(async (purchase) => {
75
86
  setCurrentPurchaseError(undefined);
76
87
  setCurrentPurchase(purchase);
88
+ if ('expirationDateIos' in purchase) {
89
+ await refreshSubscriptionStatus(purchase.id);
90
+ }
77
91
  if (optionsRef.current?.onPurchaseSuccess) {
78
92
  optionsRef.current.onPurchaseSuccess(purchase);
79
93
  }
@@ -86,14 +100,17 @@ export function useIAP(options) {
86
100
  }
87
101
  });
88
102
  if (Platform.OS === 'ios') {
89
- subscriptionsRef.current.promotedProductsIos = transactionUpdatedIos((event) => {
90
- setPromotedProductsIOS((prevProducts) => event.transaction
91
- ? [...prevProducts, event.transaction]
92
- : prevProducts);
103
+ subscriptionsRef.current.promotedProductsIos = transactionUpdatedIos(async (event) => {
104
+ if (event.transaction) {
105
+ setPromotedProductsIOS((prevProducts) => [...prevProducts, event.transaction]);
106
+ if ('expirationDateIos' in event.transaction) {
107
+ await refreshSubscriptionStatus(event.transaction.id);
108
+ }
109
+ }
93
110
  });
94
111
  }
95
112
  }
96
- }, []);
113
+ }, [refreshSubscriptionStatus]);
97
114
  useEffect(() => {
98
115
  initIapWithSubscriptions();
99
116
  const currentSubscriptions = subscriptionsRef.current;
@@ -1 +1 @@
1
- {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n transactionUpdatedIos,\n getProducts,\n getAvailablePurchases,\n getPurchaseHistory,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n requestPurchase as requestPurchaseInternal,\n} from './';\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n} from './ExpoIap.types';\nimport {TransactionEvent} from './modules/ios';\nimport {Subscription} from 'expo-modules-core';\nimport {Platform} from 'react-native';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: ProductPurchase[];\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n clearCurrentPurchase: () => void;\n clearCurrentPurchaseError: () => void;\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<string | boolean | PurchaseResult | void>;\n getAvailablePurchases: (skus: string[]) => Promise<void>;\n getPurchaseHistories: (skus: string[]) => Promise<void>;\n getProducts: (skus: string[]) => Promise<void>;\n getSubscriptions: (skus: string[]) => Promise<void>;\n requestPurchase: typeof requestPurchaseInternal;\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (\n purchase: ProductPurchase | SubscriptionPurchase,\n ) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n}\n\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<\n ProductPurchase[]\n >([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: Subscription;\n purchaseError?: Subscription;\n promotedProductsIos?: Subscription;\n }>({});\n\n const clearCurrentPurchase = useCallback(() => {\n setCurrentPurchase(undefined);\n }, []);\n\n const clearCurrentPurchaseError = useCallback(() => {\n setCurrentPurchaseError(undefined);\n }, []);\n\n const getProductsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n setProducts(await getProducts(skus));\n },\n [],\n );\n\n const getSubscriptionsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n setSubscriptions(await getSubscriptions(skus));\n },\n [],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n setAvailablePurchases(await getAvailablePurchases());\n }, []);\n\n const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistory());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n }): Promise<string | boolean | PurchaseResult | void> => {\n try {\n return await finishTransactionInternal({\n purchase,\n isConsumable,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n clearCurrentPurchase();\n }\n if (purchase.id === currentPurchaseError?.productId) {\n clearCurrentPurchaseError();\n }\n }\n },\n [\n currentPurchase?.id,\n currentPurchaseError?.productId,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n ],\n );\n\n const requestPurchaseWithReset = useCallback(\n async (requestObj: Parameters<typeof requestPurchaseInternal>[0]) => {\n clearCurrentPurchase();\n clearCurrentPurchaseError();\n\n try {\n return await requestPurchaseInternal(requestObj);\n } catch (error) {\n throw error;\n }\n },\n [clearCurrentPurchase, clearCurrentPurchaseError],\n );\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n setConnected(result);\n\n if (result) {\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n\n if (optionsRef.current?.onPurchaseSuccess) {\n optionsRef.current.onPurchaseSuccess(purchase);\n }\n },\n );\n\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n\n if (optionsRef.current?.onPurchaseError) {\n optionsRef.current.onPurchaseError(error);\n }\n },\n );\n\n if (Platform.OS === 'ios') {\n subscriptionsRef.current.promotedProductsIos = transactionUpdatedIos(\n (event: TransactionEvent) => {\n setPromotedProductsIOS((prevProducts) =>\n event.transaction\n ? [...prevProducts, event.transaction]\n : prevProducts,\n );\n },\n );\n }\n }\n }, []);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIos?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n getAvailablePurchases: getAvailablePurchasesInternal,\n getPurchaseHistories: getPurchaseHistoriesInternal,\n getProducts: getProductsInternal,\n getSubscriptions: getSubscriptionsInternal,\n requestPurchase: requestPurchaseWithReset,\n };\n}\n"]}
1
+ {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n transactionUpdatedIos,\n getProducts,\n getAvailablePurchases,\n getPurchaseHistory,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n requestPurchase as requestPurchaseInternal,\n sync,\n} from './';\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n} from './ExpoIap.types';\nimport {TransactionEvent} from './modules/ios';\nimport {Subscription} from 'expo-modules-core';\nimport {Platform} from 'react-native';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: ProductPurchase[];\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n clearCurrentPurchase: () => void;\n clearCurrentPurchaseError: () => void;\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<string | boolean | PurchaseResult | void>;\n getAvailablePurchases: (skus: string[]) => Promise<void>;\n getPurchaseHistories: (skus: string[]) => Promise<void>;\n getProducts: (skus: string[]) => Promise<void>;\n getSubscriptions: (skus: string[]) => Promise<void>;\n requestPurchase: typeof requestPurchaseInternal;\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (purchase: ProductPurchase | SubscriptionPurchase) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n}\n\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<\n ProductPurchase[]\n >([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: Subscription;\n purchaseError?: Subscription;\n promotedProductsIos?: Subscription;\n }>({});\n\n const clearCurrentPurchase = useCallback(() => {\n setCurrentPurchase(undefined);\n }, []);\n\n const clearCurrentPurchaseError = useCallback(() => {\n setCurrentPurchaseError(undefined);\n }, []);\n\n const getProductsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n setProducts(await getProducts(skus));\n },\n [],\n );\n\n const getSubscriptionsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n setSubscriptions(await getSubscriptions(skus));\n },\n [],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n setAvailablePurchases(await getAvailablePurchases());\n }, []);\n\n const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistory());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n }): Promise<string | boolean | PurchaseResult | void> => {\n try {\n return await finishTransactionInternal({\n purchase,\n isConsumable,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n clearCurrentPurchase();\n }\n if (purchase.id === currentPurchaseError?.productId) {\n clearCurrentPurchaseError();\n }\n }\n },\n [currentPurchase?.id, currentPurchaseError?.productId, clearCurrentPurchase, clearCurrentPurchaseError],\n );\n\n const requestPurchaseWithReset = useCallback(\n async (requestObj: Parameters<typeof requestPurchaseInternal>[0]) => {\n clearCurrentPurchase();\n clearCurrentPurchaseError();\n \n try {\n return await requestPurchaseInternal(requestObj);\n } catch (error) {\n throw error;\n }\n },\n [clearCurrentPurchase, clearCurrentPurchaseError],\n );\n\n const refreshSubscriptionStatus = useCallback(async (productId: string) => {\n try {\n if (Platform.OS === 'ios') {\n await sync().catch(() => {\n // Ignore errors as sync might require user password\n });\n }\n \n if (subscriptions.some(sub => sub.id === productId)) {\n await getSubscriptionsInternal([productId]);\n await getAvailablePurchasesInternal();\n }\n } catch (error) {\n console.warn('Failed to refresh subscription status:', error);\n }\n }, [getSubscriptionsInternal, getAvailablePurchasesInternal, subscriptions]);\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n setConnected(result);\n\n if (result) {\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n\n if ('expirationDateIos' in purchase) {\n await refreshSubscriptionStatus(purchase.id);\n }\n\n if (optionsRef.current?.onPurchaseSuccess) {\n optionsRef.current.onPurchaseSuccess(purchase);\n }\n },\n );\n\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n\n if (optionsRef.current?.onPurchaseError) {\n optionsRef.current.onPurchaseError(error);\n }\n },\n );\n\n if (Platform.OS === 'ios') {\n subscriptionsRef.current.promotedProductsIos = transactionUpdatedIos(\n async (event: TransactionEvent) => {\n if (event.transaction) {\n setPromotedProductsIOS((prevProducts) => \n [...prevProducts, event.transaction!]\n );\n \n if ('expirationDateIos' in event.transaction) {\n await refreshSubscriptionStatus(event.transaction.id);\n }\n }\n },\n );\n }\n }\n }, [refreshSubscriptionStatus]);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIos?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n getAvailablePurchases: getAvailablePurchasesInternal,\n getPurchaseHistories: getPurchaseHistoriesInternal,\n getProducts: getProductsInternal,\n getSubscriptions: getSubscriptionsInternal,\n requestPurchase: requestPurchaseWithReset,\n };\n}\n"]}
package/iap.md CHANGED
@@ -209,8 +209,8 @@ Transactions map to `Purchase` or `SubscriptionPurchase` with platform-specific
209
209
  Below is a simple example of fetching products and making a purchase with `expo-iap` in a managed workflow, updated to use the new `requestPurchase` signature:
210
210
 
211
211
  ```tsx
212
- import {useEffect, useState} from 'react';
213
- import {Button, Text, View} from 'react-native';
212
+ import { useEffect, useState } from 'react';
213
+ import { Button, Text, View } from 'react-native';
214
214
  import {
215
215
  initConnection,
216
216
  endConnection,
@@ -237,7 +237,7 @@ export default function SimpleIAP() {
237
237
 
238
238
  const purchaseListener = purchaseUpdatedListener(async (purchase) => {
239
239
  if (purchase) {
240
- await finishTransaction({purchase, isConsumable: true});
240
+ await finishTransaction({ purchase, isConsumable: true });
241
241
  alert('Purchase completed!');
242
242
  }
243
243
  });
@@ -252,12 +252,12 @@ export default function SimpleIAP() {
252
252
  const buyItem = async () => {
253
253
  if (!product) return;
254
254
  await requestPurchase({
255
- request: {skus: [product.id]}, // Android expects 'skus'; iOS would use 'sku'
255
+ request: { skus: [product.id] }, // Android expects 'skus'; iOS would use 'sku'
256
256
  });
257
257
  };
258
258
 
259
259
  return (
260
- <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
260
+ <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
261
261
  <Text>{isConnected ? 'Connected' : 'Connecting...'}</Text>
262
262
  {product ? (
263
263
  <>
@@ -277,7 +277,7 @@ export default function SimpleIAP() {
277
277
  The `useIAP` hook simplifies managing in-app purchases. Below is an example updated to use the new `requestPurchase` signature:
278
278
 
279
279
  ```tsx
280
- import {useEffect, useState} from 'react';
280
+ import { useEffect, useState } from 'react';
281
281
  import {
282
282
  SafeAreaView,
283
283
  ScrollView,
@@ -289,20 +289,12 @@ import {
289
289
  InteractionManager,
290
290
  Alert,
291
291
  } from 'react-native';
292
- import {useIAP} from 'expo-iap';
293
- import type {ProductPurchase, SubscriptionProduct} from 'expo-iap';
292
+ import { useIAP } from 'expo-iap';
293
+ import type { ProductPurchase, SubscriptionProduct } from 'expo-iap';
294
294
 
295
295
  // Define SKUs
296
- const productSkus = [
297
- 'cpk.points.1000',
298
- 'cpk.points.5000',
299
- 'cpk.points.10000',
300
- 'cpk.points.30000',
301
- ];
302
- const subscriptionSkus = [
303
- 'cpk.membership.monthly.bronze',
304
- 'cpk.membership.monthly.silver',
305
- ];
296
+ const productSkus = ['cpk.points.1000', 'cpk.points.5000', 'cpk.points.10000', 'cpk.points.30000'];
297
+ const subscriptionSkus = ['cpk.membership.monthly.bronze', 'cpk.membership.monthly.silver'];
306
298
 
307
299
  // Define operations
308
300
  const operations = ['getProducts', 'getSubscriptions'] as const;
@@ -329,10 +321,7 @@ export default function IAPWithHook() {
329
321
 
330
322
  const initializeIAP = async () => {
331
323
  try {
332
- await Promise.all([
333
- getProducts(productSkus),
334
- getSubscriptions(subscriptionSkus),
335
- ]);
324
+ await Promise.all([getProducts(productSkus), getSubscriptions(subscriptionSkus)]);
336
325
  setIsReady(true);
337
326
  } catch (error) {
338
327
  console.error('Error initializing IAP:', error);
@@ -400,10 +389,7 @@ export default function IAPWithHook() {
400
389
  <View style={styles.buttons}>
401
390
  <ScrollView contentContainerStyle={styles.buttonsWrapper} horizontal>
402
391
  {operations.map((operation) => (
403
- <Pressable
404
- key={operation}
405
- onPress={() => handleOperation(operation)}
406
- >
392
+ <Pressable key={operation} onPress={() => handleOperation(operation)}>
407
393
  <View style={styles.buttonView}>
408
394
  <Text>{operation}</Text>
409
395
  </View>
@@ -415,10 +401,10 @@ export default function IAPWithHook() {
415
401
  {!isReady ? (
416
402
  <Text>Loading...</Text>
417
403
  ) : (
418
- <View style={{gap: 12}}>
419
- <Text style={{fontSize: 20}}>Products</Text>
404
+ <View style={{ gap: 12 }}>
405
+ <Text style={{ fontSize: 20 }}>Products</Text>
420
406
  {products.map((item) => (
421
- <View key={item.id} style={{gap: 12}}>
407
+ <View key={item.id} style={{ gap: 12 }}>
422
408
  <Text>
423
409
  {item.title} -{' '}
424
410
  {item.platform === 'android'
@@ -429,24 +415,20 @@ export default function IAPWithHook() {
429
415
  title="Buy"
430
416
  onPress={() =>
431
417
  requestPurchase({
432
- request:
433
- item.platform === 'android'
434
- ? {skus: [item.id]}
435
- : {sku: item.id},
418
+ request: item.platform === 'android' ? { skus: [item.id] } : { sku: item.id },
436
419
  })
437
420
  }
438
421
  />
439
422
  </View>
440
423
  ))}
441
424
 
442
- <Text style={{fontSize: 20}}>Subscriptions</Text>
425
+ <Text style={{ fontSize: 20 }}>Subscriptions</Text>
443
426
  {subscriptions.map((item) => (
444
- <View key={item.id} style={{gap: 12}}>
427
+ <View key={item.id} style={{ gap: 12 }}>
445
428
  <Text>
446
429
  {item.title || item.displayName} -{' '}
447
430
  {item.platform === 'android' && item.subscriptionOfferDetails
448
- ? item.subscriptionOfferDetails[0]?.pricingPhases
449
- .pricingPhaseList[0].formattedPrice
431
+ ? item.subscriptionOfferDetails[0]?.pricingPhases.pricingPhaseList[0].formattedPrice
450
432
  : item.displayPrice}
451
433
  </Text>
452
434
  <Button
@@ -463,7 +445,7 @@ export default function IAPWithHook() {
463
445
  offerToken: offer.offerToken,
464
446
  })) || [],
465
447
  }
466
- : {sku: item.id},
448
+ : { sku: item.id },
467
449
  type: 'subs',
468
450
  })
469
451
  }
@@ -172,6 +172,8 @@ public class ExpoIapModule: Module {
172
172
  private var productStore: ProductStore?
173
173
  private var hasListeners = false
174
174
  private var updateListenerTask: Task<Void, Error>?
175
+ private var subscriptionPollingTask: Task<Void, Error>?
176
+ private var pollingSkus: Set<String> = []
175
177
 
176
178
  public func definition() -> ModuleDefinition {
177
179
  Name("ExpoIap")
@@ -560,7 +562,17 @@ public class ExpoIapModule: Module {
560
562
  "Cannot find window scene or not available on macOS"
561
563
  ])
562
564
  }
565
+
566
+ // Get all subscription products before showing the management UI
567
+ let subscriptionSkus = await self.getAllSubscriptionProductIds()
568
+ self.pollingSkus = Set(subscriptionSkus)
569
+
570
+ // Show the management UI
563
571
  try await AppStore.showManageSubscriptions(in: windowScene)
572
+
573
+ // Start polling for status changes
574
+ self.pollForSubscriptionStatusChanges()
575
+
564
576
  return true
565
577
  #else
566
578
  throw NSError(
@@ -698,4 +710,70 @@ public class ExpoIapModule: Module {
698
710
  return item
699
711
  }
700
712
  }
713
+
714
+ private func getAllSubscriptionProductIds() async -> [String] {
715
+ guard let productStore = self.productStore else { return [] }
716
+ let products = await productStore.getAllProducts()
717
+ return products.compactMap { product in
718
+ if product.subscription != nil {
719
+ return product.id
720
+ }
721
+ return nil
722
+ }
723
+ }
724
+
725
+ private func pollForSubscriptionStatusChanges() {
726
+ subscriptionPollingTask?.cancel()
727
+
728
+ subscriptionPollingTask = Task {
729
+ try? await Task.sleep(nanoseconds: 1_500_000_000) // 1.5 seconds
730
+
731
+ var previousStatuses: [String: Product.SubscriptionInfo.RenewalState] = [:]
732
+
733
+ for sku in self.pollingSkus {
734
+ guard let product = await self.productStore?.getProduct(productID: sku),
735
+ let status = try? await product.subscription?.status.first else { continue }
736
+
737
+ previousStatuses[sku] = status.renewalInfo.verified?.willAutoRenew == true ?
738
+ .willRenew : .willNotRenew
739
+ }
740
+
741
+ for _ in 1...5 {
742
+ try? await Task.sleep(nanoseconds: 2_000_000_000) // 2 seconds
743
+
744
+ if Task.isCancelled {
745
+ return
746
+ }
747
+
748
+ for sku in self.pollingSkus {
749
+ guard let product = await self.productStore?.getProduct(productID: sku),
750
+ let status = try? await product.subscription?.status.first,
751
+ let transaction = await product.latestTransaction?.verified else { continue }
752
+
753
+ let currentRenewalState = status.renewalInfo.verified?.willAutoRenew == true ?
754
+ Product.SubscriptionInfo.RenewalState.willRenew :
755
+ Product.SubscriptionInfo.RenewalState.willNotRenew
756
+
757
+ if let previousState = previousStatuses[sku],
758
+ previousState != currentRenewalState {
759
+
760
+ var purchaseMap = serializeTransaction(transaction)
761
+
762
+ if let renewalInfo = status.renewalInfo.verified {
763
+ if let renewalInfoDict = serializeRenewalInfo(.verified(renewalInfo)) {
764
+ purchaseMap["renewalInfo"] = renewalInfoDict
765
+ }
766
+ }
767
+
768
+ self.sendEvent(IapEvent.PurchaseUpdated, purchaseMap)
769
+ self.sendEvent(IapEvent.TransactionIapUpdated, ["transaction": purchaseMap])
770
+
771
+ previousStatuses[sku] = currentRenewalState
772
+ }
773
+ }
774
+ }
775
+
776
+ self.pollingSkus.removeAll()
777
+ }
778
+ }
701
779
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "2.3.3",
3
+ "version": "2.3.4-rc.1",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -72,7 +72,10 @@ export const beginRefundRequest = (sku: string): Promise<RefundRequestStatus> =>
72
72
  ExpoIapModule.beginRefundRequest(sku);
73
73
 
74
74
  /**
75
- *
75
+ * Shows the system UI for managing subscriptions.
76
+ * When the user changes subscription renewal status, the system will emit events to
77
+ * purchaseUpdatedListener and transactionUpdatedIos listeners.
78
+ * @returns {Promise<null>}
76
79
  */
77
80
  export const showManageSubscriptions = (): Promise<null> =>
78
81
  ExpoIapModule.showManageSubscriptions();
package/src/useIap.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  finishTransaction as finishTransactionInternal,
11
11
  getSubscriptions,
12
12
  requestPurchase as requestPurchaseInternal,
13
+ sync,
13
14
  } from './';
14
15
  import {useCallback, useEffect, useState, useRef} from 'react';
15
16
  import {
@@ -51,9 +52,7 @@ type UseIap = {
51
52
  };
52
53
 
53
54
  export interface UseIAPOptions {
54
- onPurchaseSuccess?: (
55
- purchase: ProductPurchase | SubscriptionPurchase,
56
- ) => void;
55
+ onPurchaseSuccess?: (purchase: ProductPurchase | SubscriptionPurchase) => void;
57
56
  onPurchaseError?: (error: PurchaseError) => void;
58
57
  }
59
58
 
@@ -140,19 +139,14 @@ export function useIAP(options?: UseIAPOptions): UseIap {
140
139
  }
141
140
  }
142
141
  },
143
- [
144
- currentPurchase?.id,
145
- currentPurchaseError?.productId,
146
- clearCurrentPurchase,
147
- clearCurrentPurchaseError,
148
- ],
142
+ [currentPurchase?.id, currentPurchaseError?.productId, clearCurrentPurchase, clearCurrentPurchaseError],
149
143
  );
150
144
 
151
145
  const requestPurchaseWithReset = useCallback(
152
146
  async (requestObj: Parameters<typeof requestPurchaseInternal>[0]) => {
153
147
  clearCurrentPurchase();
154
148
  clearCurrentPurchaseError();
155
-
149
+
156
150
  try {
157
151
  return await requestPurchaseInternal(requestObj);
158
152
  } catch (error) {
@@ -162,6 +156,23 @@ export function useIAP(options?: UseIAPOptions): UseIap {
162
156
  [clearCurrentPurchase, clearCurrentPurchaseError],
163
157
  );
164
158
 
159
+ const refreshSubscriptionStatus = useCallback(async (productId: string) => {
160
+ try {
161
+ if (Platform.OS === 'ios') {
162
+ await sync().catch(() => {
163
+ // Ignore errors as sync might require user password
164
+ });
165
+ }
166
+
167
+ if (subscriptions.some(sub => sub.id === productId)) {
168
+ await getSubscriptionsInternal([productId]);
169
+ await getAvailablePurchasesInternal();
170
+ }
171
+ } catch (error) {
172
+ console.warn('Failed to refresh subscription status:', error);
173
+ }
174
+ }, [getSubscriptionsInternal, getAvailablePurchasesInternal, subscriptions]);
175
+
165
176
  const initIapWithSubscriptions = useCallback(async (): Promise<void> => {
166
177
  const result = await initConnection();
167
178
  setConnected(result);
@@ -172,6 +183,10 @@ export function useIAP(options?: UseIAPOptions): UseIap {
172
183
  setCurrentPurchaseError(undefined);
173
184
  setCurrentPurchase(purchase);
174
185
 
186
+ if ('expirationDateIos' in purchase) {
187
+ await refreshSubscriptionStatus(purchase.id);
188
+ }
189
+
175
190
  if (optionsRef.current?.onPurchaseSuccess) {
176
191
  optionsRef.current.onPurchaseSuccess(purchase);
177
192
  }
@@ -191,17 +206,21 @@ export function useIAP(options?: UseIAPOptions): UseIap {
191
206
 
192
207
  if (Platform.OS === 'ios') {
193
208
  subscriptionsRef.current.promotedProductsIos = transactionUpdatedIos(
194
- (event: TransactionEvent) => {
195
- setPromotedProductsIOS((prevProducts) =>
196
- event.transaction
197
- ? [...prevProducts, event.transaction]
198
- : prevProducts,
199
- );
209
+ async (event: TransactionEvent) => {
210
+ if (event.transaction) {
211
+ setPromotedProductsIOS((prevProducts) =>
212
+ [...prevProducts, event.transaction!]
213
+ );
214
+
215
+ if ('expirationDateIos' in event.transaction) {
216
+ await refreshSubscriptionStatus(event.transaction.id);
217
+ }
218
+ }
200
219
  },
201
220
  );
202
221
  }
203
222
  }
204
- }, []);
223
+ }, [refreshSubscriptionStatus]);
205
224
 
206
225
  useEffect(() => {
207
226
  initIapWithSubscriptions();