react-native-iap 14.4.10 → 14.4.12
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.
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +195 -12
- package/ios/HybridRnIap.swift +115 -2
- package/ios/RnIapHelper.swift +32 -1
- package/lib/module/hooks/useIAP.js +24 -3
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js +275 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js +18 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/type-bridge.js +39 -1
- package/lib/module/utils/type-bridge.js.map +1 -1
- package/lib/typescript/plugin/src/withIAP.d.ts +27 -0
- package/lib/typescript/plugin/src/withIAP.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts +6 -1
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +133 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +101 -2
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +116 -13
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
- package/nitrogen/generated/android/NitroIapOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JAlternativeBillingModeAndroid.hpp +62 -0
- package/nitrogen/generated/android/c++/JExternalPurchaseLinkResultIOS.hpp +58 -0
- package/nitrogen/generated/android/c++/JExternalPurchaseNoticeAction.hpp +59 -0
- package/nitrogen/generated/android/c++/JExternalPurchaseNoticeResultIOS.hpp +60 -0
- package/nitrogen/generated/android/c++/JFunc_void_NitroProduct.hpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_UserChoiceBillingDetails.hpp +78 -0
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +136 -3
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +9 -1
- package/nitrogen/generated/android/c++/JInitConnectionConfig.hpp +55 -0
- package/nitrogen/generated/android/c++/JNitroOneTimePurchaseOfferDetail.hpp +61 -0
- package/nitrogen/generated/android/c++/JNitroProduct.hpp +16 -2
- package/nitrogen/generated/android/c++/JNitroPurchase.hpp +74 -2
- package/nitrogen/generated/android/c++/JPurchaseAndroid.hpp +4 -0
- package/nitrogen/generated/android/c++/JPurchaseIOS.hpp +4 -0
- package/nitrogen/generated/android/c++/JUserChoiceBillingDetails.hpp +75 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/AlternativeBillingModeAndroid.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/ExternalPurchaseLinkResultIOS.kt +32 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/ExternalPurchaseNoticeAction.kt +21 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/ExternalPurchaseNoticeResultIOS.kt +32 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Func_void_UserChoiceBillingDetails.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +43 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/InitConnectionConfig.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroOneTimePurchaseOfferDetail.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroProduct.kt +10 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchase.kt +55 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/PurchaseAndroid.kt +3 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/PurchaseIOS.kt +3 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/UserChoiceBillingDetails.kt +32 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.cpp +24 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +174 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +21 -0
- package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +84 -3
- package/nitrogen/generated/ios/swift/AlternativeBillingModeAndroid.swift +44 -0
- package/nitrogen/generated/ios/swift/ExternalPurchaseLinkResultIOS.swift +65 -0
- package/nitrogen/generated/ios/swift/ExternalPurchaseNoticeAction.swift +40 -0
- package/nitrogen/generated/ios/swift/ExternalPurchaseNoticeResultIOS.swift +65 -0
- package/nitrogen/generated/ios/swift/Func_void_ExternalPurchaseLinkResultIOS.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_ExternalPurchaseNoticeResultIOS.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_UserChoiceBillingDetails.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +9 -1
- package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +168 -2
- package/nitrogen/generated/ios/swift/InitConnectionConfig.swift +47 -0
- package/nitrogen/generated/ios/swift/NitroOneTimePurchaseOfferDetail.swift +57 -0
- package/nitrogen/generated/ios/swift/NitroProduct.swift +91 -1
- package/nitrogen/generated/ios/swift/NitroPurchase.swift +520 -1
- package/nitrogen/generated/ios/swift/PurchaseAndroid.swift +31 -1
- package/nitrogen/generated/ios/swift/PurchaseIOS.swift +31 -1
- package/nitrogen/generated/ios/swift/UserChoiceBillingDetails.swift +58 -0
- package/nitrogen/generated/shared/c++/AlternativeBillingModeAndroid.hpp +80 -0
- package/nitrogen/generated/shared/c++/ExternalPurchaseLinkResultIOS.hpp +72 -0
- package/nitrogen/generated/shared/c++/ExternalPurchaseNoticeAction.hpp +76 -0
- package/nitrogen/generated/shared/c++/ExternalPurchaseNoticeResultIOS.hpp +74 -0
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.cpp +8 -0
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +22 -2
- package/nitrogen/generated/shared/c++/InitConnectionConfig.hpp +69 -0
- package/nitrogen/generated/shared/c++/NitroOneTimePurchaseOfferDetail.hpp +75 -0
- package/nitrogen/generated/shared/c++/NitroProduct.hpp +17 -2
- package/nitrogen/generated/shared/c++/NitroPurchase.hpp +74 -2
- package/nitrogen/generated/shared/c++/PurchaseAndroid.hpp +5 -1
- package/nitrogen/generated/shared/c++/PurchaseIOS.hpp +5 -1
- package/nitrogen/generated/shared/c++/UserChoiceBillingDetails.hpp +72 -0
- package/openiap-versions.json +3 -3
- package/package.json +3 -3
- package/plugin/build/withIAP.d.ts +27 -0
- package/plugin/build/withIAP.js +91 -1
- package/plugin/src/withIAP.ts +162 -0
- package/src/hooks/useIAP.ts +47 -1
- package/src/index.ts +313 -2
- package/src/specs/RnIap.nitro.ts +131 -1
- package/src/types.ts +124 -13
- package/src/utils/type-bridge.ts +76 -16
package/src/index.ts
CHANGED
|
@@ -292,6 +292,82 @@ export const promotedProductListenerIOS = (
|
|
|
292
292
|
};
|
|
293
293
|
};
|
|
294
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Add a listener for user choice billing events (Android only).
|
|
297
|
+
* Fires when a user selects alternative billing in the User Choice Billing dialog.
|
|
298
|
+
*
|
|
299
|
+
* @param listener - Function to call when user chooses alternative billing
|
|
300
|
+
* @returns EventSubscription with remove() method to unsubscribe
|
|
301
|
+
* @platform Android
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* ```typescript
|
|
305
|
+
* const subscription = userChoiceBillingListenerAndroid((details) => {
|
|
306
|
+
* console.log('User chose alternative billing');
|
|
307
|
+
* console.log('Products:', details.products);
|
|
308
|
+
* console.log('Token:', details.externalTransactionToken);
|
|
309
|
+
*
|
|
310
|
+
* // Send token to backend for Google Play reporting
|
|
311
|
+
* await reportToGooglePlay(details.externalTransactionToken);
|
|
312
|
+
* });
|
|
313
|
+
*
|
|
314
|
+
* // Later, remove the listener
|
|
315
|
+
* subscription.remove();
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
type NitroUserChoiceBillingListener = Parameters<
|
|
319
|
+
RnIap['addUserChoiceBillingListenerAndroid']
|
|
320
|
+
>[0];
|
|
321
|
+
const userChoiceBillingListenerMap = new WeakMap<
|
|
322
|
+
(details: any) => void,
|
|
323
|
+
NitroUserChoiceBillingListener
|
|
324
|
+
>();
|
|
325
|
+
|
|
326
|
+
export const userChoiceBillingListenerAndroid = (
|
|
327
|
+
listener: (details: any) => void,
|
|
328
|
+
): EventSubscription => {
|
|
329
|
+
if (Platform.OS !== 'android') {
|
|
330
|
+
RnIapConsole.warn(
|
|
331
|
+
'userChoiceBillingListenerAndroid: This listener is only available on Android',
|
|
332
|
+
);
|
|
333
|
+
return {remove: () => {}};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const wrappedListener: NitroUserChoiceBillingListener = (details) => {
|
|
337
|
+
listener(details);
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
userChoiceBillingListenerMap.set(listener, wrappedListener);
|
|
341
|
+
let attached = false;
|
|
342
|
+
try {
|
|
343
|
+
IAP.instance.addUserChoiceBillingListenerAndroid(wrappedListener);
|
|
344
|
+
attached = true;
|
|
345
|
+
} catch (e) {
|
|
346
|
+
const msg = toErrorMessage(e);
|
|
347
|
+
if (msg.includes('Nitro runtime not installed')) {
|
|
348
|
+
RnIapConsole.warn(
|
|
349
|
+
'[userChoiceBillingListenerAndroid] Nitro not ready yet; listener inert until initConnection()',
|
|
350
|
+
);
|
|
351
|
+
} else {
|
|
352
|
+
throw e;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
remove: () => {
|
|
358
|
+
const wrapped = userChoiceBillingListenerMap.get(listener);
|
|
359
|
+
if (wrapped) {
|
|
360
|
+
if (attached) {
|
|
361
|
+
try {
|
|
362
|
+
IAP.instance.removeUserChoiceBillingListenerAndroid(wrapped);
|
|
363
|
+
} catch {}
|
|
364
|
+
}
|
|
365
|
+
userChoiceBillingListenerMap.delete(listener);
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
|
|
295
371
|
// ------------------------------
|
|
296
372
|
// Query API
|
|
297
373
|
// ------------------------------
|
|
@@ -832,10 +908,32 @@ export const getTransactionJwsIOS: QueryField<'getTransactionJwsIOS'> = async (
|
|
|
832
908
|
|
|
833
909
|
/**
|
|
834
910
|
* Initialize connection to the store
|
|
911
|
+
* @param config - Optional configuration including alternative billing mode for Android
|
|
912
|
+
* @param config.alternativeBillingModeAndroid - Alternative billing mode: 'none', 'user-choice', or 'alternative-only'
|
|
913
|
+
*
|
|
914
|
+
* @example
|
|
915
|
+
* ```typescript
|
|
916
|
+
* // Standard billing (default)
|
|
917
|
+
* await initConnection();
|
|
918
|
+
*
|
|
919
|
+
* // User choice billing (Android)
|
|
920
|
+
* await initConnection({
|
|
921
|
+
* alternativeBillingModeAndroid: 'user-choice'
|
|
922
|
+
* });
|
|
923
|
+
*
|
|
924
|
+
* // Alternative billing only (Android)
|
|
925
|
+
* await initConnection({
|
|
926
|
+
* alternativeBillingModeAndroid: 'alternative-only'
|
|
927
|
+
* });
|
|
928
|
+
* ```
|
|
835
929
|
*/
|
|
836
|
-
export const initConnection: MutationField<'initConnection'> = async (
|
|
930
|
+
export const initConnection: MutationField<'initConnection'> = async (
|
|
931
|
+
config,
|
|
932
|
+
) => {
|
|
837
933
|
try {
|
|
838
|
-
return await IAP.instance.initConnection(
|
|
934
|
+
return await IAP.instance.initConnection(
|
|
935
|
+
config as Record<string, unknown> | undefined,
|
|
936
|
+
);
|
|
839
937
|
} catch (error) {
|
|
840
938
|
RnIapConsole.error('Failed to initialize IAP connection:', error);
|
|
841
939
|
throw error;
|
|
@@ -1648,3 +1746,216 @@ const normalizeProductQueryType = (
|
|
|
1648
1746
|
}
|
|
1649
1747
|
return 'in-app';
|
|
1650
1748
|
};
|
|
1749
|
+
|
|
1750
|
+
// ============================================================================
|
|
1751
|
+
// ALTERNATIVE BILLING APIs
|
|
1752
|
+
// ============================================================================
|
|
1753
|
+
|
|
1754
|
+
// ------------------------------
|
|
1755
|
+
// Android Alternative Billing
|
|
1756
|
+
// ------------------------------
|
|
1757
|
+
|
|
1758
|
+
/**
|
|
1759
|
+
* Check if alternative billing is available for this user/device (Android only).
|
|
1760
|
+
* Step 1 of alternative billing flow.
|
|
1761
|
+
*
|
|
1762
|
+
* @returns Promise<boolean> - true if available, false otherwise
|
|
1763
|
+
* @throws Error if billing client not ready
|
|
1764
|
+
* @platform Android
|
|
1765
|
+
*
|
|
1766
|
+
* @example
|
|
1767
|
+
* ```typescript
|
|
1768
|
+
* const isAvailable = await checkAlternativeBillingAvailabilityAndroid();
|
|
1769
|
+
* if (isAvailable) {
|
|
1770
|
+
* // Proceed with alternative billing flow
|
|
1771
|
+
* }
|
|
1772
|
+
* ```
|
|
1773
|
+
*/
|
|
1774
|
+
export const checkAlternativeBillingAvailabilityAndroid: MutationField<
|
|
1775
|
+
'checkAlternativeBillingAvailabilityAndroid'
|
|
1776
|
+
> = async () => {
|
|
1777
|
+
if (Platform.OS !== 'android') {
|
|
1778
|
+
throw new Error('Alternative billing is only supported on Android');
|
|
1779
|
+
}
|
|
1780
|
+
try {
|
|
1781
|
+
return await IAP.instance.checkAlternativeBillingAvailabilityAndroid();
|
|
1782
|
+
} catch (error) {
|
|
1783
|
+
RnIapConsole.error(
|
|
1784
|
+
'Failed to check alternative billing availability:',
|
|
1785
|
+
error,
|
|
1786
|
+
);
|
|
1787
|
+
throw error;
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
|
|
1791
|
+
/**
|
|
1792
|
+
* Show alternative billing information dialog to user (Android only).
|
|
1793
|
+
* Step 2 of alternative billing flow.
|
|
1794
|
+
* Must be called BEFORE processing payment in your payment system.
|
|
1795
|
+
*
|
|
1796
|
+
* @returns Promise<boolean> - true if user accepted, false if user canceled
|
|
1797
|
+
* @throws Error if billing client not ready
|
|
1798
|
+
* @platform Android
|
|
1799
|
+
*
|
|
1800
|
+
* @example
|
|
1801
|
+
* ```typescript
|
|
1802
|
+
* const userAccepted = await showAlternativeBillingDialogAndroid();
|
|
1803
|
+
* if (userAccepted) {
|
|
1804
|
+
* // Process payment in your payment system
|
|
1805
|
+
* const success = await processCustomPayment();
|
|
1806
|
+
* if (success) {
|
|
1807
|
+
* // Create reporting token
|
|
1808
|
+
* const token = await createAlternativeBillingTokenAndroid();
|
|
1809
|
+
* // Send token to your backend for Google Play reporting
|
|
1810
|
+
* }
|
|
1811
|
+
* }
|
|
1812
|
+
* ```
|
|
1813
|
+
*/
|
|
1814
|
+
export const showAlternativeBillingDialogAndroid: MutationField<
|
|
1815
|
+
'showAlternativeBillingDialogAndroid'
|
|
1816
|
+
> = async () => {
|
|
1817
|
+
if (Platform.OS !== 'android') {
|
|
1818
|
+
throw new Error('Alternative billing is only supported on Android');
|
|
1819
|
+
}
|
|
1820
|
+
try {
|
|
1821
|
+
return await IAP.instance.showAlternativeBillingDialogAndroid();
|
|
1822
|
+
} catch (error) {
|
|
1823
|
+
RnIapConsole.error('Failed to show alternative billing dialog:', error);
|
|
1824
|
+
throw error;
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
|
|
1828
|
+
/**
|
|
1829
|
+
* Create external transaction token for Google Play reporting (Android only).
|
|
1830
|
+
* Step 3 of alternative billing flow.
|
|
1831
|
+
* Must be called AFTER successful payment in your payment system.
|
|
1832
|
+
* Token must be reported to Google Play backend within 24 hours.
|
|
1833
|
+
*
|
|
1834
|
+
* @param sku - Optional product SKU that was purchased
|
|
1835
|
+
* @returns Promise<string | null> - Token string or null if creation failed
|
|
1836
|
+
* @throws Error if billing client not ready
|
|
1837
|
+
* @platform Android
|
|
1838
|
+
*
|
|
1839
|
+
* @example
|
|
1840
|
+
* ```typescript
|
|
1841
|
+
* const token = await createAlternativeBillingTokenAndroid('premium_subscription');
|
|
1842
|
+
* if (token) {
|
|
1843
|
+
* // Send token to your backend
|
|
1844
|
+
* await fetch('/api/report-transaction', {
|
|
1845
|
+
* method: 'POST',
|
|
1846
|
+
* body: JSON.stringify({ token, sku: 'premium_subscription' })
|
|
1847
|
+
* });
|
|
1848
|
+
* }
|
|
1849
|
+
* ```
|
|
1850
|
+
*/
|
|
1851
|
+
export const createAlternativeBillingTokenAndroid: MutationField<
|
|
1852
|
+
'createAlternativeBillingTokenAndroid'
|
|
1853
|
+
> = async (sku?: string) => {
|
|
1854
|
+
if (Platform.OS !== 'android') {
|
|
1855
|
+
throw new Error('Alternative billing is only supported on Android');
|
|
1856
|
+
}
|
|
1857
|
+
try {
|
|
1858
|
+
return await IAP.instance.createAlternativeBillingTokenAndroid(sku ?? null);
|
|
1859
|
+
} catch (error) {
|
|
1860
|
+
RnIapConsole.error('Failed to create alternative billing token:', error);
|
|
1861
|
+
throw error;
|
|
1862
|
+
}
|
|
1863
|
+
};
|
|
1864
|
+
|
|
1865
|
+
// ------------------------------
|
|
1866
|
+
// iOS External Purchase
|
|
1867
|
+
// ------------------------------
|
|
1868
|
+
|
|
1869
|
+
/**
|
|
1870
|
+
* Check if the device can present an external purchase notice sheet (iOS 18.2+).
|
|
1871
|
+
*
|
|
1872
|
+
* @returns Promise<boolean> - true if notice sheet can be presented
|
|
1873
|
+
* @platform iOS
|
|
1874
|
+
*
|
|
1875
|
+
* @example
|
|
1876
|
+
* ```typescript
|
|
1877
|
+
* const canPresent = await canPresentExternalPurchaseNoticeIOS();
|
|
1878
|
+
* if (canPresent) {
|
|
1879
|
+
* // Present notice before external purchase
|
|
1880
|
+
* const result = await presentExternalPurchaseNoticeSheetIOS();
|
|
1881
|
+
* }
|
|
1882
|
+
* ```
|
|
1883
|
+
*/
|
|
1884
|
+
export const canPresentExternalPurchaseNoticeIOS: QueryField<
|
|
1885
|
+
'canPresentExternalPurchaseNoticeIOS'
|
|
1886
|
+
> = async () => {
|
|
1887
|
+
if (Platform.OS !== 'ios') {
|
|
1888
|
+
return false;
|
|
1889
|
+
}
|
|
1890
|
+
try {
|
|
1891
|
+
return await IAP.instance.canPresentExternalPurchaseNoticeIOS();
|
|
1892
|
+
} catch (error) {
|
|
1893
|
+
RnIapConsole.error(
|
|
1894
|
+
'Failed to check external purchase notice availability:',
|
|
1895
|
+
error,
|
|
1896
|
+
);
|
|
1897
|
+
return false;
|
|
1898
|
+
}
|
|
1899
|
+
};
|
|
1900
|
+
|
|
1901
|
+
/**
|
|
1902
|
+
* Present an external purchase notice sheet to inform users about external purchases (iOS 18.2+).
|
|
1903
|
+
* This must be called before opening an external purchase link.
|
|
1904
|
+
*
|
|
1905
|
+
* @returns Promise<ExternalPurchaseNoticeResultIOS> - Result with action and error if any
|
|
1906
|
+
* @platform iOS
|
|
1907
|
+
*
|
|
1908
|
+
* @example
|
|
1909
|
+
* ```typescript
|
|
1910
|
+
* const result = await presentExternalPurchaseNoticeSheetIOS();
|
|
1911
|
+
* if (result.result === 'continue') {
|
|
1912
|
+
* // User chose to continue, open external purchase link
|
|
1913
|
+
* await presentExternalPurchaseLinkIOS('https://your-website.com/purchase');
|
|
1914
|
+
* }
|
|
1915
|
+
* ```
|
|
1916
|
+
*/
|
|
1917
|
+
export const presentExternalPurchaseNoticeSheetIOS: MutationField<
|
|
1918
|
+
'presentExternalPurchaseNoticeSheetIOS'
|
|
1919
|
+
> = async () => {
|
|
1920
|
+
if (Platform.OS !== 'ios') {
|
|
1921
|
+
throw new Error('External purchase is only supported on iOS');
|
|
1922
|
+
}
|
|
1923
|
+
try {
|
|
1924
|
+
return (await IAP.instance.presentExternalPurchaseNoticeSheetIOS()) as any;
|
|
1925
|
+
} catch (error) {
|
|
1926
|
+
RnIapConsole.error(
|
|
1927
|
+
'Failed to present external purchase notice sheet:',
|
|
1928
|
+
error,
|
|
1929
|
+
);
|
|
1930
|
+
throw error;
|
|
1931
|
+
}
|
|
1932
|
+
};
|
|
1933
|
+
|
|
1934
|
+
/**
|
|
1935
|
+
* Present an external purchase link to redirect users to your website (iOS 16.0+).
|
|
1936
|
+
*
|
|
1937
|
+
* @param url - The external purchase URL to open
|
|
1938
|
+
* @returns Promise<ExternalPurchaseLinkResultIOS> - Result with success status and error if any
|
|
1939
|
+
* @platform iOS
|
|
1940
|
+
*
|
|
1941
|
+
* @example
|
|
1942
|
+
* ```typescript
|
|
1943
|
+
* const result = await presentExternalPurchaseLinkIOS('https://your-website.com/purchase');
|
|
1944
|
+
* if (result.success) {
|
|
1945
|
+
* console.log('User completed external purchase');
|
|
1946
|
+
* }
|
|
1947
|
+
* ```
|
|
1948
|
+
*/
|
|
1949
|
+
export const presentExternalPurchaseLinkIOS: MutationField<
|
|
1950
|
+
'presentExternalPurchaseLinkIOS'
|
|
1951
|
+
> = async (url) => {
|
|
1952
|
+
if (Platform.OS !== 'ios') {
|
|
1953
|
+
throw new Error('External purchase is only supported on iOS');
|
|
1954
|
+
}
|
|
1955
|
+
try {
|
|
1956
|
+
return (await IAP.instance.presentExternalPurchaseLinkIOS(url)) as any;
|
|
1957
|
+
} catch (error) {
|
|
1958
|
+
RnIapConsole.error('Failed to present external purchase link:', error);
|
|
1959
|
+
throw error;
|
|
1960
|
+
}
|
|
1961
|
+
};
|
package/src/specs/RnIap.nitro.ts
CHANGED
|
@@ -6,6 +6,9 @@ import type {HybridObject} from 'react-native-nitro-modules';
|
|
|
6
6
|
import type {
|
|
7
7
|
AndroidSubscriptionOfferInput,
|
|
8
8
|
DeepLinkOptions,
|
|
9
|
+
InitConnectionConfig,
|
|
10
|
+
ExternalPurchaseLinkResultIOS,
|
|
11
|
+
ExternalPurchaseNoticeResultIOS,
|
|
9
12
|
MutationFinishTransactionArgs,
|
|
10
13
|
ProductCommon,
|
|
11
14
|
PurchaseCommon,
|
|
@@ -16,6 +19,7 @@ import type {
|
|
|
16
19
|
RequestPurchaseIosProps,
|
|
17
20
|
RequestPurchaseResult,
|
|
18
21
|
RequestSubscriptionAndroidProps,
|
|
22
|
+
UserChoiceBillingDetails,
|
|
19
23
|
} from '../types';
|
|
20
24
|
|
|
21
25
|
// ╔══════════════════════════════════════════════════════════════════════════╗
|
|
@@ -179,6 +183,15 @@ export interface NitroReceiptValidationResultAndroid {
|
|
|
179
183
|
testTransaction: ReceiptValidationResultAndroid['testTransaction'];
|
|
180
184
|
}
|
|
181
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Android one-time purchase offer details
|
|
188
|
+
*/
|
|
189
|
+
export interface NitroOneTimePurchaseOfferDetail {
|
|
190
|
+
formattedPrice: string;
|
|
191
|
+
priceAmountMicros: string;
|
|
192
|
+
priceCurrencyCode: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
182
195
|
export interface NitroPurchase {
|
|
183
196
|
id: PurchaseCommon['id'];
|
|
184
197
|
productId: PurchaseCommon['productId'];
|
|
@@ -188,10 +201,29 @@ export interface NitroPurchase {
|
|
|
188
201
|
quantity: PurchaseCommon['quantity'];
|
|
189
202
|
purchaseState: PurchaseCommon['purchaseState'];
|
|
190
203
|
isAutoRenewing: PurchaseCommon['isAutoRenewing'];
|
|
204
|
+
// iOS specific fields
|
|
191
205
|
quantityIOS?: number | null;
|
|
192
206
|
originalTransactionDateIOS?: number | null;
|
|
193
207
|
originalTransactionIdentifierIOS?: string | null;
|
|
194
208
|
appAccountToken?: string | null;
|
|
209
|
+
appBundleIdIOS?: string | null;
|
|
210
|
+
countryCodeIOS?: string | null;
|
|
211
|
+
currencyCodeIOS?: string | null;
|
|
212
|
+
currencySymbolIOS?: string | null;
|
|
213
|
+
environmentIOS?: string | null;
|
|
214
|
+
expirationDateIOS?: number | null;
|
|
215
|
+
isUpgradedIOS?: boolean | null;
|
|
216
|
+
offerIOS?: string | null;
|
|
217
|
+
ownershipTypeIOS?: string | null;
|
|
218
|
+
reasonIOS?: string | null;
|
|
219
|
+
reasonStringRepresentationIOS?: string | null;
|
|
220
|
+
revocationDateIOS?: number | null;
|
|
221
|
+
revocationReasonIOS?: string | null;
|
|
222
|
+
storefrontCountryCodeIOS?: string | null;
|
|
223
|
+
subscriptionGroupIdIOS?: string | null;
|
|
224
|
+
transactionReasonIOS?: string | null;
|
|
225
|
+
webOrderLineItemIdIOS?: string | null;
|
|
226
|
+
// Android specific fields
|
|
195
227
|
purchaseTokenAndroid?: string | null;
|
|
196
228
|
dataAndroid?: string | null;
|
|
197
229
|
signatureAndroid?: string | null;
|
|
@@ -201,6 +233,7 @@ export interface NitroPurchase {
|
|
|
201
233
|
packageNameAndroid?: string | null;
|
|
202
234
|
obfuscatedAccountIdAndroid?: string | null;
|
|
203
235
|
obfuscatedProfileIdAndroid?: string | null;
|
|
236
|
+
developerPayloadAndroid?: string | null;
|
|
204
237
|
}
|
|
205
238
|
|
|
206
239
|
export interface NitroProduct {
|
|
@@ -217,6 +250,7 @@ export interface NitroProduct {
|
|
|
217
250
|
typeIOS?: string | null;
|
|
218
251
|
isFamilyShareableIOS?: boolean | null;
|
|
219
252
|
jsonRepresentationIOS?: string | null;
|
|
253
|
+
discountsIOS?: string | null;
|
|
220
254
|
introductoryPriceIOS?: string | null;
|
|
221
255
|
introductoryPriceAsAmountIOS?: number | null;
|
|
222
256
|
introductoryPriceNumberOfPeriodsIOS?: number | null;
|
|
@@ -225,6 +259,7 @@ export interface NitroProduct {
|
|
|
225
259
|
subscriptionPeriodNumberIOS?: number | null;
|
|
226
260
|
subscriptionPeriodUnitIOS?: string | null;
|
|
227
261
|
// Android specific fields
|
|
262
|
+
nameAndroid?: string | null;
|
|
228
263
|
originalPriceAndroid?: string | null;
|
|
229
264
|
originalPriceAmountMicrosAndroid?: number | null;
|
|
230
265
|
introductoryPriceCyclesAndroid?: number | null;
|
|
@@ -233,6 +268,7 @@ export interface NitroProduct {
|
|
|
233
268
|
subscriptionPeriodAndroid?: string | null;
|
|
234
269
|
freeTrialPeriodAndroid?: string | null;
|
|
235
270
|
subscriptionOfferDetailsAndroid?: string | null;
|
|
271
|
+
oneTimePurchaseOfferDetailsAndroid?: NitroOneTimePurchaseOfferDetail | null;
|
|
236
272
|
}
|
|
237
273
|
|
|
238
274
|
// ╔══════════════════════════════════════════════════════════════════════════╗
|
|
@@ -247,9 +283,10 @@ export interface RnIap extends HybridObject<{ios: 'swift'; android: 'kotlin'}> {
|
|
|
247
283
|
|
|
248
284
|
/**
|
|
249
285
|
* Initialize connection to the store
|
|
286
|
+
* @param config - Optional configuration including alternative billing mode for Android
|
|
250
287
|
* @returns Promise<boolean> - true if connection successful
|
|
251
288
|
*/
|
|
252
|
-
initConnection(): Promise<boolean>;
|
|
289
|
+
initConnection(config?: InitConnectionConfig | null): Promise<boolean>;
|
|
253
290
|
|
|
254
291
|
/**
|
|
255
292
|
* End connection to the store
|
|
@@ -527,4 +564,97 @@ export interface RnIap extends HybridObject<{ios: 'swift'; android: 'kotlin'}> {
|
|
|
527
564
|
deepLinkToSubscriptionsAndroid?(
|
|
528
565
|
options: NitroDeepLinkOptionsAndroid,
|
|
529
566
|
): Promise<void>;
|
|
567
|
+
|
|
568
|
+
// ╔════════════════════════════════════════════════════════════════════════╗
|
|
569
|
+
// ║ ALTERNATIVE BILLING (Android) ║
|
|
570
|
+
// ╚════════════════════════════════════════════════════════════════════════╝
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Check if alternative billing is available for this user/device (Android only).
|
|
574
|
+
* Step 1 of alternative billing flow.
|
|
575
|
+
*
|
|
576
|
+
* @returns Promise<boolean> - true if available, false otherwise
|
|
577
|
+
* @throws Error if billing client not ready
|
|
578
|
+
* @platform Android
|
|
579
|
+
*/
|
|
580
|
+
checkAlternativeBillingAvailabilityAndroid(): Promise<boolean>;
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Show alternative billing information dialog to user (Android only).
|
|
584
|
+
* Step 2 of alternative billing flow.
|
|
585
|
+
* Must be called BEFORE processing payment in your payment system.
|
|
586
|
+
*
|
|
587
|
+
* @returns Promise<boolean> - true if user accepted, false if user canceled
|
|
588
|
+
* @throws Error if billing client not ready
|
|
589
|
+
* @platform Android
|
|
590
|
+
*/
|
|
591
|
+
showAlternativeBillingDialogAndroid(): Promise<boolean>;
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Create external transaction token for Google Play reporting (Android only).
|
|
595
|
+
* Step 3 of alternative billing flow.
|
|
596
|
+
* Must be called AFTER successful payment in your payment system.
|
|
597
|
+
* Token must be reported to Google Play backend within 24 hours.
|
|
598
|
+
*
|
|
599
|
+
* @param sku - Optional product SKU that was purchased
|
|
600
|
+
* @returns Promise<string | null> - Token string or null if creation failed
|
|
601
|
+
* @throws Error if billing client not ready
|
|
602
|
+
* @platform Android
|
|
603
|
+
*/
|
|
604
|
+
createAlternativeBillingTokenAndroid(
|
|
605
|
+
sku?: string | null,
|
|
606
|
+
): Promise<string | null>;
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Add a listener for user choice billing events (Android only).
|
|
610
|
+
* Fires when a user selects alternative billing in the User Choice Billing dialog.
|
|
611
|
+
*
|
|
612
|
+
* @param listener - Function to call when user chooses alternative billing
|
|
613
|
+
* @platform Android
|
|
614
|
+
*/
|
|
615
|
+
addUserChoiceBillingListenerAndroid(
|
|
616
|
+
listener: (details: UserChoiceBillingDetails) => void,
|
|
617
|
+
): void;
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Remove a user choice billing listener (Android only).
|
|
621
|
+
*
|
|
622
|
+
* @param listener - Function to remove from listeners
|
|
623
|
+
* @platform Android
|
|
624
|
+
*/
|
|
625
|
+
removeUserChoiceBillingListenerAndroid(
|
|
626
|
+
listener: (details: UserChoiceBillingDetails) => void,
|
|
627
|
+
): void;
|
|
628
|
+
|
|
629
|
+
// ╔════════════════════════════════════════════════════════════════════════╗
|
|
630
|
+
// ║ EXTERNAL PURCHASE LINKS (iOS 16.0+) ║
|
|
631
|
+
// ╚════════════════════════════════════════════════════════════════════════╝
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Check if the device can present an external purchase notice sheet (iOS 18.2+).
|
|
635
|
+
*
|
|
636
|
+
* @returns Promise<boolean> - true if notice sheet can be presented
|
|
637
|
+
* @platform iOS
|
|
638
|
+
*/
|
|
639
|
+
canPresentExternalPurchaseNoticeIOS(): Promise<boolean>;
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Present an external purchase notice sheet to inform users about external purchases (iOS 18.2+).
|
|
643
|
+
* This must be called before opening an external purchase link.
|
|
644
|
+
*
|
|
645
|
+
* @returns Promise<ExternalPurchaseNoticeResultIOS> - Result with action and error if any
|
|
646
|
+
* @platform iOS
|
|
647
|
+
*/
|
|
648
|
+
presentExternalPurchaseNoticeSheetIOS(): Promise<ExternalPurchaseNoticeResultIOS>;
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Present an external purchase link to redirect users to your website (iOS 16.0+).
|
|
652
|
+
*
|
|
653
|
+
* @param url - The external purchase URL to open
|
|
654
|
+
* @returns Promise<ExternalPurchaseLinkResultIOS> - Result with success status and error if any
|
|
655
|
+
* @platform iOS
|
|
656
|
+
*/
|
|
657
|
+
presentExternalPurchaseLinkIOS(
|
|
658
|
+
url: string,
|
|
659
|
+
): Promise<ExternalPurchaseLinkResultIOS>;
|
|
530
660
|
}
|