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.
@@ -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: string;
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
- type PlatformErrorData = {
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]: toStandardizedCode(ErrorCode.Unknown),
39
- [ErrorCode.UserCancelled]: toStandardizedCode(ErrorCode.UserCancelled),
40
- [ErrorCode.UserError]: toStandardizedCode(ErrorCode.UserError),
41
- [ErrorCode.ItemUnavailable]: toStandardizedCode(ErrorCode.ItemUnavailable),
42
- [ErrorCode.RemoteError]: toStandardizedCode(ErrorCode.RemoteError),
43
- [ErrorCode.NetworkError]: toStandardizedCode(ErrorCode.NetworkError),
44
- [ErrorCode.ServiceError]: toStandardizedCode(ErrorCode.ServiceError),
45
- [ErrorCode.ReceiptFailed]: toStandardizedCode(ErrorCode.ReceiptFailed),
46
- [ErrorCode.ReceiptFinished]: toStandardizedCode(ErrorCode.ReceiptFinished),
47
- [ErrorCode.ReceiptFinishedFailed]: toStandardizedCode(
48
- ErrorCode.ReceiptFinishedFailed,
49
- ),
50
- [ErrorCode.NotPrepared]: toStandardizedCode(ErrorCode.NotPrepared),
51
- [ErrorCode.NotEnded]: toStandardizedCode(ErrorCode.NotEnded),
52
- [ErrorCode.AlreadyOwned]: toStandardizedCode(ErrorCode.AlreadyOwned),
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.DeferredPayment]: toStandardizedCode(ErrorCode.DeferredPayment),
58
- [ErrorCode.Interrupted]: toStandardizedCode(ErrorCode.Interrupted),
59
- [ErrorCode.IapNotAvailable]: toStandardizedCode(ErrorCode.IapNotAvailable),
60
- [ErrorCode.PurchaseError]: toStandardizedCode(ErrorCode.PurchaseError),
61
- [ErrorCode.SyncError]: toStandardizedCode(ErrorCode.SyncError),
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.ActivityUnavailable]: toStandardizedCode(
66
- ErrorCode.ActivityUnavailable,
67
- ),
68
- [ErrorCode.AlreadyPrepared]: toStandardizedCode(ErrorCode.AlreadyPrepared),
69
- [ErrorCode.Pending]: toStandardizedCode(ErrorCode.Pending),
70
- [ErrorCode.ConnectionClosed]: toStandardizedCode(ErrorCode.ConnectionClosed),
71
- [ErrorCode.InitConnection]: toStandardizedCode(ErrorCode.InitConnection),
72
- [ErrorCode.ServiceDisconnected]: toStandardizedCode(
73
- ErrorCode.ServiceDisconnected,
74
- ),
75
- [ErrorCode.QueryProduct]: toStandardizedCode(ErrorCode.QueryProduct),
76
- [ErrorCode.SkuNotFound]: toStandardizedCode(ErrorCode.SkuNotFound),
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 error = new Error(props.message) as PurchaseError;
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 = props.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: PlatformErrorData,
114
+ errorData: PurchaseErrorProps,
112
115
  platform: IapPlatform,
113
116
  ): PurchaseError => {
114
117
  const normalizedPlatform = normalizePlatform(platform);
115
118
  const errorCode = errorData.code
116
- ? ErrorCodeUtils.fromPlatformCode(errorData.code, normalizedPlatform)
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
- standardized
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
- if (OPENIAP_ERROR_CODE_SET.has(platformCode)) {
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 === platformCode,
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
- nativeCode === platformCode &&
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 (mappedCode === platformCode) {
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
- standardized
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
- const standardized = toStandardizedCode(errorCode);
194
- if (
195
- (NATIVE_ERROR_CODES as Record<string, unknown>)[standardized] !==
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;