@idealyst/payments 1.2.108 → 1.2.109
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/README.md +54 -56
- package/package.json +11 -11
- package/src/constants.ts +3 -4
- package/src/errors.ts +53 -21
- package/src/index.native.ts +30 -18
- package/src/index.ts +30 -18
- package/src/index.web.ts +30 -18
- package/src/payments.native.ts +249 -192
- package/src/payments.web.ts +47 -68
- package/src/types.ts +136 -122
- package/src/usePayments.ts +149 -85
package/README.md
CHANGED
|
@@ -1,100 +1,98 @@
|
|
|
1
1
|
# @idealyst/payments
|
|
2
2
|
|
|
3
|
-
Cross-platform
|
|
3
|
+
Cross-platform In-App Purchase (IAP) wrapper for React Native. Supports StoreKit 2 (iOS) and Google Play Billing (Android) via `react-native-iap`.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
yarn add @idealyst/payments
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
### Native (required for mobile payments)
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
yarn add
|
|
10
|
+
# React Native — required for IAP:
|
|
11
|
+
yarn add react-native-iap
|
|
12
|
+
cd ios && pod install
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
Follow the [
|
|
15
|
+
Follow the [react-native-iap setup guide](https://github.com/dooboolab-community/react-native-iap#installation) for iOS/Android configuration.
|
|
18
16
|
|
|
19
17
|
### Web
|
|
20
18
|
|
|
21
|
-
No additional dependencies. Web provides a stub
|
|
19
|
+
No additional dependencies. Web provides a stub — `initializeIAP()` succeeds but product fetches return empty arrays and purchase actions throw descriptive errors.
|
|
22
20
|
|
|
23
21
|
## Quick Start
|
|
24
22
|
|
|
25
23
|
```tsx
|
|
26
|
-
import {
|
|
24
|
+
import { useIAP } from '@idealyst/payments';
|
|
27
25
|
|
|
28
|
-
function
|
|
26
|
+
function SubscriptionScreen() {
|
|
29
27
|
const {
|
|
30
28
|
isReady,
|
|
31
|
-
|
|
32
|
-
isGooglePayAvailable,
|
|
29
|
+
subscriptions,
|
|
33
30
|
isProcessing,
|
|
34
31
|
error,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
merchantIdentifier: 'merchant.com.myapp',
|
|
40
|
-
merchantName: 'My App',
|
|
41
|
-
merchantCountryCode: 'US',
|
|
42
|
-
},
|
|
32
|
+
purchaseSubscription,
|
|
33
|
+
finishTransaction,
|
|
34
|
+
} = useIAP({
|
|
35
|
+
subscriptionSkus: ['pro_monthly', 'pro_yearly'],
|
|
43
36
|
});
|
|
44
37
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
38
|
+
const handleSubscribe = async (sku: string) => {
|
|
39
|
+
try {
|
|
40
|
+
const purchase = await purchaseSubscription(sku);
|
|
41
|
+
|
|
42
|
+
// Validate receipt on your server first, then finish
|
|
43
|
+
await validateReceiptOnServer(purchase.transactionReceipt);
|
|
44
|
+
await finishTransaction(purchase);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error('Purchase failed:', err);
|
|
47
|
+
}
|
|
55
48
|
};
|
|
56
49
|
|
|
57
|
-
if (!isReady) return
|
|
50
|
+
if (!isReady) return <Text>Loading...</Text>;
|
|
58
51
|
|
|
59
52
|
return (
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
<View>
|
|
54
|
+
{subscriptions.map((sub) => (
|
|
55
|
+
<Button
|
|
56
|
+
key={sub.sku}
|
|
57
|
+
onPress={() => handleSubscribe(sub.sku)}
|
|
58
|
+
disabled={isProcessing}
|
|
59
|
+
>
|
|
60
|
+
{sub.title} — {sub.priceFormatted}
|
|
61
|
+
</Button>
|
|
62
|
+
))}
|
|
63
|
+
</View>
|
|
65
64
|
);
|
|
66
65
|
}
|
|
67
66
|
```
|
|
68
67
|
|
|
69
68
|
## Flat Function API
|
|
70
69
|
|
|
71
|
-
For more control, use the flat functions directly:
|
|
72
|
-
|
|
73
70
|
```typescript
|
|
74
71
|
import {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
initializeIAP,
|
|
73
|
+
getProducts,
|
|
74
|
+
getSubscriptions,
|
|
75
|
+
purchaseProduct,
|
|
76
|
+
purchaseSubscription,
|
|
77
|
+
finishTransaction,
|
|
78
|
+
restorePurchases,
|
|
79
|
+
endConnection,
|
|
79
80
|
} from '@idealyst/payments';
|
|
80
81
|
|
|
81
|
-
await
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const methods = await checkPaymentAvailability();
|
|
88
|
-
const result = await confirmPayment({ clientSecret: '...', amount: { amount: 1099, currencyCode: 'usd' } });
|
|
82
|
+
await initializeIAP();
|
|
83
|
+
const products = await getProducts(['gem_pack_100', 'gem_pack_500']);
|
|
84
|
+
const purchase = await purchaseProduct('gem_pack_100');
|
|
85
|
+
await finishTransaction(purchase, true); // consumable
|
|
86
|
+
await endConnection(); // cleanup
|
|
89
87
|
```
|
|
90
88
|
|
|
91
89
|
## Platform Support
|
|
92
90
|
|
|
93
91
|
| Feature | iOS | Android | Web |
|
|
94
92
|
|---------|-----|---------|-----|
|
|
95
|
-
|
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
| Products (one-time) | Yes | Yes | Stub |
|
|
94
|
+
| Subscriptions | Yes | Yes | Stub |
|
|
95
|
+
| Restore Purchases | Yes | Yes | Stub |
|
|
96
|
+
| useIAP hook | Yes | Yes | Stub |
|
|
97
|
+
| purchaseProduct | Yes | Yes | Throws |
|
|
98
|
+
| purchaseSubscription | Yes | Yes | Throws |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/payments",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "Cross-platform
|
|
3
|
+
"version": "1.2.109",
|
|
4
|
+
"description": "Cross-platform In-App Purchase wrapper for React Native (StoreKit 2, Google Play Billing)",
|
|
5
5
|
"documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/payments#readme",
|
|
6
6
|
"readme": "README.md",
|
|
7
7
|
"main": "src/index.ts",
|
|
@@ -38,15 +38,15 @@
|
|
|
38
38
|
"publish:npm": "npm publish"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@stripe/stripe-react-native": ">=0.38.0",
|
|
42
41
|
"react": ">=16.8.0",
|
|
43
|
-
"react-native": ">=0.60.0"
|
|
42
|
+
"react-native": ">=0.60.0",
|
|
43
|
+
"react-native-iap": ">=14.0.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependenciesMeta": {
|
|
46
|
-
"
|
|
46
|
+
"react-native": {
|
|
47
47
|
"optional": true
|
|
48
48
|
},
|
|
49
|
-
"react-native": {
|
|
49
|
+
"react-native-iap": {
|
|
50
50
|
"optional": true
|
|
51
51
|
}
|
|
52
52
|
},
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"keywords": [
|
|
62
62
|
"react",
|
|
63
63
|
"react-native",
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
64
|
+
"in-app-purchase",
|
|
65
|
+
"iap",
|
|
66
|
+
"storekit",
|
|
67
|
+
"google-play-billing",
|
|
68
|
+
"subscriptions",
|
|
69
69
|
"cross-platform"
|
|
70
70
|
]
|
|
71
71
|
}
|
package/src/constants.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IAPProviderStatus } from './types';
|
|
2
2
|
|
|
3
|
-
export const INITIAL_PROVIDER_STATUS:
|
|
3
|
+
export const INITIAL_PROVIDER_STATUS: IAPProviderStatus = {
|
|
4
4
|
state: 'uninitialized',
|
|
5
|
-
|
|
6
|
-
isPaymentAvailable: false,
|
|
5
|
+
isStoreAvailable: false,
|
|
7
6
|
};
|
package/src/errors.ts
CHANGED
|
@@ -1,46 +1,78 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IAPError, IAPErrorCode } from './types';
|
|
2
2
|
|
|
3
|
-
export function
|
|
4
|
-
code:
|
|
3
|
+
export function createIAPError(
|
|
4
|
+
code: IAPErrorCode,
|
|
5
5
|
message: string,
|
|
6
6
|
originalError?: unknown,
|
|
7
|
-
):
|
|
7
|
+
): IAPError {
|
|
8
8
|
return { code, message, originalError };
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Normalize a
|
|
12
|
+
* Normalize a react-native-iap error (or any thrown value) into an IAPError.
|
|
13
|
+
*
|
|
14
|
+
* react-native-iap errors have a `code` property with values like
|
|
15
|
+
* "E_USER_CANCELLED", "E_ALREADY_OWNED", etc.
|
|
13
16
|
*/
|
|
14
|
-
export function normalizeError(error: unknown):
|
|
17
|
+
export function normalizeError(error: unknown): IAPError {
|
|
15
18
|
if (error && typeof error === 'object' && 'code' in error) {
|
|
16
|
-
const
|
|
19
|
+
const iapError = error as { code?: string; message?: string };
|
|
17
20
|
|
|
18
|
-
switch (
|
|
19
|
-
case '
|
|
20
|
-
|
|
21
|
-
return createPaymentError(
|
|
21
|
+
switch (iapError.code) {
|
|
22
|
+
case 'E_USER_CANCELLED':
|
|
23
|
+
return createIAPError(
|
|
22
24
|
'user_cancelled',
|
|
23
|
-
'
|
|
25
|
+
'Purchase was cancelled by user',
|
|
24
26
|
error,
|
|
25
27
|
);
|
|
26
|
-
case '
|
|
27
|
-
return
|
|
28
|
-
'
|
|
29
|
-
|
|
28
|
+
case 'E_ALREADY_OWNED':
|
|
29
|
+
return createIAPError(
|
|
30
|
+
'already_owned',
|
|
31
|
+
'This item has already been purchased',
|
|
32
|
+
error,
|
|
33
|
+
);
|
|
34
|
+
case 'E_NOT_PREPARED':
|
|
35
|
+
return createIAPError(
|
|
36
|
+
'not_initialized',
|
|
37
|
+
'IAP connection not initialized. Call initializeIAP() first.',
|
|
38
|
+
error,
|
|
39
|
+
);
|
|
40
|
+
case 'E_DEFERRED':
|
|
41
|
+
return createIAPError(
|
|
42
|
+
'purchase_pending',
|
|
43
|
+
'Purchase is pending approval (e.g., Ask to Buy)',
|
|
44
|
+
error,
|
|
45
|
+
);
|
|
46
|
+
case 'E_ITEM_UNAVAILABLE':
|
|
47
|
+
return createIAPError(
|
|
48
|
+
'item_unavailable',
|
|
49
|
+
'The requested product is not available in the store',
|
|
50
|
+
error,
|
|
51
|
+
);
|
|
52
|
+
case 'E_NETWORK_ERROR':
|
|
53
|
+
return createIAPError(
|
|
54
|
+
'network_error',
|
|
55
|
+
iapError.message || 'A network error occurred',
|
|
56
|
+
error,
|
|
57
|
+
);
|
|
58
|
+
case 'E_SERVICE_ERROR':
|
|
59
|
+
return createIAPError(
|
|
60
|
+
'store_error',
|
|
61
|
+
iapError.message || 'The store service encountered an error',
|
|
30
62
|
error,
|
|
31
63
|
);
|
|
32
64
|
default:
|
|
33
|
-
return
|
|
34
|
-
'
|
|
35
|
-
|
|
65
|
+
return createIAPError(
|
|
66
|
+
'unknown',
|
|
67
|
+
iapError.message || 'An unknown IAP error occurred',
|
|
36
68
|
error,
|
|
37
69
|
);
|
|
38
70
|
}
|
|
39
71
|
}
|
|
40
72
|
|
|
41
73
|
if (error instanceof Error) {
|
|
42
|
-
return
|
|
74
|
+
return createIAPError('unknown', error.message, error);
|
|
43
75
|
}
|
|
44
76
|
|
|
45
|
-
return
|
|
77
|
+
return createIAPError('unknown', String(error), error);
|
|
46
78
|
}
|
package/src/index.native.ts
CHANGED
|
@@ -1,27 +1,39 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export { INITIAL_PROVIDER_STATUS } from './constants';
|
|
3
|
-
export {
|
|
3
|
+
export { createIAPError, normalizeError } from './errors';
|
|
4
4
|
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
initializeIAP,
|
|
6
|
+
getProducts,
|
|
7
|
+
getSubscriptions,
|
|
8
|
+
purchaseProduct,
|
|
9
|
+
purchaseSubscription,
|
|
10
|
+
finishTransaction,
|
|
11
|
+
restorePurchases,
|
|
12
|
+
getIAPStatus,
|
|
13
|
+
endConnection,
|
|
10
14
|
} from './payments.native';
|
|
11
15
|
|
|
12
|
-
import {
|
|
16
|
+
import { createUseIAPHook } from './usePayments';
|
|
13
17
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
initializeIAP,
|
|
19
|
+
getProducts,
|
|
20
|
+
getSubscriptions,
|
|
21
|
+
purchaseProduct,
|
|
22
|
+
purchaseSubscription,
|
|
23
|
+
finishTransaction,
|
|
24
|
+
restorePurchases,
|
|
25
|
+
getIAPStatus,
|
|
26
|
+
endConnection,
|
|
19
27
|
} from './payments.native';
|
|
20
28
|
|
|
21
|
-
export const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
export const useIAP = createUseIAPHook({
|
|
30
|
+
initializeIAP,
|
|
31
|
+
getProducts,
|
|
32
|
+
getSubscriptions,
|
|
33
|
+
purchaseProduct,
|
|
34
|
+
purchaseSubscription,
|
|
35
|
+
finishTransaction,
|
|
36
|
+
restorePurchases,
|
|
37
|
+
getIAPStatus,
|
|
38
|
+
endConnection,
|
|
27
39
|
});
|
package/src/index.ts
CHANGED
|
@@ -2,28 +2,40 @@
|
|
|
2
2
|
// Platform-specific entry points (index.native.ts, index.web.ts) are resolved by bundlers.
|
|
3
3
|
export * from './types';
|
|
4
4
|
export { INITIAL_PROVIDER_STATUS } from './constants';
|
|
5
|
-
export {
|
|
5
|
+
export { createIAPError, normalizeError } from './errors';
|
|
6
6
|
export {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
initializeIAP,
|
|
8
|
+
getProducts,
|
|
9
|
+
getSubscriptions,
|
|
10
|
+
purchaseProduct,
|
|
11
|
+
purchaseSubscription,
|
|
12
|
+
finishTransaction,
|
|
13
|
+
restorePurchases,
|
|
14
|
+
getIAPStatus,
|
|
15
|
+
endConnection,
|
|
12
16
|
} from './payments.web';
|
|
13
17
|
|
|
14
|
-
import {
|
|
18
|
+
import { createUseIAPHook } from './usePayments';
|
|
15
19
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
initializeIAP,
|
|
21
|
+
getProducts,
|
|
22
|
+
getSubscriptions,
|
|
23
|
+
purchaseProduct,
|
|
24
|
+
purchaseSubscription,
|
|
25
|
+
finishTransaction,
|
|
26
|
+
restorePurchases,
|
|
27
|
+
getIAPStatus,
|
|
28
|
+
endConnection,
|
|
21
29
|
} from './payments.web';
|
|
22
30
|
|
|
23
|
-
export const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
export const useIAP = createUseIAPHook({
|
|
32
|
+
initializeIAP,
|
|
33
|
+
getProducts,
|
|
34
|
+
getSubscriptions,
|
|
35
|
+
purchaseProduct,
|
|
36
|
+
purchaseSubscription,
|
|
37
|
+
finishTransaction,
|
|
38
|
+
restorePurchases,
|
|
39
|
+
getIAPStatus,
|
|
40
|
+
endConnection,
|
|
29
41
|
});
|
package/src/index.web.ts
CHANGED
|
@@ -1,27 +1,39 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export { INITIAL_PROVIDER_STATUS } from './constants';
|
|
3
|
-
export {
|
|
3
|
+
export { createIAPError, normalizeError } from './errors';
|
|
4
4
|
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
initializeIAP,
|
|
6
|
+
getProducts,
|
|
7
|
+
getSubscriptions,
|
|
8
|
+
purchaseProduct,
|
|
9
|
+
purchaseSubscription,
|
|
10
|
+
finishTransaction,
|
|
11
|
+
restorePurchases,
|
|
12
|
+
getIAPStatus,
|
|
13
|
+
endConnection,
|
|
10
14
|
} from './payments.web';
|
|
11
15
|
|
|
12
|
-
import {
|
|
16
|
+
import { createUseIAPHook } from './usePayments';
|
|
13
17
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
initializeIAP,
|
|
19
|
+
getProducts,
|
|
20
|
+
getSubscriptions,
|
|
21
|
+
purchaseProduct,
|
|
22
|
+
purchaseSubscription,
|
|
23
|
+
finishTransaction,
|
|
24
|
+
restorePurchases,
|
|
25
|
+
getIAPStatus,
|
|
26
|
+
endConnection,
|
|
19
27
|
} from './payments.web';
|
|
20
28
|
|
|
21
|
-
export const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
export const useIAP = createUseIAPHook({
|
|
30
|
+
initializeIAP,
|
|
31
|
+
getProducts,
|
|
32
|
+
getSubscriptions,
|
|
33
|
+
purchaseProduct,
|
|
34
|
+
purchaseSubscription,
|
|
35
|
+
finishTransaction,
|
|
36
|
+
restorePurchases,
|
|
37
|
+
getIAPStatus,
|
|
38
|
+
endConnection,
|
|
27
39
|
});
|