expo-iap 3.1.2 → 3.1.3
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/expo/modules/iap/ExpoIapHelper.kt +171 -0
- package/android/src/main/java/expo/modules/iap/ExpoIapLog.kt +4 -0
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +50 -114
- package/build/types.d.ts +34 -34
- package/build/types.js +34 -34
- package/build/types.js.map +1 -1
- package/build/utils/errorMapping.d.ts +7 -8
- package/build/utils/errorMapping.d.ts.map +1 -1
- package/build/utils/errorMapping.js +83 -53
- package/build/utils/errorMapping.js.map +1 -1
- package/coverage/clover.xml +105 -96
- package/coverage/coverage-final.json +1 -1
- package/coverage/lcov-report/index.html +19 -19
- package/coverage/lcov-report/src/helpers/index.html +1 -1
- package/coverage/lcov-report/src/helpers/subscription.ts.html +1 -1
- package/coverage/lcov-report/src/index.html +1 -1
- package/coverage/lcov-report/src/index.ts.html +1 -1
- package/coverage/lcov-report/src/modules/android.ts.html +1 -1
- package/coverage/lcov-report/src/modules/index.html +1 -1
- package/coverage/lcov-report/src/modules/ios.ts.html +1 -1
- package/coverage/lcov-report/src/utils/errorMapping.ts.html +191 -134
- package/coverage/lcov-report/src/utils/index.html +19 -19
- package/coverage/lcov.info +215 -185
- package/ios/ExpoIapHelper.swift +101 -6
- package/ios/ExpoIapModule.swift +29 -97
- package/openiap-versions.json +1 -1
- package/package.json +1 -1
- package/src/types.ts +34 -34
- package/src/utils/errorMapping.ts +101 -82
|
@@ -7,27 +7,36 @@
|
|
|
7
7
|
import {NATIVE_ERROR_CODES} from '../ExpoIapModule';
|
|
8
8
|
import {ErrorCode, IapPlatform} from '../types';
|
|
9
9
|
|
|
10
|
+
const toKebabCase = (str: string): string => {
|
|
11
|
+
if (str.includes('_')) {
|
|
12
|
+
return str
|
|
13
|
+
.split('_')
|
|
14
|
+
.map((word) => word.toLowerCase())
|
|
15
|
+
.join('-');
|
|
16
|
+
} else {
|
|
17
|
+
return str
|
|
18
|
+
.replace(/([A-Z])/g, '-$1')
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
.replace(/^-/, '');
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
10
24
|
export interface PurchaseErrorProps {
|
|
11
|
-
message
|
|
25
|
+
message?: string;
|
|
12
26
|
responseCode?: number;
|
|
13
27
|
debugMessage?: string;
|
|
14
|
-
code?: ErrorCode;
|
|
28
|
+
code?: ErrorCode | string | number;
|
|
15
29
|
productId?: string;
|
|
16
30
|
platform?: IapPlatform;
|
|
17
31
|
}
|
|
18
32
|
|
|
19
|
-
|
|
20
|
-
code?: string | number;
|
|
21
|
-
message?: string;
|
|
33
|
+
export interface PurchaseError extends Error {
|
|
22
34
|
responseCode?: number;
|
|
23
35
|
debugMessage?: string;
|
|
36
|
+
code?: ErrorCode;
|
|
24
37
|
productId?: string;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
export type PurchaseError = Error & PurchaseErrorProps;
|
|
28
|
-
|
|
29
|
-
const toStandardizedCode = (errorCode: ErrorCode): string =>
|
|
30
|
-
errorCode.startsWith('E_') ? errorCode : `E_${errorCode}`;
|
|
38
|
+
platform?: IapPlatform;
|
|
39
|
+
}
|
|
31
40
|
|
|
32
41
|
const normalizePlatform = (platform: IapPlatform): 'ios' | 'android' =>
|
|
33
42
|
typeof platform === 'string' && platform.toLowerCase() === 'ios'
|
|
@@ -35,54 +44,42 @@ const normalizePlatform = (platform: IapPlatform): 'ios' | 'android' =>
|
|
|
35
44
|
: 'android';
|
|
36
45
|
|
|
37
46
|
const COMMON_ERROR_CODE_MAP: Record<ErrorCode, string> = {
|
|
38
|
-
[ErrorCode.Unknown]:
|
|
39
|
-
[ErrorCode.UserCancelled]:
|
|
40
|
-
[ErrorCode.UserError]:
|
|
41
|
-
[ErrorCode.ItemUnavailable]:
|
|
42
|
-
[ErrorCode.RemoteError]:
|
|
43
|
-
[ErrorCode.NetworkError]:
|
|
44
|
-
[ErrorCode.ServiceError]:
|
|
45
|
-
[ErrorCode.ReceiptFailed]:
|
|
46
|
-
[ErrorCode.ReceiptFinished]:
|
|
47
|
-
[ErrorCode.ReceiptFinishedFailed]:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
[ErrorCode.
|
|
51
|
-
[ErrorCode.
|
|
52
|
-
[ErrorCode.
|
|
53
|
-
[ErrorCode.DeveloperError]: toStandardizedCode(ErrorCode.DeveloperError),
|
|
54
|
-
[ErrorCode.BillingResponseJsonParseError]: toStandardizedCode(
|
|
47
|
+
[ErrorCode.Unknown]: ErrorCode.Unknown,
|
|
48
|
+
[ErrorCode.UserCancelled]: ErrorCode.UserCancelled,
|
|
49
|
+
[ErrorCode.UserError]: ErrorCode.UserError,
|
|
50
|
+
[ErrorCode.ItemUnavailable]: ErrorCode.ItemUnavailable,
|
|
51
|
+
[ErrorCode.RemoteError]: ErrorCode.RemoteError,
|
|
52
|
+
[ErrorCode.NetworkError]: ErrorCode.NetworkError,
|
|
53
|
+
[ErrorCode.ServiceError]: ErrorCode.ServiceError,
|
|
54
|
+
[ErrorCode.ReceiptFailed]: ErrorCode.ReceiptFailed,
|
|
55
|
+
[ErrorCode.ReceiptFinished]: ErrorCode.ReceiptFinished,
|
|
56
|
+
[ErrorCode.ReceiptFinishedFailed]: ErrorCode.ReceiptFinishedFailed,
|
|
57
|
+
[ErrorCode.NotPrepared]: ErrorCode.NotPrepared,
|
|
58
|
+
[ErrorCode.NotEnded]: ErrorCode.NotEnded,
|
|
59
|
+
[ErrorCode.AlreadyOwned]: ErrorCode.AlreadyOwned,
|
|
60
|
+
[ErrorCode.DeveloperError]: ErrorCode.DeveloperError,
|
|
61
|
+
[ErrorCode.BillingResponseJsonParseError]:
|
|
55
62
|
ErrorCode.BillingResponseJsonParseError,
|
|
56
|
-
|
|
57
|
-
[ErrorCode.
|
|
58
|
-
[ErrorCode.
|
|
59
|
-
[ErrorCode.
|
|
60
|
-
[ErrorCode.
|
|
61
|
-
[ErrorCode.
|
|
62
|
-
[ErrorCode.TransactionValidationFailed]: toStandardizedCode(
|
|
63
|
+
[ErrorCode.DeferredPayment]: ErrorCode.DeferredPayment,
|
|
64
|
+
[ErrorCode.Interrupted]: ErrorCode.Interrupted,
|
|
65
|
+
[ErrorCode.IapNotAvailable]: ErrorCode.IapNotAvailable,
|
|
66
|
+
[ErrorCode.PurchaseError]: ErrorCode.PurchaseError,
|
|
67
|
+
[ErrorCode.SyncError]: ErrorCode.SyncError,
|
|
68
|
+
[ErrorCode.TransactionValidationFailed]:
|
|
63
69
|
ErrorCode.TransactionValidationFailed,
|
|
64
|
-
|
|
65
|
-
[ErrorCode.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
[ErrorCode.
|
|
69
|
-
[ErrorCode.
|
|
70
|
-
[ErrorCode.
|
|
71
|
-
[ErrorCode.
|
|
72
|
-
[ErrorCode.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
[ErrorCode.
|
|
76
|
-
[ErrorCode.
|
|
77
|
-
[ErrorCode.SkuOfferMismatch]: toStandardizedCode(ErrorCode.SkuOfferMismatch),
|
|
78
|
-
[ErrorCode.ItemNotOwned]: toStandardizedCode(ErrorCode.ItemNotOwned),
|
|
79
|
-
[ErrorCode.BillingUnavailable]: toStandardizedCode(
|
|
80
|
-
ErrorCode.BillingUnavailable,
|
|
81
|
-
),
|
|
82
|
-
[ErrorCode.FeatureNotSupported]: toStandardizedCode(
|
|
83
|
-
ErrorCode.FeatureNotSupported,
|
|
84
|
-
),
|
|
85
|
-
[ErrorCode.EmptySkuList]: toStandardizedCode(ErrorCode.EmptySkuList),
|
|
70
|
+
[ErrorCode.ActivityUnavailable]: ErrorCode.ActivityUnavailable,
|
|
71
|
+
[ErrorCode.AlreadyPrepared]: ErrorCode.AlreadyPrepared,
|
|
72
|
+
[ErrorCode.Pending]: ErrorCode.Pending,
|
|
73
|
+
[ErrorCode.ConnectionClosed]: ErrorCode.ConnectionClosed,
|
|
74
|
+
[ErrorCode.InitConnection]: ErrorCode.InitConnection,
|
|
75
|
+
[ErrorCode.ServiceDisconnected]: ErrorCode.ServiceDisconnected,
|
|
76
|
+
[ErrorCode.QueryProduct]: ErrorCode.QueryProduct,
|
|
77
|
+
[ErrorCode.SkuNotFound]: ErrorCode.SkuNotFound,
|
|
78
|
+
[ErrorCode.SkuOfferMismatch]: ErrorCode.SkuOfferMismatch,
|
|
79
|
+
[ErrorCode.ItemNotOwned]: ErrorCode.ItemNotOwned,
|
|
80
|
+
[ErrorCode.BillingUnavailable]: ErrorCode.BillingUnavailable,
|
|
81
|
+
[ErrorCode.FeatureNotSupported]: ErrorCode.FeatureNotSupported,
|
|
82
|
+
[ErrorCode.EmptySkuList]: ErrorCode.EmptySkuList,
|
|
86
83
|
};
|
|
87
84
|
|
|
88
85
|
export const ErrorCodeMapping = {
|
|
@@ -90,30 +87,38 @@ export const ErrorCodeMapping = {
|
|
|
90
87
|
android: COMMON_ERROR_CODE_MAP,
|
|
91
88
|
} as const;
|
|
92
89
|
|
|
93
|
-
const OPENIAP_ERROR_CODE_SET: Set<string> = new Set(
|
|
94
|
-
Object.values(ErrorCode).map((code) => toStandardizedCode(code)),
|
|
95
|
-
);
|
|
90
|
+
const OPENIAP_ERROR_CODE_SET: Set<string> = new Set(Object.values(ErrorCode));
|
|
96
91
|
|
|
97
92
|
export const createPurchaseError = (
|
|
98
93
|
props: PurchaseErrorProps,
|
|
99
94
|
): PurchaseError => {
|
|
100
|
-
const
|
|
95
|
+
const errorCode = props.code
|
|
96
|
+
? typeof props.code === 'string' || typeof props.code === 'number'
|
|
97
|
+
? ErrorCodeUtils.fromPlatformCode(props.code, props.platform || 'ios')
|
|
98
|
+
: props.code
|
|
99
|
+
: undefined;
|
|
100
|
+
|
|
101
|
+
const error = new Error(
|
|
102
|
+
props.message ?? 'Unknown error occurred',
|
|
103
|
+
) as PurchaseError;
|
|
101
104
|
error.name = '[expo-iap]: PurchaseError';
|
|
102
105
|
error.responseCode = props.responseCode;
|
|
103
106
|
error.debugMessage = props.debugMessage;
|
|
104
|
-
error.code =
|
|
107
|
+
error.code = errorCode;
|
|
105
108
|
error.productId = props.productId;
|
|
106
109
|
error.platform = props.platform;
|
|
107
110
|
return error;
|
|
108
111
|
};
|
|
109
112
|
|
|
110
113
|
export const createPurchaseErrorFromPlatform = (
|
|
111
|
-
errorData:
|
|
114
|
+
errorData: PurchaseErrorProps,
|
|
112
115
|
platform: IapPlatform,
|
|
113
116
|
): PurchaseError => {
|
|
114
117
|
const normalizedPlatform = normalizePlatform(platform);
|
|
115
118
|
const errorCode = errorData.code
|
|
116
|
-
?
|
|
119
|
+
? typeof errorData.code === 'string' || typeof errorData.code === 'number'
|
|
120
|
+
? ErrorCodeUtils.fromPlatformCode(errorData.code, normalizedPlatform)
|
|
121
|
+
: errorData.code
|
|
117
122
|
: ErrorCode.Unknown;
|
|
118
123
|
|
|
119
124
|
return createPurchaseError({
|
|
@@ -128,11 +133,9 @@ export const createPurchaseErrorFromPlatform = (
|
|
|
128
133
|
|
|
129
134
|
export const ErrorCodeUtils = {
|
|
130
135
|
getNativeErrorCode: (errorCode: ErrorCode): string => {
|
|
131
|
-
const standardized = toStandardizedCode(errorCode);
|
|
132
136
|
return (
|
|
133
|
-
(NATIVE_ERROR_CODES as Record<string, string | undefined>)[
|
|
134
|
-
|
|
135
|
-
] ?? standardized
|
|
137
|
+
(NATIVE_ERROR_CODES as Record<string, string | undefined>)[errorCode] ??
|
|
138
|
+
errorCode
|
|
136
139
|
);
|
|
137
140
|
},
|
|
138
141
|
fromPlatformCode: (
|
|
@@ -140,9 +143,12 @@ export const ErrorCodeUtils = {
|
|
|
140
143
|
_platform: IapPlatform,
|
|
141
144
|
): ErrorCode => {
|
|
142
145
|
if (typeof platformCode === 'string' && platformCode.startsWith('E_')) {
|
|
143
|
-
|
|
146
|
+
const withoutE = platformCode.substring(2);
|
|
147
|
+
const camelCased = toKebabCase(withoutE);
|
|
148
|
+
const withE = `E_${camelCased}`;
|
|
149
|
+
if (OPENIAP_ERROR_CODE_SET.has(withE)) {
|
|
144
150
|
const match = Object.entries(COMMON_ERROR_CODE_MAP).find(
|
|
145
|
-
([, value]) => value ===
|
|
151
|
+
([, value]) => value === withE,
|
|
146
152
|
);
|
|
147
153
|
if (match) {
|
|
148
154
|
return match[0] as ErrorCode;
|
|
@@ -150,11 +156,18 @@ export const ErrorCodeUtils = {
|
|
|
150
156
|
}
|
|
151
157
|
}
|
|
152
158
|
|
|
159
|
+
const normalizedCode =
|
|
160
|
+
typeof platformCode === 'string'
|
|
161
|
+
? toKebabCase(platformCode)
|
|
162
|
+
: platformCode;
|
|
163
|
+
|
|
153
164
|
for (const [standardized, nativeCode] of Object.entries(
|
|
154
165
|
(NATIVE_ERROR_CODES || {}) as Record<string, string | number>,
|
|
155
166
|
)) {
|
|
167
|
+
const normalizedNative =
|
|
168
|
+
typeof nativeCode === 'string' ? toKebabCase(nativeCode) : nativeCode;
|
|
156
169
|
if (
|
|
157
|
-
|
|
170
|
+
normalizedNative === normalizedCode &&
|
|
158
171
|
OPENIAP_ERROR_CODE_SET.has(standardized)
|
|
159
172
|
) {
|
|
160
173
|
const match = Object.entries(COMMON_ERROR_CODE_MAP).find(
|
|
@@ -169,7 +182,10 @@ export const ErrorCodeUtils = {
|
|
|
169
182
|
for (const [errorCode, mappedCode] of Object.entries(
|
|
170
183
|
COMMON_ERROR_CODE_MAP,
|
|
171
184
|
)) {
|
|
172
|
-
if (
|
|
185
|
+
if (
|
|
186
|
+
mappedCode === normalizedCode ||
|
|
187
|
+
mappedCode === `E_${normalizedCode}`
|
|
188
|
+
) {
|
|
173
189
|
return errorCode as ErrorCode;
|
|
174
190
|
}
|
|
175
191
|
}
|
|
@@ -180,9 +196,8 @@ export const ErrorCodeUtils = {
|
|
|
180
196
|
errorCode: ErrorCode,
|
|
181
197
|
_platform: IapPlatform,
|
|
182
198
|
): string | number => {
|
|
183
|
-
const standardized = toStandardizedCode(errorCode);
|
|
184
199
|
const native = (NATIVE_ERROR_CODES as Record<string, string | number>)[
|
|
185
|
-
|
|
200
|
+
errorCode
|
|
186
201
|
];
|
|
187
202
|
return native ?? COMMON_ERROR_CODE_MAP[errorCode] ?? 'E_UNKNOWN';
|
|
188
203
|
},
|
|
@@ -190,14 +205,9 @@ export const ErrorCodeUtils = {
|
|
|
190
205
|
errorCode: ErrorCode,
|
|
191
206
|
platform: IapPlatform,
|
|
192
207
|
): boolean => {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
undefined
|
|
197
|
-
) {
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
return standardized in ErrorCodeMapping[normalizePlatform(platform)];
|
|
208
|
+
return (
|
|
209
|
+
(NATIVE_ERROR_CODES as Record<string, unknown>)[errorCode] !== undefined
|
|
210
|
+
);
|
|
201
211
|
},
|
|
202
212
|
};
|
|
203
213
|
|
|
@@ -218,11 +228,20 @@ const normalizeErrorCode = (code?: string | null): string | undefined => {
|
|
|
218
228
|
return code;
|
|
219
229
|
}
|
|
220
230
|
|
|
231
|
+
const camelCased = toKebabCase(code);
|
|
232
|
+
if (ERROR_CODES.has(camelCased)) {
|
|
233
|
+
return camelCased;
|
|
234
|
+
}
|
|
235
|
+
|
|
221
236
|
if (code.startsWith('E_')) {
|
|
222
237
|
const trimmed = code.substring(2);
|
|
223
238
|
if (ERROR_CODES.has(trimmed)) {
|
|
224
239
|
return trimmed;
|
|
225
240
|
}
|
|
241
|
+
const camelTrimmed = toKebabCase(trimmed);
|
|
242
|
+
if (ERROR_CODES.has(camelTrimmed)) {
|
|
243
|
+
return camelTrimmed;
|
|
244
|
+
}
|
|
226
245
|
}
|
|
227
246
|
|
|
228
247
|
return code;
|