react-native-iap 15.4.1 → 15.5.1
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 +1 -0
- package/ios/HybridRnIap.swift +3 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/vega-adapter.js +77 -6
- package/lib/module/vega-adapter.js.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +18 -12
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +7 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/vega-adapter.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitProps.hpp +5 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitProps.kt +7 -2
- package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitProps.swift +38 -1
- package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitProps.hpp +5 -1
- package/openiap-versions.json +3 -3
- package/package.json +5 -5
- package/src/specs/RnIap.nitro.ts +25 -12
- package/src/types.ts +7 -0
- package/src/vega-adapter.ts +125 -22
package/src/vega-adapter.ts
CHANGED
|
@@ -150,6 +150,129 @@ function createVegaError(
|
|
|
150
150
|
return error;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
function isValidIpv4Address(address: string): boolean {
|
|
154
|
+
const octets = address.split('.');
|
|
155
|
+
return (
|
|
156
|
+
octets.length === 4 &&
|
|
157
|
+
octets.every(
|
|
158
|
+
(octet) =>
|
|
159
|
+
/^(?:0|[1-9]\d{0,2})$/.test(octet) && Number(octet) <= 255,
|
|
160
|
+
)
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function isValidIpv6Address(address: string): boolean {
|
|
165
|
+
let ipv6Part = address;
|
|
166
|
+
let ipv4GroupCount = 0;
|
|
167
|
+
|
|
168
|
+
if (address.includes('.')) {
|
|
169
|
+
const lastColon = address.lastIndexOf(':');
|
|
170
|
+
if (lastColon < 0 || !isValidIpv4Address(address.slice(lastColon + 1))) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
const ipv6Prefix = address.slice(0, lastColon);
|
|
174
|
+
ipv6Part = ipv6Prefix.endsWith(':') ? `${ipv6Prefix}:` : ipv6Prefix;
|
|
175
|
+
ipv4GroupCount = 2;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (
|
|
179
|
+
!ipv6Part.includes(':') ||
|
|
180
|
+
!/^[0-9a-f:]+$/i.test(ipv6Part) ||
|
|
181
|
+
ipv6Part.includes(':::')
|
|
182
|
+
) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const compressionIndex = ipv6Part.indexOf('::');
|
|
187
|
+
const hasCompression = compressionIndex >= 0;
|
|
188
|
+
if (
|
|
189
|
+
(hasCompression && ipv6Part.indexOf('::', compressionIndex + 2) >= 0) ||
|
|
190
|
+
(!hasCompression && (ipv6Part.startsWith(':') || ipv6Part.endsWith(':')))
|
|
191
|
+
) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const sections = hasCompression ? ipv6Part.split('::') : [ipv6Part];
|
|
196
|
+
const groups: string[] = [];
|
|
197
|
+
for (const section of sections) {
|
|
198
|
+
if (section.length > 0) groups.push(...section.split(':'));
|
|
199
|
+
}
|
|
200
|
+
if (!groups.every((group) => /^[0-9a-f]{1,4}$/i.test(group))) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const groupCount = groups.length + ipv4GroupCount;
|
|
205
|
+
return hasCompression ? groupCount < 8 : groupCount === 8;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function isValidHostname(hostname: string): boolean {
|
|
209
|
+
if (/^[0-9.]+$/.test(hostname)) {
|
|
210
|
+
return isValidIpv4Address(hostname);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const normalizedHostname = hostname.endsWith('.')
|
|
214
|
+
? hostname.slice(0, -1)
|
|
215
|
+
: hostname;
|
|
216
|
+
if (normalizedHostname.length === 0 || normalizedHostname.length > 253) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
const hostnameLabels = normalizedHostname.split('.');
|
|
220
|
+
const lastLabel = hostnameLabels[hostnameLabels.length - 1]!;
|
|
221
|
+
if (/^(?:[0-9]+|0x[0-9a-f]+)$/i.test(lastLabel)) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return normalizedHostname
|
|
226
|
+
.split('.')
|
|
227
|
+
.every(
|
|
228
|
+
(label) =>
|
|
229
|
+
label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(label),
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function getIapkitVerifyUrl(baseUrl?: string | null): string {
|
|
234
|
+
const requestedBaseUrl =
|
|
235
|
+
typeof baseUrl === 'string' && baseUrl.trim().length > 0
|
|
236
|
+
? baseUrl.trim()
|
|
237
|
+
: IAPKIT_DEFAULT_BASE_URL;
|
|
238
|
+
const normalizedBaseUrl = requestedBaseUrl.replace(/\/+$/, '');
|
|
239
|
+
// Kepler's URL polyfill throws for standard getters such as protocol,
|
|
240
|
+
// host, and pathname. Parse the small origin-only contract directly.
|
|
241
|
+
const originMatch = /^(https?):\/\/([^/?#@\s\\]+)$/i.exec(normalizedBaseUrl);
|
|
242
|
+
if (!originMatch) {
|
|
243
|
+
throw createVegaError(
|
|
244
|
+
ErrorCode.DeveloperError,
|
|
245
|
+
'IAPKit baseUrl must be a valid HTTP(S) origin',
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const authority = originMatch[2]!;
|
|
250
|
+
const isBracketedIpv6 = authority.startsWith('[');
|
|
251
|
+
const authorityMatch = isBracketedIpv6
|
|
252
|
+
? /^\[([^\]]+)\](?::([0-9]+))?$/.exec(authority)
|
|
253
|
+
: /^([^:]+)(?::([0-9]+))?$/.exec(authority);
|
|
254
|
+
const host = authorityMatch?.[1];
|
|
255
|
+
const requestedPort = authorityMatch?.[2];
|
|
256
|
+
const portNumber = requestedPort ? Number(requestedPort) : null;
|
|
257
|
+
const hasValidHost =
|
|
258
|
+
typeof host === 'string' &&
|
|
259
|
+
(isBracketedIpv6 ? isValidIpv6Address(host) : isValidHostname(host));
|
|
260
|
+
const hasValidPort =
|
|
261
|
+
portNumber === null ||
|
|
262
|
+
(Number.isInteger(portNumber) && portNumber >= 1 && portNumber <= 65535);
|
|
263
|
+
if (!authorityMatch || !hasValidHost || !hasValidPort) {
|
|
264
|
+
throw createVegaError(
|
|
265
|
+
ErrorCode.DeveloperError,
|
|
266
|
+
'IAPKit baseUrl must be a valid HTTP(S) origin',
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const scheme = originMatch[1]!.toLowerCase();
|
|
271
|
+
const serializedHost = isBracketedIpv6 ? `[${host!}]` : host!;
|
|
272
|
+
const port = requestedPort ? `:${requestedPort}` : '';
|
|
273
|
+
return `${scheme}://${serializedHost}${port}${IAPKIT_VERIFY_PATH}`;
|
|
274
|
+
}
|
|
275
|
+
|
|
153
276
|
function parseVegaErrorPayload(error: unknown): Record<string, unknown> {
|
|
154
277
|
if (!(error instanceof Error)) return {};
|
|
155
278
|
const vegaError = error as VegaError;
|
|
@@ -1023,27 +1146,6 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1023
1146
|
const verifyWithIapkit = async (
|
|
1024
1147
|
params: NitroVerifyPurchaseWithProviderProps,
|
|
1025
1148
|
): Promise<NitroVerifyPurchaseWithProviderResult> => {
|
|
1026
|
-
type IapkitEndpointOptions = NonNullable<
|
|
1027
|
-
NitroVerifyPurchaseWithProviderProps['iapkit']
|
|
1028
|
-
> & {
|
|
1029
|
-
baseUrl?: string | null;
|
|
1030
|
-
};
|
|
1031
|
-
|
|
1032
|
-
function iapkitVerifyUrl(
|
|
1033
|
-
iapkit: NitroVerifyPurchaseWithProviderProps['iapkit'],
|
|
1034
|
-
): string {
|
|
1035
|
-
const endpointOptions = iapkit as
|
|
1036
|
-
| IapkitEndpointOptions
|
|
1037
|
-
| null
|
|
1038
|
-
| undefined;
|
|
1039
|
-
const baseUrl =
|
|
1040
|
-
typeof endpointOptions?.baseUrl === 'string' &&
|
|
1041
|
-
endpointOptions.baseUrl.trim().length > 0
|
|
1042
|
-
? endpointOptions.baseUrl.trim()
|
|
1043
|
-
: IAPKIT_DEFAULT_BASE_URL;
|
|
1044
|
-
return `${baseUrl.replace(/\/+$/, '')}${IAPKIT_VERIFY_PATH}`;
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
1149
|
function normalizeIapkitState(state: unknown): IapkitPurchaseState {
|
|
1048
1150
|
const normalized =
|
|
1049
1151
|
typeof state === 'string'
|
|
@@ -1198,6 +1300,7 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1198
1300
|
|
|
1199
1301
|
const apiKey =
|
|
1200
1302
|
typeof iapkit?.apiKey === 'string' ? iapkit.apiKey.trim() : '';
|
|
1303
|
+
const verificationUrl = getIapkitVerifyUrl(iapkit?.baseUrl);
|
|
1201
1304
|
let response: Response;
|
|
1202
1305
|
try {
|
|
1203
1306
|
const controller = new AbortController();
|
|
@@ -1205,7 +1308,7 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1205
1308
|
() => controller.abort(),
|
|
1206
1309
|
IAPKIT_VERIFY_TIMEOUT_MS,
|
|
1207
1310
|
);
|
|
1208
|
-
response = await fetch(
|
|
1311
|
+
response = await fetch(verificationUrl, {
|
|
1209
1312
|
method: 'POST',
|
|
1210
1313
|
headers: {
|
|
1211
1314
|
'Content-Type': 'application/json',
|