@nosslabs/iap 0.3.1 → 0.4.0
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/CHANGELOG.md +71 -2
- package/README.md +1 -1
- package/dist/index.cjs +58 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -7
- package/dist/index.d.ts +75 -7
- package/dist/index.js +58 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -224,6 +224,19 @@ declare const optionsConfigSchema: z.ZodObject<{
|
|
|
224
224
|
* Excess entries stay in storage and are processed on subsequent launches.
|
|
225
225
|
*/
|
|
226
226
|
recoveryMaxBatch: z.ZodDefault<z.ZodNumber>;
|
|
227
|
+
/**
|
|
228
|
+
* List of backend `valid:false` error codes that recovery should treat as
|
|
229
|
+
* permanent — entries with a matching error are removed from storage
|
|
230
|
+
* instead of retried on every launch. When omitted (the default), iap
|
|
231
|
+
* uses `DEFAULT_PERMANENT_ERROR_CODES` (`['TRANSACTION_NOT_FOUND',
|
|
232
|
+
* 'PRODUCT_MISMATCH']`).
|
|
233
|
+
*
|
|
234
|
+
* REPLACES the default when provided — pass `[...DEFAULT_PERMANENT_ERROR_CODES,
|
|
235
|
+
* 'YOUR_CODE']` to extend, or `[]` to disable the feature entirely
|
|
236
|
+
* (revert to retry-forever behavior). Export the constant from
|
|
237
|
+
* `@nosslabs/iap` for the spread form.
|
|
238
|
+
*/
|
|
239
|
+
permanentErrorCodes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
227
240
|
productPriceCacheTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
228
241
|
logLevel: z.ZodDefault<z.ZodEnum<["silent", "error", "warn", "info", "debug"]>>;
|
|
229
242
|
logger: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -234,12 +247,14 @@ declare const optionsConfigSchema: z.ZodObject<{
|
|
|
234
247
|
recoveryMaxBatch: number;
|
|
235
248
|
productPriceCacheTtlMs: number;
|
|
236
249
|
logLevel: "silent" | "error" | "warn" | "info" | "debug";
|
|
250
|
+
permanentErrorCodes?: string[] | undefined;
|
|
237
251
|
logger?: unknown;
|
|
238
252
|
}, {
|
|
239
253
|
refreshOnResume?: boolean | undefined;
|
|
240
254
|
entitlementCacheTtlMs?: number | undefined;
|
|
241
255
|
recoverUnfinishedTransactions?: boolean | undefined;
|
|
242
256
|
recoveryMaxBatch?: number | undefined;
|
|
257
|
+
permanentErrorCodes?: string[] | undefined;
|
|
243
258
|
productPriceCacheTtlMs?: number | undefined;
|
|
244
259
|
logLevel?: "silent" | "error" | "warn" | "info" | "debug" | undefined;
|
|
245
260
|
logger?: unknown;
|
|
@@ -434,6 +449,19 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
434
449
|
* Excess entries stay in storage and are processed on subsequent launches.
|
|
435
450
|
*/
|
|
436
451
|
recoveryMaxBatch: z.ZodDefault<z.ZodNumber>;
|
|
452
|
+
/**
|
|
453
|
+
* List of backend `valid:false` error codes that recovery should treat as
|
|
454
|
+
* permanent — entries with a matching error are removed from storage
|
|
455
|
+
* instead of retried on every launch. When omitted (the default), iap
|
|
456
|
+
* uses `DEFAULT_PERMANENT_ERROR_CODES` (`['TRANSACTION_NOT_FOUND',
|
|
457
|
+
* 'PRODUCT_MISMATCH']`).
|
|
458
|
+
*
|
|
459
|
+
* REPLACES the default when provided — pass `[...DEFAULT_PERMANENT_ERROR_CODES,
|
|
460
|
+
* 'YOUR_CODE']` to extend, or `[]` to disable the feature entirely
|
|
461
|
+
* (revert to retry-forever behavior). Export the constant from
|
|
462
|
+
* `@nosslabs/iap` for the spread form.
|
|
463
|
+
*/
|
|
464
|
+
permanentErrorCodes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
437
465
|
productPriceCacheTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
438
466
|
logLevel: z.ZodDefault<z.ZodEnum<["silent", "error", "warn", "info", "debug"]>>;
|
|
439
467
|
logger: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -444,12 +472,14 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
444
472
|
recoveryMaxBatch: number;
|
|
445
473
|
productPriceCacheTtlMs: number;
|
|
446
474
|
logLevel: "silent" | "error" | "warn" | "info" | "debug";
|
|
475
|
+
permanentErrorCodes?: string[] | undefined;
|
|
447
476
|
logger?: unknown;
|
|
448
477
|
}, {
|
|
449
478
|
refreshOnResume?: boolean | undefined;
|
|
450
479
|
entitlementCacheTtlMs?: number | undefined;
|
|
451
480
|
recoverUnfinishedTransactions?: boolean | undefined;
|
|
452
481
|
recoveryMaxBatch?: number | undefined;
|
|
482
|
+
permanentErrorCodes?: string[] | undefined;
|
|
453
483
|
productPriceCacheTtlMs?: number | undefined;
|
|
454
484
|
logLevel?: "silent" | "error" | "warn" | "info" | "debug" | undefined;
|
|
455
485
|
logger?: unknown;
|
|
@@ -462,6 +492,7 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
462
492
|
recoveryMaxBatch: number;
|
|
463
493
|
productPriceCacheTtlMs: number;
|
|
464
494
|
logLevel: "silent" | "error" | "warn" | "info" | "debug";
|
|
495
|
+
permanentErrorCodes?: string[] | undefined;
|
|
465
496
|
logger?: unknown;
|
|
466
497
|
};
|
|
467
498
|
backend: {
|
|
@@ -514,6 +545,7 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
514
545
|
entitlementCacheTtlMs?: number | undefined;
|
|
515
546
|
recoverUnfinishedTransactions?: boolean | undefined;
|
|
516
547
|
recoveryMaxBatch?: number | undefined;
|
|
548
|
+
permanentErrorCodes?: string[] | undefined;
|
|
517
549
|
productPriceCacheTtlMs?: number | undefined;
|
|
518
550
|
logLevel?: "silent" | "error" | "warn" | "info" | "debug" | undefined;
|
|
519
551
|
logger?: unknown;
|
|
@@ -536,6 +568,7 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
536
568
|
recoveryMaxBatch: number;
|
|
537
569
|
productPriceCacheTtlMs: number;
|
|
538
570
|
logLevel: "silent" | "error" | "warn" | "info" | "debug";
|
|
571
|
+
permanentErrorCodes?: string[] | undefined;
|
|
539
572
|
logger?: unknown;
|
|
540
573
|
};
|
|
541
574
|
backend: {
|
|
@@ -588,6 +621,7 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
588
621
|
entitlementCacheTtlMs?: number | undefined;
|
|
589
622
|
recoverUnfinishedTransactions?: boolean | undefined;
|
|
590
623
|
recoveryMaxBatch?: number | undefined;
|
|
624
|
+
permanentErrorCodes?: string[] | undefined;
|
|
591
625
|
productPriceCacheTtlMs?: number | undefined;
|
|
592
626
|
logLevel?: "silent" | "error" | "warn" | "info" | "debug" | undefined;
|
|
593
627
|
logger?: unknown;
|
|
@@ -814,6 +848,24 @@ interface EventMap<TEntitlement extends EntitlementBase = EntitlementBase> {
|
|
|
814
848
|
productId: string;
|
|
815
849
|
lastFetchedAt: number;
|
|
816
850
|
};
|
|
851
|
+
/**
|
|
852
|
+
* Recovery classified an `unfinished_transactions` entry as permanently
|
|
853
|
+
* invalid (per `options.permanentErrorCodes`) and removed it from
|
|
854
|
+
* storage. Will not be retried on subsequent launches. Useful for ops
|
|
855
|
+
* logging / alerting on stuck-loop self-heal events.
|
|
856
|
+
*
|
|
857
|
+
* **Token is unmasked.** Receipt tokens (Apple `transactionId` /
|
|
858
|
+
* Google `purchaseToken`) are useful for correlation in debugging
|
|
859
|
+
* but are receipts you don't want to leak — treat as sensitive. Mask
|
|
860
|
+
* before forwarding to external analytics / logging services. iap's
|
|
861
|
+
* own internal logs use a masked form (see `lib/redact.ts`).
|
|
862
|
+
*/
|
|
863
|
+
'recovery-dropped-permanent': {
|
|
864
|
+
productId: string;
|
|
865
|
+
token: string;
|
|
866
|
+
error: string;
|
|
867
|
+
message?: string;
|
|
868
|
+
};
|
|
817
869
|
error: {
|
|
818
870
|
error: IAPError;
|
|
819
871
|
};
|
|
@@ -995,12 +1047,6 @@ interface IAP<TEntitlement extends EntitlementBase = EntitlementBase> {
|
|
|
995
1047
|
}
|
|
996
1048
|
declare function createIAP<TEntitlement extends EntitlementBase = EntitlementBase>(input: IAPConfigInput): IAP<TEntitlement>;
|
|
997
1049
|
|
|
998
|
-
/**
|
|
999
|
-
* Library version. Updated by the publish workflow to match `package.json`.
|
|
1000
|
-
* Read at runtime by the logger so error reports include the version.
|
|
1001
|
-
*/
|
|
1002
|
-
declare const VERSION = "0.1.0";
|
|
1003
|
-
|
|
1004
1050
|
interface VerifyAppleRequest {
|
|
1005
1051
|
productId: string;
|
|
1006
1052
|
/** Apple StoreKit transaction id (numeric string). */
|
|
@@ -1098,6 +1144,28 @@ interface BackendAdapter<TEntitlement extends EntitlementBase = EntitlementBase>
|
|
|
1098
1144
|
listProducts?(): Promise<ConfiguredProduct[]>;
|
|
1099
1145
|
}
|
|
1100
1146
|
|
|
1147
|
+
/**
|
|
1148
|
+
* Backend `valid:false` error codes that are treated as permanent by default
|
|
1149
|
+
* (entry removed from `unfinished_transactions` instead of retried forever).
|
|
1150
|
+
*
|
|
1151
|
+
* Conservative on purpose: only the two codes the documented recipe
|
|
1152
|
+
* contract identifies as unambiguous "domain-not-found" answers. Consumers
|
|
1153
|
+
* with custom backend error vocabularies extend via
|
|
1154
|
+
* `options.permanentErrorCodes`.
|
|
1155
|
+
*
|
|
1156
|
+
* Distinct from the `RECOVERABLE_CODES` set in `lib/errors.ts`: that one
|
|
1157
|
+
* classifies iap-internal `IAPErrorCode`s for transport/storage retry
|
|
1158
|
+
* semantics; this one classifies opaque error strings the consumer's
|
|
1159
|
+
* backend returns in `valid:false` responses.
|
|
1160
|
+
*/
|
|
1161
|
+
declare const DEFAULT_PERMANENT_ERROR_CODES: readonly string[];
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* Library version. Updated by the publish workflow to match `package.json`.
|
|
1165
|
+
* Read at runtime by the logger so error reports include the version.
|
|
1166
|
+
*/
|
|
1167
|
+
declare const VERSION = "0.1.0";
|
|
1168
|
+
|
|
1101
1169
|
interface HttpBackendAdapterOptions {
|
|
1102
1170
|
baseUrl: string;
|
|
1103
1171
|
endpoints: {
|
|
@@ -1154,4 +1222,4 @@ declare class HttpBackendAdapter<TEntitlement extends EntitlementBase = Entitlem
|
|
|
1154
1222
|
listProducts(): Promise<ConfiguredProduct[]>;
|
|
1155
1223
|
}
|
|
1156
1224
|
|
|
1157
|
-
export { type AppUserId, type BackendAdapter, type BackendConfig, type BackendConfigInput, type ConfiguredProduct, type DefaultEntitlement, type EntitlementBase, type EventMap, type EventName, type EventPayload, HttpBackendAdapter, HttpClient, type HttpRequest, type IAP, type IAPConfig, type IAPConfigInput, IAPError, IAPErrorCode, type LogLevel, type Logger, type NativeTransaction, type OptionsConfig, type Platform, type Product, type ProductType, type PurchaseOptions, type PurchaseResult, type RestoreRequest, type RestoreRequestTransaction, type RestoreResponse, type RestoreResult, type StorageConfig, type Unsubscribe, VERSION, type VerifiedTransaction, type VerifyAppleRequest, type VerifyGoogleRequest, type VerifyResponse, createIAP, errorHint, isIAPError };
|
|
1225
|
+
export { type AppUserId, type BackendAdapter, type BackendConfig, type BackendConfigInput, type ConfiguredProduct, DEFAULT_PERMANENT_ERROR_CODES, type DefaultEntitlement, type EntitlementBase, type EventMap, type EventName, type EventPayload, HttpBackendAdapter, HttpClient, type HttpRequest, type IAP, type IAPConfig, type IAPConfigInput, IAPError, IAPErrorCode, type LogLevel, type Logger, type NativeTransaction, type OptionsConfig, type Platform, type Product, type ProductType, type PurchaseOptions, type PurchaseResult, type RestoreRequest, type RestoreRequestTransaction, type RestoreResponse, type RestoreResult, type StorageConfig, type Unsubscribe, VERSION, type VerifiedTransaction, type VerifyAppleRequest, type VerifyGoogleRequest, type VerifyResponse, createIAP, errorHint, isIAPError };
|
package/dist/index.d.ts
CHANGED
|
@@ -224,6 +224,19 @@ declare const optionsConfigSchema: z.ZodObject<{
|
|
|
224
224
|
* Excess entries stay in storage and are processed on subsequent launches.
|
|
225
225
|
*/
|
|
226
226
|
recoveryMaxBatch: z.ZodDefault<z.ZodNumber>;
|
|
227
|
+
/**
|
|
228
|
+
* List of backend `valid:false` error codes that recovery should treat as
|
|
229
|
+
* permanent — entries with a matching error are removed from storage
|
|
230
|
+
* instead of retried on every launch. When omitted (the default), iap
|
|
231
|
+
* uses `DEFAULT_PERMANENT_ERROR_CODES` (`['TRANSACTION_NOT_FOUND',
|
|
232
|
+
* 'PRODUCT_MISMATCH']`).
|
|
233
|
+
*
|
|
234
|
+
* REPLACES the default when provided — pass `[...DEFAULT_PERMANENT_ERROR_CODES,
|
|
235
|
+
* 'YOUR_CODE']` to extend, or `[]` to disable the feature entirely
|
|
236
|
+
* (revert to retry-forever behavior). Export the constant from
|
|
237
|
+
* `@nosslabs/iap` for the spread form.
|
|
238
|
+
*/
|
|
239
|
+
permanentErrorCodes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
227
240
|
productPriceCacheTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
228
241
|
logLevel: z.ZodDefault<z.ZodEnum<["silent", "error", "warn", "info", "debug"]>>;
|
|
229
242
|
logger: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -234,12 +247,14 @@ declare const optionsConfigSchema: z.ZodObject<{
|
|
|
234
247
|
recoveryMaxBatch: number;
|
|
235
248
|
productPriceCacheTtlMs: number;
|
|
236
249
|
logLevel: "silent" | "error" | "warn" | "info" | "debug";
|
|
250
|
+
permanentErrorCodes?: string[] | undefined;
|
|
237
251
|
logger?: unknown;
|
|
238
252
|
}, {
|
|
239
253
|
refreshOnResume?: boolean | undefined;
|
|
240
254
|
entitlementCacheTtlMs?: number | undefined;
|
|
241
255
|
recoverUnfinishedTransactions?: boolean | undefined;
|
|
242
256
|
recoveryMaxBatch?: number | undefined;
|
|
257
|
+
permanentErrorCodes?: string[] | undefined;
|
|
243
258
|
productPriceCacheTtlMs?: number | undefined;
|
|
244
259
|
logLevel?: "silent" | "error" | "warn" | "info" | "debug" | undefined;
|
|
245
260
|
logger?: unknown;
|
|
@@ -434,6 +449,19 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
434
449
|
* Excess entries stay in storage and are processed on subsequent launches.
|
|
435
450
|
*/
|
|
436
451
|
recoveryMaxBatch: z.ZodDefault<z.ZodNumber>;
|
|
452
|
+
/**
|
|
453
|
+
* List of backend `valid:false` error codes that recovery should treat as
|
|
454
|
+
* permanent — entries with a matching error are removed from storage
|
|
455
|
+
* instead of retried on every launch. When omitted (the default), iap
|
|
456
|
+
* uses `DEFAULT_PERMANENT_ERROR_CODES` (`['TRANSACTION_NOT_FOUND',
|
|
457
|
+
* 'PRODUCT_MISMATCH']`).
|
|
458
|
+
*
|
|
459
|
+
* REPLACES the default when provided — pass `[...DEFAULT_PERMANENT_ERROR_CODES,
|
|
460
|
+
* 'YOUR_CODE']` to extend, or `[]` to disable the feature entirely
|
|
461
|
+
* (revert to retry-forever behavior). Export the constant from
|
|
462
|
+
* `@nosslabs/iap` for the spread form.
|
|
463
|
+
*/
|
|
464
|
+
permanentErrorCodes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
437
465
|
productPriceCacheTtlMs: z.ZodDefault<z.ZodNumber>;
|
|
438
466
|
logLevel: z.ZodDefault<z.ZodEnum<["silent", "error", "warn", "info", "debug"]>>;
|
|
439
467
|
logger: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -444,12 +472,14 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
444
472
|
recoveryMaxBatch: number;
|
|
445
473
|
productPriceCacheTtlMs: number;
|
|
446
474
|
logLevel: "silent" | "error" | "warn" | "info" | "debug";
|
|
475
|
+
permanentErrorCodes?: string[] | undefined;
|
|
447
476
|
logger?: unknown;
|
|
448
477
|
}, {
|
|
449
478
|
refreshOnResume?: boolean | undefined;
|
|
450
479
|
entitlementCacheTtlMs?: number | undefined;
|
|
451
480
|
recoverUnfinishedTransactions?: boolean | undefined;
|
|
452
481
|
recoveryMaxBatch?: number | undefined;
|
|
482
|
+
permanentErrorCodes?: string[] | undefined;
|
|
453
483
|
productPriceCacheTtlMs?: number | undefined;
|
|
454
484
|
logLevel?: "silent" | "error" | "warn" | "info" | "debug" | undefined;
|
|
455
485
|
logger?: unknown;
|
|
@@ -462,6 +492,7 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
462
492
|
recoveryMaxBatch: number;
|
|
463
493
|
productPriceCacheTtlMs: number;
|
|
464
494
|
logLevel: "silent" | "error" | "warn" | "info" | "debug";
|
|
495
|
+
permanentErrorCodes?: string[] | undefined;
|
|
465
496
|
logger?: unknown;
|
|
466
497
|
};
|
|
467
498
|
backend: {
|
|
@@ -514,6 +545,7 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
514
545
|
entitlementCacheTtlMs?: number | undefined;
|
|
515
546
|
recoverUnfinishedTransactions?: boolean | undefined;
|
|
516
547
|
recoveryMaxBatch?: number | undefined;
|
|
548
|
+
permanentErrorCodes?: string[] | undefined;
|
|
517
549
|
productPriceCacheTtlMs?: number | undefined;
|
|
518
550
|
logLevel?: "silent" | "error" | "warn" | "info" | "debug" | undefined;
|
|
519
551
|
logger?: unknown;
|
|
@@ -536,6 +568,7 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
536
568
|
recoveryMaxBatch: number;
|
|
537
569
|
productPriceCacheTtlMs: number;
|
|
538
570
|
logLevel: "silent" | "error" | "warn" | "info" | "debug";
|
|
571
|
+
permanentErrorCodes?: string[] | undefined;
|
|
539
572
|
logger?: unknown;
|
|
540
573
|
};
|
|
541
574
|
backend: {
|
|
@@ -588,6 +621,7 @@ declare const iapConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
588
621
|
entitlementCacheTtlMs?: number | undefined;
|
|
589
622
|
recoverUnfinishedTransactions?: boolean | undefined;
|
|
590
623
|
recoveryMaxBatch?: number | undefined;
|
|
624
|
+
permanentErrorCodes?: string[] | undefined;
|
|
591
625
|
productPriceCacheTtlMs?: number | undefined;
|
|
592
626
|
logLevel?: "silent" | "error" | "warn" | "info" | "debug" | undefined;
|
|
593
627
|
logger?: unknown;
|
|
@@ -814,6 +848,24 @@ interface EventMap<TEntitlement extends EntitlementBase = EntitlementBase> {
|
|
|
814
848
|
productId: string;
|
|
815
849
|
lastFetchedAt: number;
|
|
816
850
|
};
|
|
851
|
+
/**
|
|
852
|
+
* Recovery classified an `unfinished_transactions` entry as permanently
|
|
853
|
+
* invalid (per `options.permanentErrorCodes`) and removed it from
|
|
854
|
+
* storage. Will not be retried on subsequent launches. Useful for ops
|
|
855
|
+
* logging / alerting on stuck-loop self-heal events.
|
|
856
|
+
*
|
|
857
|
+
* **Token is unmasked.** Receipt tokens (Apple `transactionId` /
|
|
858
|
+
* Google `purchaseToken`) are useful for correlation in debugging
|
|
859
|
+
* but are receipts you don't want to leak — treat as sensitive. Mask
|
|
860
|
+
* before forwarding to external analytics / logging services. iap's
|
|
861
|
+
* own internal logs use a masked form (see `lib/redact.ts`).
|
|
862
|
+
*/
|
|
863
|
+
'recovery-dropped-permanent': {
|
|
864
|
+
productId: string;
|
|
865
|
+
token: string;
|
|
866
|
+
error: string;
|
|
867
|
+
message?: string;
|
|
868
|
+
};
|
|
817
869
|
error: {
|
|
818
870
|
error: IAPError;
|
|
819
871
|
};
|
|
@@ -995,12 +1047,6 @@ interface IAP<TEntitlement extends EntitlementBase = EntitlementBase> {
|
|
|
995
1047
|
}
|
|
996
1048
|
declare function createIAP<TEntitlement extends EntitlementBase = EntitlementBase>(input: IAPConfigInput): IAP<TEntitlement>;
|
|
997
1049
|
|
|
998
|
-
/**
|
|
999
|
-
* Library version. Updated by the publish workflow to match `package.json`.
|
|
1000
|
-
* Read at runtime by the logger so error reports include the version.
|
|
1001
|
-
*/
|
|
1002
|
-
declare const VERSION = "0.1.0";
|
|
1003
|
-
|
|
1004
1050
|
interface VerifyAppleRequest {
|
|
1005
1051
|
productId: string;
|
|
1006
1052
|
/** Apple StoreKit transaction id (numeric string). */
|
|
@@ -1098,6 +1144,28 @@ interface BackendAdapter<TEntitlement extends EntitlementBase = EntitlementBase>
|
|
|
1098
1144
|
listProducts?(): Promise<ConfiguredProduct[]>;
|
|
1099
1145
|
}
|
|
1100
1146
|
|
|
1147
|
+
/**
|
|
1148
|
+
* Backend `valid:false` error codes that are treated as permanent by default
|
|
1149
|
+
* (entry removed from `unfinished_transactions` instead of retried forever).
|
|
1150
|
+
*
|
|
1151
|
+
* Conservative on purpose: only the two codes the documented recipe
|
|
1152
|
+
* contract identifies as unambiguous "domain-not-found" answers. Consumers
|
|
1153
|
+
* with custom backend error vocabularies extend via
|
|
1154
|
+
* `options.permanentErrorCodes`.
|
|
1155
|
+
*
|
|
1156
|
+
* Distinct from the `RECOVERABLE_CODES` set in `lib/errors.ts`: that one
|
|
1157
|
+
* classifies iap-internal `IAPErrorCode`s for transport/storage retry
|
|
1158
|
+
* semantics; this one classifies opaque error strings the consumer's
|
|
1159
|
+
* backend returns in `valid:false` responses.
|
|
1160
|
+
*/
|
|
1161
|
+
declare const DEFAULT_PERMANENT_ERROR_CODES: readonly string[];
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* Library version. Updated by the publish workflow to match `package.json`.
|
|
1165
|
+
* Read at runtime by the logger so error reports include the version.
|
|
1166
|
+
*/
|
|
1167
|
+
declare const VERSION = "0.1.0";
|
|
1168
|
+
|
|
1101
1169
|
interface HttpBackendAdapterOptions {
|
|
1102
1170
|
baseUrl: string;
|
|
1103
1171
|
endpoints: {
|
|
@@ -1154,4 +1222,4 @@ declare class HttpBackendAdapter<TEntitlement extends EntitlementBase = Entitlem
|
|
|
1154
1222
|
listProducts(): Promise<ConfiguredProduct[]>;
|
|
1155
1223
|
}
|
|
1156
1224
|
|
|
1157
|
-
export { type AppUserId, type BackendAdapter, type BackendConfig, type BackendConfigInput, type ConfiguredProduct, type DefaultEntitlement, type EntitlementBase, type EventMap, type EventName, type EventPayload, HttpBackendAdapter, HttpClient, type HttpRequest, type IAP, type IAPConfig, type IAPConfigInput, IAPError, IAPErrorCode, type LogLevel, type Logger, type NativeTransaction, type OptionsConfig, type Platform, type Product, type ProductType, type PurchaseOptions, type PurchaseResult, type RestoreRequest, type RestoreRequestTransaction, type RestoreResponse, type RestoreResult, type StorageConfig, type Unsubscribe, VERSION, type VerifiedTransaction, type VerifyAppleRequest, type VerifyGoogleRequest, type VerifyResponse, createIAP, errorHint, isIAPError };
|
|
1225
|
+
export { type AppUserId, type BackendAdapter, type BackendConfig, type BackendConfigInput, type ConfiguredProduct, DEFAULT_PERMANENT_ERROR_CODES, type DefaultEntitlement, type EntitlementBase, type EventMap, type EventName, type EventPayload, HttpBackendAdapter, HttpClient, type HttpRequest, type IAP, type IAPConfig, type IAPConfigInput, IAPError, IAPErrorCode, type LogLevel, type Logger, type NativeTransaction, type OptionsConfig, type Platform, type Product, type ProductType, type PurchaseOptions, type PurchaseResult, type RestoreRequest, type RestoreRequestTransaction, type RestoreResponse, type RestoreResult, type StorageConfig, type Unsubscribe, VERSION, type VerifiedTransaction, type VerifyAppleRequest, type VerifyGoogleRequest, type VerifyResponse, createIAP, errorHint, isIAPError };
|
package/dist/index.js
CHANGED
|
@@ -728,6 +728,19 @@ var optionsConfigSchema = z.object({
|
|
|
728
728
|
* Excess entries stay in storage and are processed on subsequent launches.
|
|
729
729
|
*/
|
|
730
730
|
recoveryMaxBatch: z.number().int().positive().default(50),
|
|
731
|
+
/**
|
|
732
|
+
* List of backend `valid:false` error codes that recovery should treat as
|
|
733
|
+
* permanent — entries with a matching error are removed from storage
|
|
734
|
+
* instead of retried on every launch. When omitted (the default), iap
|
|
735
|
+
* uses `DEFAULT_PERMANENT_ERROR_CODES` (`['TRANSACTION_NOT_FOUND',
|
|
736
|
+
* 'PRODUCT_MISMATCH']`).
|
|
737
|
+
*
|
|
738
|
+
* REPLACES the default when provided — pass `[...DEFAULT_PERMANENT_ERROR_CODES,
|
|
739
|
+
* 'YOUR_CODE']` to extend, or `[]` to disable the feature entirely
|
|
740
|
+
* (revert to retry-forever behavior). Export the constant from
|
|
741
|
+
* `@nosslabs/iap` for the spread form.
|
|
742
|
+
*/
|
|
743
|
+
permanentErrorCodes: z.array(z.string()).optional(),
|
|
731
744
|
productPriceCacheTtlMs: z.number().int().positive().default(24 * 60 * 60 * 1e3),
|
|
732
745
|
logLevel: z.enum(["silent", "error", "warn", "info", "debug"]).default("info"),
|
|
733
746
|
logger: z.unknown().optional()
|
|
@@ -1376,6 +1389,10 @@ async function resolveAppUserId(supply, ctx) {
|
|
|
1376
1389
|
}
|
|
1377
1390
|
|
|
1378
1391
|
// src/core/recovery-flow.ts
|
|
1392
|
+
var DEFAULT_PERMANENT_ERROR_CODES = [
|
|
1393
|
+
"TRANSACTION_NOT_FOUND",
|
|
1394
|
+
"PRODUCT_MISMATCH"
|
|
1395
|
+
];
|
|
1379
1396
|
var RecoveryOrchestrator = class {
|
|
1380
1397
|
constructor(deps) {
|
|
1381
1398
|
this.deps = deps;
|
|
@@ -1385,7 +1402,7 @@ var RecoveryOrchestrator = class {
|
|
|
1385
1402
|
const { unfinished, logger, maxBatch } = this.deps;
|
|
1386
1403
|
const allEntries = await unfinished.list();
|
|
1387
1404
|
if (allEntries.length === 0) {
|
|
1388
|
-
return { recovered: 0, failures: 0, inspected: 0 };
|
|
1405
|
+
return { recovered: 0, failures: 0, droppedPermanent: 0, inspected: 0 };
|
|
1389
1406
|
}
|
|
1390
1407
|
const entries = allEntries.slice(0, maxBatch);
|
|
1391
1408
|
if (allEntries.length > maxBatch) {
|
|
@@ -1398,6 +1415,7 @@ var RecoveryOrchestrator = class {
|
|
|
1398
1415
|
const settled = await Promise.allSettled(entries.map((entry) => this.processEntry(entry)));
|
|
1399
1416
|
let recovered = 0;
|
|
1400
1417
|
let failures = 0;
|
|
1418
|
+
let droppedPermanent = 0;
|
|
1401
1419
|
let latestEntitlements = null;
|
|
1402
1420
|
for (const result of settled) {
|
|
1403
1421
|
if (result.status === "rejected") {
|
|
@@ -1407,6 +1425,8 @@ var RecoveryOrchestrator = class {
|
|
|
1407
1425
|
if (result.value.kind === "recovered") {
|
|
1408
1426
|
recovered += 1;
|
|
1409
1427
|
latestEntitlements = result.value.entitlements;
|
|
1428
|
+
} else if (result.value.kind === "dropped-permanent") {
|
|
1429
|
+
droppedPermanent += 1;
|
|
1410
1430
|
} else {
|
|
1411
1431
|
failures += 1;
|
|
1412
1432
|
}
|
|
@@ -1415,19 +1435,47 @@ var RecoveryOrchestrator = class {
|
|
|
1415
1435
|
await this.applyEntitlements(latestEntitlements);
|
|
1416
1436
|
}
|
|
1417
1437
|
logger.debug(
|
|
1418
|
-
`Recovery: ${recovered} recovered, ${failures} left in list (will retry next launch).`
|
|
1438
|
+
`Recovery: ${recovered} recovered, ${droppedPermanent} dropped (permanent), ${failures} left in list (will retry next launch).`
|
|
1419
1439
|
);
|
|
1420
|
-
return { recovered, failures, inspected: entries.length };
|
|
1440
|
+
return { recovered, failures, droppedPermanent, inspected: entries.length };
|
|
1421
1441
|
}
|
|
1422
1442
|
async processEntry(entry) {
|
|
1423
|
-
const { nativeAdapter, unfinished, logger } = this.deps;
|
|
1443
|
+
const { nativeAdapter, unfinished, logger, emitter, permanentErrorCodes } = this.deps;
|
|
1424
1444
|
const tx = entryToNativeTransaction(entry);
|
|
1425
1445
|
const tokenLabel = maskToken(entry.token);
|
|
1426
1446
|
try {
|
|
1427
1447
|
const response = await verifyNativeTransaction(this.deps.backend, tx);
|
|
1428
1448
|
if (!response.valid) {
|
|
1449
|
+
if (permanentErrorCodes.has(response.error)) {
|
|
1450
|
+
logger.info(
|
|
1451
|
+
`Recovery: dropping permanently-invalid token=${tokenLabel} productId=${entry.productId} (${response.error}).`
|
|
1452
|
+
);
|
|
1453
|
+
try {
|
|
1454
|
+
await nativeAdapter.acknowledge(tx);
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
logger.warn(
|
|
1457
|
+
`Recovery: best-effort acknowledge() failed for productId=${entry.productId}; proceeding with removal anyway.`,
|
|
1458
|
+
error
|
|
1459
|
+
);
|
|
1460
|
+
}
|
|
1461
|
+
try {
|
|
1462
|
+
await unfinished.remove(entry.token);
|
|
1463
|
+
} catch (error) {
|
|
1464
|
+
logger.warn(
|
|
1465
|
+
`Recovery: unfinished.remove() failed for productId=${entry.productId} after permanent classification; will dedupe on next launch.`,
|
|
1466
|
+
error
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
emitter.emit("recovery-dropped-permanent", {
|
|
1470
|
+
productId: entry.productId,
|
|
1471
|
+
token: entry.token,
|
|
1472
|
+
error: response.error,
|
|
1473
|
+
...response.message !== void 0 ? { message: response.message } : {}
|
|
1474
|
+
});
|
|
1475
|
+
return { kind: "dropped-permanent" };
|
|
1476
|
+
}
|
|
1429
1477
|
logger.debug(
|
|
1430
|
-
`Recovery: backend rejected token=${tokenLabel} productId=${entry.productId} (${response.error}); leaving in list.`
|
|
1478
|
+
`Recovery: backend rejected token=${tokenLabel} productId=${entry.productId} (${response.error}); leaving in list for retry.`
|
|
1431
1479
|
);
|
|
1432
1480
|
return { kind: "failed" };
|
|
1433
1481
|
}
|
|
@@ -1878,7 +1926,10 @@ function createIAP(input) {
|
|
|
1878
1926
|
state.restorer = new RestoreOrchestrator(sharedDeps);
|
|
1879
1927
|
state.recoverer = new RecoveryOrchestrator({
|
|
1880
1928
|
...sharedDeps,
|
|
1881
|
-
maxBatch: state.config.options.recoveryMaxBatch
|
|
1929
|
+
maxBatch: state.config.options.recoveryMaxBatch,
|
|
1930
|
+
permanentErrorCodes: new Set(
|
|
1931
|
+
state.config.options.permanentErrorCodes ?? DEFAULT_PERMANENT_ERROR_CODES
|
|
1932
|
+
)
|
|
1882
1933
|
});
|
|
1883
1934
|
try {
|
|
1884
1935
|
await state.adapter.isAvailable();
|
|
@@ -2069,6 +2120,6 @@ init_errors();
|
|
|
2069
2120
|
// src/version.ts
|
|
2070
2121
|
var VERSION = "0.1.0";
|
|
2071
2122
|
|
|
2072
|
-
export { HttpBackendAdapter, HttpClient, IAPError, IAPErrorCode, VERSION, createIAP, errorHint, isIAPError };
|
|
2123
|
+
export { DEFAULT_PERMANENT_ERROR_CODES, HttpBackendAdapter, HttpClient, IAPError, IAPErrorCode, VERSION, createIAP, errorHint, isIAPError };
|
|
2073
2124
|
//# sourceMappingURL=index.js.map
|
|
2074
2125
|
//# sourceMappingURL=index.js.map
|