expo-iap 2.2.0-rc.3 → 2.2.0-rc.4

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.
Files changed (43) hide show
  1. package/build/ExpoIap.types.d.ts +80 -0
  2. package/build/ExpoIap.types.d.ts.map +1 -0
  3. package/build/ExpoIap.types.js +1 -0
  4. package/build/ExpoIap.types.js.map +1 -0
  5. package/build/ExpoIapModule.d.ts +3 -0
  6. package/build/ExpoIapModule.d.ts.map +1 -0
  7. package/build/ExpoIapModule.js +1 -0
  8. package/build/ExpoIapModule.js.map +1 -0
  9. package/build/index.d.ts +37 -0
  10. package/build/index.d.ts.map +1 -0
  11. package/build/index.js +1 -0
  12. package/build/index.js.map +1 -0
  13. package/build/modules/android.d.ts +48 -0
  14. package/build/modules/android.d.ts.map +1 -0
  15. package/build/modules/android.js +1 -0
  16. package/build/modules/android.js.map +1 -0
  17. package/build/modules/ios.d.ts +49 -0
  18. package/build/modules/ios.d.ts.map +1 -0
  19. package/build/modules/ios.js +1 -0
  20. package/build/modules/ios.js.map +1 -0
  21. package/build/types/ExpoIapAndroid.types.d.ts +113 -0
  22. package/build/types/ExpoIapAndroid.types.d.ts.map +1 -0
  23. package/build/types/ExpoIapAndroid.types.js +1 -0
  24. package/build/types/ExpoIapAndroid.types.js.map +1 -0
  25. package/build/types/ExpoIapIos.types.d.ts +118 -0
  26. package/build/types/ExpoIapIos.types.d.ts.map +1 -0
  27. package/build/types/ExpoIapIos.types.js +1 -0
  28. package/build/types/ExpoIapIos.types.js.map +1 -0
  29. package/build/useIap.d.ts +23 -0
  30. package/build/useIap.d.ts.map +1 -0
  31. package/build/useIap.js +1 -0
  32. package/build/useIap.js.map +1 -0
  33. package/iap.md +16 -0
  34. package/package.json +3 -2
  35. package/tsconfig.json +1 -6
  36. package/build/ExpoIap.types.ts +0 -125
  37. package/build/ExpoIapModule.ts +0 -5
  38. package/build/index.ts +0 -354
  39. package/build/modules/android.ts +0 -99
  40. package/build/modules/ios.ts +0 -84
  41. package/build/types/ExpoIapAndroid.types.ts +0 -127
  42. package/build/types/ExpoIapIos.types.ts +0 -136
  43. package/build/useIap.ts +0 -185
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoIapIos.types.js","sourceRoot":"","sources":["../../src/types/ExpoIapIos.types.ts"],"names":[],"mappings":"","sourcesContent":["import {PurchaseBase, ProductBase} from '../ExpoIap.types';\n\ntype SubscriptionIosPeriod = 'DAY' | 'WEEK' | 'MONTH' | 'YEAR' | '';\ntype PaymentMode = '' | 'FREETRIAL' | 'PAYASYOUGO' | 'PAYUPFRONT';\n\ntype SubscriptionOffer = {\n displayPrice: string;\n id: string;\n paymentMode: PaymentMode;\n period: SubscriptionIosPeriod;\n periodCount: number;\n price: number;\n type: 'introductory' | 'promotional';\n};\n\ntype SubscriptionInfo = {\n introductoryOffer?: SubscriptionOffer;\n promotionalOffers?: SubscriptionOffer[];\n subscriptionGroupID: string;\n subscriptionPeriod: SubscriptionIosPeriod;\n};\n\nexport type ProductIos = ProductBase & {\n displayName: string;\n displayPrice: string;\n isFamilyShareable: boolean;\n jsonRepresentation: string;\n subscription: SubscriptionInfo;\n introductoryPriceNumberOfPeriodsIOS?: string;\n introductoryPriceSubscriptionPeriodIOS?: SubscriptionIosPeriod;\n};\n\nexport type Discount = {\n identifier: string;\n type: string;\n numberOfPeriods: string;\n price: string;\n localizedPrice: string;\n paymentMode: PaymentMode;\n subscriptionPeriod: string;\n};\n\nexport type SubscriptionProductIos = ProductIos & {\n discounts?: Discount[];\n introductoryPrice?: string;\n introductoryPriceAsAmountIOS?: string;\n introductoryPricePaymentModeIOS?: PaymentMode;\n introductoryPriceNumberOfPeriodsIOS?: string;\n introductoryPriceSubscriptionPeriodIOS?: SubscriptionIosPeriod;\n subscriptionPeriodNumberIOS?: string;\n subscriptionPeriodUnitIOS?: SubscriptionIosPeriod;\n};\n\nexport type PaymentDiscount = {\n /**\n * A string used to uniquely identify a discount offer for a product.\n */\n identifier: string;\n /**\n * A string that identifies the key used to generate the signature.\n */\n keyIdentifier: string;\n /**\n * A universally unique ID (UUID) value that you define.\n */\n nonce: string;\n /**\n * A UTF-8 string representing the properties of a specific discount offer, cryptographically signed.\n */\n signature: string;\n /**\n * The date and time of the signature's creation in milliseconds, formatted in Unix epoch time.\n */\n timestamp: number;\n};\n\nexport type RequestPurchaseIosProps = {\n sku: string;\n /**\n * UUID representing user account\n */\n appAccountToken?: string;\n quantity?: number;\n withOffer?: PaymentDiscount;\n};\n\nexport type RequestSubscriptionIosProps = RequestPurchaseIosProps;\n\ntype SubscriptionStatus =\n | 'expired'\n | 'inBillingRetryPeriod'\n | 'inGracePeriod'\n | 'revoked'\n | 'subscribed';\n\ntype RenewalInfo = {\n jsonRepresentation?: string;\n willAutoRenew: boolean;\n autoRenewPreference?: string;\n};\n\nexport type ProductStatusIos = {\n state: SubscriptionStatus;\n renewalInfo?: RenewalInfo;\n};\n\nexport type ProductPurchaseIos = PurchaseBase & {\n // iOS basic fields\n quantityIos?: number;\n originalTransactionDateIos?: number;\n originalTransactionIdentifierIos?: string;\n verificationResultIos?: string;\n appAccountToken?: string;\n // iOS additional fields from StoreKit 2\n expirationDateIos?: number;\n webOrderLineItemIdIos?: number;\n environmentIos?: string;\n storefrontCountryCodeIos?: string;\n appBundleIdIos?: string;\n productTypeIos?: string;\n subscriptionGroupIdIos?: string;\n isUpgradedIos?: boolean;\n ownershipTypeIos?: string;\n reasonIos?: string;\n reasonStringRepresentationIos?: string;\n transactionReasonIos?: 'PURCHASE' | 'RENEWAL' | string;\n revocationDateIos?: number;\n revocationReasonIos?: string;\n offerIos?: {\n id: string;\n type: string;\n paymentMode: string;\n };\n priceIos?: number;\n currencyIos?: string;\n};\n"]}
@@ -0,0 +1,23 @@
1
+ import { Product, ProductPurchase, Purchase, PurchaseError, PurchaseResult, SubscriptionProduct } from './ExpoIap.types';
2
+ type IAP_STATUS = {
3
+ connected: boolean;
4
+ products: Product[];
5
+ promotedProductsIOS: ProductPurchase[];
6
+ subscriptions: SubscriptionProduct[];
7
+ purchaseHistories: ProductPurchase[];
8
+ availablePurchases: ProductPurchase[];
9
+ currentPurchase?: ProductPurchase;
10
+ currentPurchaseError?: PurchaseError;
11
+ finishTransaction: ({ purchase, isConsumable, developerPayloadAndroid, }: {
12
+ purchase: Purchase;
13
+ isConsumable?: boolean;
14
+ developerPayloadAndroid?: string;
15
+ }) => Promise<string | boolean | PurchaseResult | void>;
16
+ getAvailablePurchases: () => Promise<void>;
17
+ getPurchaseHistories: () => Promise<void>;
18
+ getProducts: (skus: string[]) => Promise<void>;
19
+ getSubscriptions: (skus: string[]) => Promise<void>;
20
+ };
21
+ export declare function useIAP(): IAP_STATUS;
22
+ export {};
23
+ //# sourceMappingURL=useIap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIap.d.ts","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,OAAO,EACP,eAAe,EACf,QAAQ,EACR,aAAa,EACb,cAAc,EACd,mBAAmB,EAEpB,MAAM,iBAAiB,CAAC;AAIzB,KAAK,UAAU,GAAG;IAChB,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,iBAAiB,EAAE,CAAC,EAClB,QAAQ,EACR,YAAY,EACZ,uBAAuB,GACxB,EAAE;QACD,QAAQ,EAAE,QAAQ,CAAC;QACnB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,KAAK,OAAO,CAAC,MAAM,GAAG,OAAO,GAAG,cAAc,GAAG,IAAI,CAAC,CAAC;IACxD,qBAAqB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,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;CACrD,CAAC;AAMF,wBAAgB,MAAM,IAAI,UAAU,CAoInC"}
package/build/useIap.js CHANGED
@@ -97,3 +97,4 @@ export function useIAP() {
97
97
  getPurchaseHistories: requestPurchaseHistories,
98
98
  };
99
99
  }
100
+ //# sourceMappingURL=useIap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,WAAW,EACX,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,IAAI,CAAC;AACZ,OAAO,EAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAC;AAqCvD,IAAI,0BAAwC,CAAC;AAC7C,IAAI,yBAAuC,CAAC;AAC5C,IAAI,4BAA0C,CAAC;AAE/C,MAAM,UAAU,MAAM;IACpB,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC3D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAY,EAAE,CAAC,CAAC;IACxD,MAAM,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,GAAG,QAAQ,CAE5D,EAAE,CAAC,CAAC;IACN,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAwB,EAAE,CAAC,CAAC;IAC9E,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CACxD,EAAE,CACH,CAAC;IACF,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAG,QAAQ,CAE1D,EAAE,CAAC,CAAC;IACN,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,EAAmB,CAAC;IAC1E,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GACnD,QAAQ,EAAiB,CAAC;IAE5B,MAAM,eAAe,GAAG,WAAW,CAAC,KAAK,EAAE,IAAc,EAAiB,EAAE;QAC1E,WAAW,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,oBAAoB,GAAG,WAAW,CACtC,KAAK,EAAE,IAAc,EAAiB,EAAE;QACtC,gBAAgB,CAAC,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,yBAAyB,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACtE,qBAAqB,CAAC,MAAM,qBAAqB,EAAE,CAAC,CAAC;IACvD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,wBAAwB,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACrE,oBAAoB,CAAC,MAAM,kBAAkB,EAAE,CAAC,CAAC;IACnD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,iBAAiB,GAAG,WAAW,CACnC,KAAK,EAAE,EACL,QAAQ,EACR,YAAY,EACZ,uBAAuB,GAKxB,EAAqD,EAAE;QACtD,IAAI,CAAC;YACH,OAAO,MAAM,iBAAiB,CAAC;gBAC7B,QAAQ;gBACR,YAAY;gBACZ,uBAAuB;aACxB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,IAAI,QAAQ,CAAC,EAAE,KAAK,eAAe,EAAE,EAAE,EAAE,CAAC;gBACxC,kBAAkB,CAAC,SAAS,CAAC,CAAC;YAChC,CAAC;YAED,IAAI,QAAQ,CAAC,EAAE,KAAK,oBAAoB,EAAE,SAAS,EAAE,CAAC,CAAE,+CAA+C;gBACrG,uBAAuB,CAAC,SAAS,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC,EACD;QACE,eAAe,EAAE,EAAE;QACnB,oBAAoB,EAAE,SAAS;QAC/B,kBAAkB;QAClB,uBAAuB;KACxB,CACF,CAAC;IAEF,MAAM,wBAAwB,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACrE,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;QAEtC,YAAY,CAAC,MAAM,CAAC,CAAC;QAErB,IAAI,MAAM,EAAE,CAAC;YACX,0BAA0B,GAAG,uBAAuB,CAClD,KAAK,EAAE,QAAyC,EAAE,EAAE;gBAClD,uBAAuB,CAAC,SAAS,CAAC,CAAC;gBACnC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YAC/B,CAAC,CACF,CAAC;YAEF,yBAAyB,GAAG,qBAAqB,CAC/C,CAAC,KAAoB,EAAE,EAAE;gBACvB,kBAAkB,CAAC,SAAS,CAAC,CAAC;gBAC9B,uBAAuB,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC,CACF,CAAC;YAEF,4BAA4B,GAAG,qBAAqB,CAClD,CAAC,KAAuB,EAAE,EAAE;gBAC1B,sBAAsB,CAAC,CAAC,YAAY,EAAE,EAAE,CACtC,KAAK,CAAC,WAAW;oBACf,CAAC,CAAC,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC;oBACtC,CAAC,CAAC,YAAY,CACjB,CAAC;YACJ,CAAC,CACF,CAAC;QACJ,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS,CAAC,GAAG,EAAE;QACb,wBAAwB,EAAE,CAAC;QAE3B,OAAO,GAAS,EAAE;YAChB,IAAI,0BAA0B;gBAAE,0BAA0B,CAAC,MAAM,EAAE,CAAC;YACpE,IAAI,yBAAyB;gBAAE,yBAAyB,CAAC,MAAM,EAAE,CAAC;YAClE,IAAI,4BAA4B;gBAAE,4BAA4B,CAAC,MAAM,EAAE,CAAC;YAExE,aAAa,EAAE,CAAC;YAChB,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAE/B,OAAO;QACL,SAAS;QACT,QAAQ;QACR,mBAAmB;QACnB,aAAa;QACb,iBAAiB;QACjB,iBAAiB;QACjB,kBAAkB;QAClB,eAAe;QACf,oBAAoB;QACpB,WAAW,EAAE,eAAe;QAC5B,gBAAgB,EAAE,oBAAoB;QACtC,qBAAqB,EAAE,yBAAyB;QAChD,oBAAoB,EAAE,wBAAwB;KAC/C,CAAC;AACJ,CAAC","sourcesContent":["import {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n transactionUpdatedIos,\n getProducts,\n getAvailablePurchases,\n getPurchaseHistory,\n getSubscriptions,\n} from './';\nimport {useCallback, useEffect, useState} 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';\n\ntype IAP_STATUS = {\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 finishTransaction: ({\n purchase,\n isConsumable,\n developerPayloadAndroid,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n developerPayloadAndroid?: string;\n }) => Promise<string | boolean | PurchaseResult | void>;\n getAvailablePurchases: () => Promise<void>;\n getPurchaseHistories: () => Promise<void>;\n getProducts: (skus: string[]) => Promise<void>;\n getSubscriptions: (skus: string[]) => Promise<void>;\n};\n\nlet purchaseUpdateSubscription: Subscription;\nlet purchaseErrorSubscription: Subscription;\nlet promotedProductsSubscription: Subscription;\n\nexport function useIAP(): IAP_STATUS {\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 requestProducts = useCallback(async (skus: string[]): Promise<void> => {\n setProducts(await getProducts(skus));\n }, []);\n\n const requestSubscriptions = useCallback(\n async (skus: string[]): Promise<void> => {\n setSubscriptions(await getSubscriptions(skus));\n },\n [],\n );\n\n const requestAvailablePurchases = useCallback(async (): Promise<void> => {\n setAvailablePurchases(await getAvailablePurchases());\n }, []);\n\n const requestPurchaseHistories = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistory());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n developerPayloadAndroid,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n developerPayloadAndroid?: string;\n }): Promise<string | boolean | PurchaseResult | void> => {\n try {\n return await finishTransaction({\n purchase,\n isConsumable,\n developerPayloadAndroid,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n setCurrentPurchase(undefined);\n }\n\n if (purchase.id === currentPurchaseError?.productId) { // Note that PurchaseError still uses productId\n setCurrentPurchaseError(undefined);\n }\n }\n },\n [\n currentPurchase?.id,\n currentPurchaseError?.productId,\n setCurrentPurchase,\n setCurrentPurchaseError,\n ],\n );\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n\n setConnected(result);\n\n if (result) {\n purchaseUpdateSubscription = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n },\n );\n\n purchaseErrorSubscription = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n },\n );\n\n promotedProductsSubscription = transactionUpdatedIos(\n (event: TransactionEvent) => {\n setPromotedProductsIOS((prevProducts) =>\n event.transaction\n ? [...prevProducts, event.transaction]\n : prevProducts,\n );\n },\n );\n }\n }, []);\n\n useEffect(() => {\n initIapWithSubscriptions();\n\n return (): void => {\n if (purchaseUpdateSubscription) purchaseUpdateSubscription.remove();\n if (purchaseErrorSubscription) purchaseErrorSubscription.remove();\n if (promotedProductsSubscription) promotedProductsSubscription.remove();\n\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 getProducts: requestProducts,\n getSubscriptions: requestSubscriptions,\n getAvailablePurchases: requestAvailablePurchases,\n getPurchaseHistories: requestPurchaseHistories,\n };\n}\n"]}
package/iap.md CHANGED
@@ -28,6 +28,22 @@ Run `npx pod-install` after installing the npm package. Since `expo-iap` uses `S
28
28
 
29
29
  No additional configuration is required beyond installing the package, as `expo-iap` leverages Google Play Billing internally.
30
30
 
31
+ ### Configure with Expo Config Plugin
32
+
33
+ Add 'expo-iap' to the plugins array in your app.json or app.config.js file:
34
+
35
+ ```json
36
+ {
37
+ "expo": {
38
+ "plugins": [
39
+ "expo-iap"
40
+ ]
41
+ }
42
+ }
43
+ ```
44
+
45
+ This plugin automatically sets up the necessary Android configuration, including adding the BILLING permission to your AndroidManifest.xml.
46
+
31
47
  ## IAP Types
32
48
 
33
49
  `expo-iap` supports the following In-App Purchase types, aligned with platform-specific APIs (Google Play Billing for Android, StoreKit2 for iOS).
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "2.2.0-rc.3",
3
+ "version": "2.2.0-rc.4",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
- "types": "build/index.ts",
6
+ "types": "build/index.d.ts",
7
7
  "scripts": {
8
8
  "build:compile": "expo-module tsc",
9
9
  "build:copy": "mkdir -p build && rsync -a --include '*/' --include '*.ts' --exclude '*' src/ build/",
@@ -16,6 +16,7 @@
16
16
  "lint:tsc": "tsc -p tsconfig.json --noEmit --skipLibCheck",
17
17
  "lint:ci": "yarn lint:tsc && yarn lint:eslint && yarn lint:prettier",
18
18
  "test": "expo-module test",
19
+ "prepare": "expo-module prepare",
19
20
  "expo-module": "expo-module",
20
21
  "open:ios": "xed example/ios",
21
22
  "open:android": "open -a \"Android Studio\" example/android",
package/tsconfig.json CHANGED
@@ -2,12 +2,7 @@
2
2
  {
3
3
  "extends": "expo-module-scripts/tsconfig.base",
4
4
  "compilerOptions": {
5
- "outDir": "./build",
6
- "sourceMap": false,
7
- "declaration": false,
8
- "declarationMap": false,
9
- "inlineSourceMap": false,
10
- "inlineSources": false
5
+ "outDir": "./build"
11
6
  },
12
7
  "include": ["./src"],
13
8
  "exclude": ["**/__mocks__/*", "**/__tests__/*"]
@@ -1,125 +0,0 @@
1
- import {
2
- ProductAndroid,
3
- ProductPurchaseAndroid,
4
- RequestPurchaseAndroidProps,
5
- RequestSubscriptionAndroidProps,
6
- SubscriptionProductAndroid,
7
- } from './types/ExpoIapAndroid.types';
8
- import {
9
- ProductIos,
10
- ProductPurchaseIos,
11
- RequestPurchaseIosProps,
12
- RequestSubscriptionIosProps,
13
- SubscriptionProductIos,
14
- } from './types/ExpoIapIos.types';
15
-
16
- export type ChangeEventPayload = {
17
- value: string;
18
- };
19
-
20
- /**
21
- * Base product type with common properties shared between iOS and Android
22
- */
23
- export type ProductBase = {
24
- id: string;
25
- title: string;
26
- description: string;
27
- type: ProductType;
28
- displayName?: string;
29
- displayPrice?: string;
30
- price?: number;
31
- currency?: string;
32
- };
33
-
34
- // Define literal platform types for better type discrimination
35
- export type IosPlatform = {platform: 'ios'};
36
- export type AndroidPlatform = {platform: 'android'};
37
-
38
- export enum ProductType {
39
- InAppPurchase = 'inapp',
40
- Subscription = 'subs',
41
- }
42
-
43
- // Common base purchase type
44
- export type PurchaseBase = {
45
- id: string;
46
- transactionId?: string;
47
- transactionDate: number;
48
- transactionReceipt: string;
49
- purchaseToken?: string;
50
- };
51
-
52
- // Union type for platform-specific product types with proper discriminators
53
- export type Product =
54
- | (ProductAndroid & AndroidPlatform)
55
- | (ProductIos & IosPlatform);
56
-
57
- // Union type for platform-specific purchase types with proper discriminators
58
- export type ProductPurchase =
59
- | (ProductPurchaseAndroid & AndroidPlatform)
60
- | (ProductPurchaseIos & IosPlatform);
61
-
62
- // Union type for platform-specific subscription purchase types with proper discriminators
63
- export type SubscriptionPurchase =
64
- | (ProductPurchaseAndroid & AndroidPlatform & { autoRenewingAndroid: boolean })
65
- | (ProductPurchaseIos & IosPlatform);
66
-
67
- export type Purchase = ProductPurchase | SubscriptionPurchase;
68
-
69
- export type RequestPurchaseProps =
70
- | RequestPurchaseIosProps
71
- | RequestPurchaseAndroidProps;
72
-
73
- export type SubscriptionProduct =
74
- | (SubscriptionProductAndroid & AndroidPlatform)
75
- | (SubscriptionProductIos & IosPlatform);
76
-
77
- export type RequestSubscriptionProps =
78
- | RequestSubscriptionAndroidProps
79
- | RequestSubscriptionIosProps;
80
-
81
- export type PurchaseResult = {
82
- responseCode?: number;
83
- debugMessage?: string;
84
- code?: string;
85
- message?: string;
86
- purchaseToken?: string;
87
- };
88
-
89
- export enum ErrorCode {
90
- E_UNKNOWN = 'E_UNKNOWN',
91
- E_USER_CANCELLED = 'E_USER_CANCELLED',
92
- E_USER_ERROR = 'E_USER_ERROR',
93
- E_ITEM_UNAVAILABLE = 'E_ITEM_UNAVAILABLE',
94
- E_REMOTE_ERROR = 'E_REMOTE_ERROR',
95
- E_NETWORK_ERROR = 'E_NETWORK_ERROR',
96
- E_SERVICE_ERROR = 'E_SERVICE_ERROR',
97
- E_RECEIPT_FAILED = 'E_RECEIPT_FAILED',
98
- E_RECEIPT_FINISHED_FAILED = 'E_RECEIPT_FINISHED_FAILED',
99
- E_NOT_PREPARED = 'E_NOT_PREPARED',
100
- E_NOT_ENDED = 'E_NOT_ENDED',
101
- E_ALREADY_OWNED = 'E_ALREADY_OWNED',
102
- E_DEVELOPER_ERROR = 'E_DEVELOPER_ERROR',
103
- E_BILLING_RESPONSE_JSON_PARSE_ERROR = 'E_BILLING_RESPONSE_JSON_PARSE_ERROR',
104
- E_DEFERRED_PAYMENT = 'E_DEFERRED_PAYMENT',
105
- E_INTERRUPTED = 'E_INTERRUPTED',
106
- E_IAP_NOT_AVAILABLE = 'E_IAP_NOT_AVAILABLE',
107
- }
108
-
109
- export class PurchaseError implements Error {
110
- constructor(
111
- public name: string,
112
- public message: string,
113
- public responseCode?: number,
114
- public debugMessage?: string,
115
- public code?: ErrorCode,
116
- public productId?: string,
117
- ) {
118
- this.name = '[expo-iap]: PurchaseError';
119
- this.message = message;
120
- this.responseCode = responseCode;
121
- this.debugMessage = debugMessage;
122
- this.code = code;
123
- this.productId = productId;
124
- }
125
- }
@@ -1,5 +0,0 @@
1
- import {requireNativeModule} from 'expo-modules-core';
2
-
3
- // It loads the native module object from the JSI or falls back to
4
- // the bridge module (from NativeModulesProxy) if the remote debugger is on.
5
- export default requireNativeModule('ExpoIap');
package/build/index.ts DELETED
@@ -1,354 +0,0 @@
1
- // Import the native module. On web, it will be resolved to ExpoIap.web.ts
2
- // and on native platforms to ExpoIap.ts
3
- import {NativeModulesProxy, EventEmitter} from 'expo-modules-core';
4
- import {Platform} from 'react-native';
5
- import {
6
- Product,
7
- ProductPurchase,
8
- ProductType,
9
- Purchase,
10
- PurchaseError,
11
- PurchaseResult,
12
- RequestSubscriptionProps,
13
- SubscriptionProduct,
14
- SubscriptionPurchase,
15
- } from './ExpoIap.types';
16
- import ExpoIapModule from './ExpoIapModule';
17
- import {
18
- RequestPurchaseAndroidProps,
19
- RequestSubscriptionAndroidProps,
20
- } from './types/ExpoIapAndroid.types';
21
- import {
22
- PaymentDiscount,
23
- RequestPurchaseIosProps,
24
- RequestSubscriptionIosProps,
25
- } from './types/ExpoIapIos.types';
26
- import {isProductIos, isSubscriptionProductIos} from './modules/ios';
27
- import {
28
- isProductAndroid,
29
- isSubscriptionProductAndroid,
30
- } from './modules/android';
31
-
32
- export * from './modules/android';
33
- export * from './modules/ios';
34
-
35
- // Get the native constant value.
36
- export const PI = ExpoIapModule.PI;
37
-
38
- export enum IapEvent {
39
- PurchaseUpdated = 'purchase-updated',
40
- PurchaseError = 'purchase-error',
41
- TransactionIapUpdated = 'iap-transaction-updated',
42
- }
43
-
44
- export async function setValueAsync(value: string) {
45
- return await ExpoIapModule.setValueAsync(value);
46
- }
47
-
48
- export const emitter = new EventEmitter(
49
- ExpoIapModule ?? NativeModulesProxy.ExpoIap,
50
- );
51
-
52
- export const purchaseUpdatedListener = (
53
- listener: (event: Purchase) => void,
54
- ) => {
55
- const emitterSubscription = emitter.addListener(
56
- IapEvent.PurchaseUpdated,
57
- listener,
58
- );
59
- return emitterSubscription;
60
- };
61
-
62
- export const purchaseErrorListener = (
63
- listener: (error: PurchaseError) => void,
64
- ) => {
65
- return emitter.addListener<PurchaseError>(IapEvent.PurchaseError, listener);
66
- };
67
-
68
- export function initConnection() {
69
- return ExpoIapModule.initConnection();
70
- }
71
-
72
- export const getProducts = async (skus: string[]): Promise<Product[]> => {
73
- console.log('getProducts', skus);
74
- if (!skus?.length) {
75
- return Promise.reject(new Error('"skus" is required'));
76
- }
77
-
78
- return Platform.select({
79
- ios: async () => {
80
- const items = await ExpoIapModule.getItems(skus);
81
- console.log('items', items);
82
- return items.filter((item: unknown) => isProductIos<Product>(item));
83
- },
84
- android: async () => {
85
- const products = await ExpoIapModule.getItemsByType(
86
- ProductType.InAppPurchase,
87
- skus,
88
- );
89
- return products.filter((product: unknown) =>
90
- isProductAndroid<Product>(product),
91
- );
92
- },
93
- default: () => Promise.reject(new Error('Unsupported Platform')),
94
- })();
95
- };
96
-
97
- export const getSubscriptions = async (
98
- skus: string[],
99
- ): Promise<SubscriptionProduct[]> => {
100
- if (!skus?.length) {
101
- return Promise.reject(new Error('"skus" is required'));
102
- }
103
-
104
- return Platform.select({
105
- ios: async () => {
106
- const rawItems = await ExpoIapModule.getItems(skus);
107
-
108
- return rawItems.filter((item: unknown) => {
109
- if (!isSubscriptionProductIos(item)) return false;
110
- return (
111
- typeof item === 'object' &&
112
- item !== null &&
113
- 'id' in item &&
114
- typeof item.id === 'string' &&
115
- skus.includes(item.id)
116
- );
117
- }) as SubscriptionProduct[];
118
- },
119
- android: async () => {
120
- const rawItems = await ExpoIapModule.getItemsByType('subs', skus);
121
-
122
- return rawItems.filter((item: unknown) => {
123
- if (!isSubscriptionProductAndroid(item)) return false;
124
- return (
125
- typeof item === 'object' &&
126
- item !== null &&
127
- 'id' in item &&
128
- typeof item.id === 'string' &&
129
- skus.includes(item.id)
130
- );
131
- }) as SubscriptionProduct[];
132
- },
133
- default: () => Promise.reject(new Error('Unsupported Platform')),
134
- })();
135
- };
136
-
137
- export async function endConnection(): Promise<boolean> {
138
- return ExpoIapModule.endConnection();
139
- }
140
-
141
- export const getPurchaseHistory = ({
142
- alsoPublishToEventListener = false,
143
- onlyIncludeActiveItems = false,
144
- }: {
145
- alsoPublishToEventListener?: boolean;
146
- onlyIncludeActiveItems?: boolean;
147
- } = {}): Promise<ProductPurchase[]> =>
148
- (
149
- Platform.select({
150
- ios: async () => {
151
- return ExpoIapModule.getAvailableItems(
152
- alsoPublishToEventListener,
153
- onlyIncludeActiveItems,
154
- );
155
- },
156
- android: async () => {
157
- const products = await ExpoIapModule.getPurchaseHistoryByType(
158
- ProductType.InAppPurchase,
159
- );
160
-
161
- const subscriptions = await ExpoIapModule.getPurchaseHistoryByType(
162
- ProductType.Subscription,
163
- );
164
-
165
- return products.concat(subscriptions);
166
- },
167
- }) || (() => Promise.resolve([]))
168
- )();
169
-
170
- export const getAvailablePurchases = ({
171
- alsoPublishToEventListener = false,
172
- onlyIncludeActiveItems = true,
173
- }: {
174
- alsoPublishToEventListener?: boolean;
175
- onlyIncludeActiveItems?: boolean;
176
- } = {}): Promise<ProductPurchase[]> =>
177
- (
178
- Platform.select({
179
- ios: () =>
180
- ExpoIapModule.getAvailableItems(
181
- alsoPublishToEventListener,
182
- onlyIncludeActiveItems,
183
- ),
184
- android: async () => {
185
- const products = await ExpoIapModule.getAvailableItemsByType(
186
- ProductType.InAppPurchase,
187
- );
188
-
189
- const subscriptions = await ExpoIapModule.getAvailableItemsByType(
190
- ProductType.Subscription,
191
- );
192
-
193
- return products.concat(subscriptions);
194
- },
195
- }) || (() => Promise.resolve([]))
196
- )();
197
-
198
- const offerToRecordIos = (
199
- offer: PaymentDiscount | undefined,
200
- ): Record<keyof PaymentDiscount, string> | undefined => {
201
- if (!offer) {
202
- return undefined;
203
- }
204
- return {
205
- identifier: offer.identifier,
206
- keyIdentifier: offer.keyIdentifier,
207
- nonce: offer.nonce,
208
- signature: offer.signature,
209
- timestamp: offer.timestamp.toString(),
210
- };
211
- };
212
-
213
- export const requestPurchase = (
214
- request: RequestPurchaseIosProps | RequestPurchaseAndroidProps,
215
- ): Promise<ProductPurchase | ProductPurchase[] | void> =>
216
- (
217
- Platform.select({
218
- ios: async () => {
219
- if (!('sku' in request)) {
220
- throw new Error('sku is required for iOS purchase');
221
- }
222
-
223
- const {sku, appAccountToken, quantity, withOffer} = request;
224
-
225
- const offer = offerToRecordIos(withOffer);
226
-
227
- const purchase = await ExpoIapModule.buyProduct(
228
- sku,
229
- appAccountToken,
230
- quantity ?? -1,
231
- offer,
232
- );
233
-
234
- return Promise.resolve(purchase);
235
- },
236
- android: async () => {
237
- if (!('skus' in request) || !request.skus.length) {
238
- throw new Error('skus is required for Android purchase');
239
- }
240
-
241
- const {
242
- skus,
243
- obfuscatedAccountIdAndroid,
244
- obfuscatedProfileIdAndroid,
245
- isOfferPersonalized,
246
- } = request;
247
-
248
- return ExpoIapModule.buyItemByType({
249
- type: ProductType.InAppPurchase,
250
- skuArr: skus,
251
- purchaseToken: undefined,
252
- replacementMode: -1,
253
- obfuscatedAccountId: obfuscatedAccountIdAndroid,
254
- obfuscatedProfileId: obfuscatedProfileIdAndroid,
255
- offerTokenArr: [],
256
- isOfferPersonalized: isOfferPersonalized ?? false,
257
- });
258
- },
259
- }) || Promise.resolve
260
- )();
261
-
262
- export const requestSubscription = (
263
- request: RequestSubscriptionProps,
264
- ): Promise<SubscriptionPurchase | SubscriptionPurchase[] | null | void> =>
265
- (
266
- Platform.select({
267
- ios: async () => {
268
- if (!('sku' in request)) {
269
- throw new Error('sku is required for iOS subscriptions');
270
- }
271
-
272
- const {sku, appAccountToken, quantity, withOffer} =
273
- request as RequestSubscriptionIosProps;
274
-
275
- const offer = offerToRecordIos(withOffer);
276
-
277
- const purchase = await ExpoIapModule.buyProduct(
278
- sku,
279
- appAccountToken,
280
- quantity ?? -1,
281
- offer,
282
- );
283
-
284
- return Promise.resolve(purchase as SubscriptionPurchase);
285
- },
286
- android: async () => {
287
- const {
288
- skus,
289
- isOfferPersonalized,
290
- obfuscatedAccountIdAndroid,
291
- obfuscatedProfileIdAndroid,
292
- subscriptionOffers,
293
- replacementModeAndroid,
294
- purchaseTokenAndroid,
295
- } = request as RequestSubscriptionAndroidProps;
296
-
297
- return ExpoIapModule.buyItemByType({
298
- type: ProductType.Subscription,
299
- skuArr: skus.map((so) => so),
300
- purchaseToken: purchaseTokenAndroid,
301
- replacementMode: replacementModeAndroid,
302
- obfuscatedAccountId: obfuscatedAccountIdAndroid,
303
- obfuscatedProfileId: obfuscatedProfileIdAndroid,
304
- offerTokenArr: subscriptionOffers.map((so) => so.offerToken),
305
- isOfferPersonalized: isOfferPersonalized ?? false,
306
- });
307
- },
308
- }) || (() => Promise.resolve(null))
309
- )();
310
-
311
- export const finishTransaction = ({
312
- purchase,
313
- isConsumable,
314
- developerPayloadAndroid,
315
- }: {
316
- purchase: Purchase;
317
- isConsumable?: boolean;
318
- developerPayloadAndroid?: string;
319
- }): Promise<PurchaseResult | boolean> => {
320
- return (
321
- Platform.select({
322
- ios: async () => {
323
- const transactionId = purchase.transactionId;
324
-
325
- if (!transactionId) {
326
- return Promise.reject(
327
- new Error('transactionId required to finish iOS transaction'),
328
- );
329
- }
330
- await ExpoIapModule.finishTransaction(transactionId);
331
- return Promise.resolve(true);
332
- },
333
- android: async () => {
334
- if (purchase?.purchaseToken) {
335
- if (!isConsumable) {
336
- return Promise.reject(
337
- new Error('purchase is not suitable to be purchased'),
338
- );
339
- }
340
-
341
- return ExpoIapModule.consumeProduct(
342
- purchase.purchaseToken,
343
- developerPayloadAndroid,
344
- );
345
- }
346
- return Promise.reject(
347
- new Error('purchase is not suitable to be purchased'),
348
- );
349
- },
350
- }) || (() => Promise.reject(new Error('Unsupported Platform')))
351
- )();
352
- };
353
-
354
- export * from './useIap';