iap-apple 2.0.4 → 3.0.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  | Statements | Branches | Functions | Lines |
2
2
  | --------------------------- | ----------------------- | ------------------------- | ----------------- |
3
- | ![Statements](https://img.shields.io/badge/statements-76.97%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-47.96%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-75%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-75%25-red.svg?style=flat) |
3
+ | ![Statements](https://img.shields.io/badge/statements-74.19%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-53.65%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-62.5%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-72.64%25-red.svg?style=flat) |
4
4
 
5
5
  # iap-apple
6
6
 
@@ -0,0 +1,146 @@
1
+ declare enum RECEIPT_STATUS_ENUM {
2
+ SUCCESS = 0,
3
+ VALID_NO_PURCHASE = 2,
4
+ CANNOT_READ_JSON = 21000,
5
+ DATA_MALFORMED = 21002,
6
+ RECEIPT_NOT_AUTHENTICATED = 21003,
7
+ SHARED_SECRET_DOESNT_MATCH = 21004,
8
+ SERVER_NOT_AVAILABLE = 21005,
9
+ SUBSCRIPTION_EXPIRED = 21006,
10
+ TEST_ENV_RECEIPT_DETECTED = 21007,
11
+ PRODUCTION_ENV_RECEIPT_DETECTED = 21008,
12
+ INTERNAL_DATA_ACCESS_ERROR = 21009,
13
+ USER_ACCOUNT_DELETED = 21010
14
+ }
15
+
16
+ interface IPendingRenewalInfo {
17
+ auto_renew_product_id: string;
18
+ auto_renew_status: '0' | '1';
19
+ original_transaction_id: string;
20
+ product_id: string;
21
+ expiration_intent?: '1' | '2' | '3' | '4' | '5';
22
+ grace_period_expires_date_ms?: string;
23
+ is_in_billing_retry_period?: '0' | '1';
24
+ offer_code_ref_name?: string;
25
+ price_consent_status?: '0' | '1';
26
+ promotional_offer_id?: string;
27
+ }
28
+ interface IReceiptInAppItem {
29
+ quantity: string;
30
+ product_id: string;
31
+ transaction_id: string;
32
+ original_transaction_id: string;
33
+ purchase_date: string;
34
+ purchase_date_ms: string;
35
+ original_purchase_date: string;
36
+ original_purchase_date_ms: string;
37
+ expires_date?: string;
38
+ expires_date_ms?: string;
39
+ expiration_intent?: '1' | '2' | '3' | '4' | '5';
40
+ is_trial_period: string;
41
+ cancellation_date?: string;
42
+ cancellation_date_ms?: string;
43
+ cancellation_reason?: '0' | '1';
44
+ app_item_id: string;
45
+ web_order_line_item_id?: string;
46
+ is_in_intro_offer_period?: string;
47
+ promotional_offer_id?: string;
48
+ offer_code_ref_name?: string;
49
+ in_app_ownership_type?: 'FAMILY_SHARED' | 'PURCHASED';
50
+ }
51
+
52
+ interface ILogger {
53
+ log: (message: string) => void;
54
+ warn: (message: string) => void;
55
+ error: (message: string) => void;
56
+ }
57
+ interface IIAPAppleConfig {
58
+ appleExcludeOldTransactions?: boolean;
59
+ appSharedSecret: string;
60
+ test?: boolean | undefined;
61
+ logger?: ILogger | null;
62
+ }
63
+ interface PurchasedItem {
64
+ bundleId: string;
65
+ appItemId: string;
66
+ originalTransactionId?: string;
67
+ transactionId: string;
68
+ productId: string;
69
+ originalPurchaseDateMS?: number;
70
+ expirationDateMS?: number;
71
+ purchaseDateMS: number;
72
+ isTrialPeriod: boolean;
73
+ cancellationDateMS?: number;
74
+ quantity: number;
75
+ }
76
+ interface IAPAppleError {
77
+ rejectionMessage: string;
78
+ data?: IVerifyReceiptResponseBody | null;
79
+ }
80
+ interface IVerifyReceiptResponseBody {
81
+ status: RECEIPT_STATUS_ENUM;
82
+ environment: 'Sandbox' | 'Production';
83
+ receipt: IReceipt;
84
+ latest_receipt: string;
85
+ latest_receipt_info: IReceiptInAppItem[];
86
+ 'is-retryable'?: boolean;
87
+ pending_renewal_info?: IPendingRenewalInfo[];
88
+ }
89
+ interface IReceipt {
90
+ bundle_id: string;
91
+ application_version: string;
92
+ in_app: IReceiptInAppItem[];
93
+ latest_receipt_info: IReceiptInAppItem[];
94
+ original_application_version: string;
95
+ receipt_creation_date_ms: string;
96
+ expiration_date_ms: string;
97
+ original_purchase_date: string;
98
+ app_item_id: string;
99
+ version_external_identifier: string;
100
+ expires_date_ms?: string;
101
+ }
102
+
103
+ /**
104
+ * Validates an Apple App Store receipt against Apple's verifyReceipt endpoint.
105
+ * Attempts production endpoint first, falls back to sandbox if needed.
106
+ *
107
+ * @param receipt - Base64-encoded receipt data from the App Store
108
+ * @param config - Configuration including shared secret and optional settings
109
+ * @returns Validated receipt response from Apple
110
+ * @throws {IAPAppleError} When validation fails or receipt is invalid
111
+ */
112
+ declare function verify(receipt: string, config: IIAPAppleConfig): Promise<IVerifyReceiptResponseBody>;
113
+ /**
114
+ * Checks whether the receipt validation was successful.
115
+ *
116
+ * @param verifyReceiptResponse - Response from Apple's verifyReceipt endpoint
117
+ * @returns True if the receipt status indicates success
118
+ */
119
+ declare const isVerifiedReceipt: (verifyReceiptResponse: IVerifyReceiptResponseBody | null) => boolean;
120
+ /**
121
+ * Determines if a purchased item has expired (cancelled or past expiration date).
122
+ *
123
+ * @param purchasedItem - The purchased item to check
124
+ * @returns True if the item has been cancelled or its expiration date has passed
125
+ * @throws {Error} If purchasedItem is invalid or missing transactionId
126
+ */
127
+ declare const isPurchasedItemExpired: (purchasedItem: PurchasedItem | null) => boolean;
128
+ /**
129
+ * Checks if a purchased item has been cancelled.
130
+ *
131
+ * @param purchasedItem - The purchased item to check
132
+ * @returns True if the item has a cancellation date
133
+ * @throws {Error} If purchasedItem is invalid or missing transactionId
134
+ */
135
+ declare const isPurchasedItemCanceled: (purchasedItem: PurchasedItem) => boolean;
136
+ /**
137
+ * Extracts purchased items from a validated receipt response.
138
+ * Combines in_app and latest_receipt_info, deduplicates by original_transaction_id,
139
+ * and returns items sorted by purchase date (newest first).
140
+ *
141
+ * @param verifyReceiptResponse - Response from Apple's verifyReceipt endpoint
142
+ * @returns Array of purchased items, deduplicated and sorted by purchase date descending
143
+ */
144
+ declare const getPurchasedItems: (verifyReceiptResponse: IVerifyReceiptResponseBody | null) => PurchasedItem[];
145
+
146
+ export { type IAPAppleError, type IIAPAppleConfig, type ILogger, type IPendingRenewalInfo, type IReceiptInAppItem, type IVerifyReceiptResponseBody, type PurchasedItem, RECEIPT_STATUS_ENUM, getPurchasedItems, isPurchasedItemCanceled, isPurchasedItemExpired, isVerifiedReceipt, verify };
@@ -0,0 +1,146 @@
1
+ declare enum RECEIPT_STATUS_ENUM {
2
+ SUCCESS = 0,
3
+ VALID_NO_PURCHASE = 2,
4
+ CANNOT_READ_JSON = 21000,
5
+ DATA_MALFORMED = 21002,
6
+ RECEIPT_NOT_AUTHENTICATED = 21003,
7
+ SHARED_SECRET_DOESNT_MATCH = 21004,
8
+ SERVER_NOT_AVAILABLE = 21005,
9
+ SUBSCRIPTION_EXPIRED = 21006,
10
+ TEST_ENV_RECEIPT_DETECTED = 21007,
11
+ PRODUCTION_ENV_RECEIPT_DETECTED = 21008,
12
+ INTERNAL_DATA_ACCESS_ERROR = 21009,
13
+ USER_ACCOUNT_DELETED = 21010
14
+ }
15
+
16
+ interface IPendingRenewalInfo {
17
+ auto_renew_product_id: string;
18
+ auto_renew_status: '0' | '1';
19
+ original_transaction_id: string;
20
+ product_id: string;
21
+ expiration_intent?: '1' | '2' | '3' | '4' | '5';
22
+ grace_period_expires_date_ms?: string;
23
+ is_in_billing_retry_period?: '0' | '1';
24
+ offer_code_ref_name?: string;
25
+ price_consent_status?: '0' | '1';
26
+ promotional_offer_id?: string;
27
+ }
28
+ interface IReceiptInAppItem {
29
+ quantity: string;
30
+ product_id: string;
31
+ transaction_id: string;
32
+ original_transaction_id: string;
33
+ purchase_date: string;
34
+ purchase_date_ms: string;
35
+ original_purchase_date: string;
36
+ original_purchase_date_ms: string;
37
+ expires_date?: string;
38
+ expires_date_ms?: string;
39
+ expiration_intent?: '1' | '2' | '3' | '4' | '5';
40
+ is_trial_period: string;
41
+ cancellation_date?: string;
42
+ cancellation_date_ms?: string;
43
+ cancellation_reason?: '0' | '1';
44
+ app_item_id: string;
45
+ web_order_line_item_id?: string;
46
+ is_in_intro_offer_period?: string;
47
+ promotional_offer_id?: string;
48
+ offer_code_ref_name?: string;
49
+ in_app_ownership_type?: 'FAMILY_SHARED' | 'PURCHASED';
50
+ }
51
+
52
+ interface ILogger {
53
+ log: (message: string) => void;
54
+ warn: (message: string) => void;
55
+ error: (message: string) => void;
56
+ }
57
+ interface IIAPAppleConfig {
58
+ appleExcludeOldTransactions?: boolean;
59
+ appSharedSecret: string;
60
+ test?: boolean | undefined;
61
+ logger?: ILogger | null;
62
+ }
63
+ interface PurchasedItem {
64
+ bundleId: string;
65
+ appItemId: string;
66
+ originalTransactionId?: string;
67
+ transactionId: string;
68
+ productId: string;
69
+ originalPurchaseDateMS?: number;
70
+ expirationDateMS?: number;
71
+ purchaseDateMS: number;
72
+ isTrialPeriod: boolean;
73
+ cancellationDateMS?: number;
74
+ quantity: number;
75
+ }
76
+ interface IAPAppleError {
77
+ rejectionMessage: string;
78
+ data?: IVerifyReceiptResponseBody | null;
79
+ }
80
+ interface IVerifyReceiptResponseBody {
81
+ status: RECEIPT_STATUS_ENUM;
82
+ environment: 'Sandbox' | 'Production';
83
+ receipt: IReceipt;
84
+ latest_receipt: string;
85
+ latest_receipt_info: IReceiptInAppItem[];
86
+ 'is-retryable'?: boolean;
87
+ pending_renewal_info?: IPendingRenewalInfo[];
88
+ }
89
+ interface IReceipt {
90
+ bundle_id: string;
91
+ application_version: string;
92
+ in_app: IReceiptInAppItem[];
93
+ latest_receipt_info: IReceiptInAppItem[];
94
+ original_application_version: string;
95
+ receipt_creation_date_ms: string;
96
+ expiration_date_ms: string;
97
+ original_purchase_date: string;
98
+ app_item_id: string;
99
+ version_external_identifier: string;
100
+ expires_date_ms?: string;
101
+ }
102
+
103
+ /**
104
+ * Validates an Apple App Store receipt against Apple's verifyReceipt endpoint.
105
+ * Attempts production endpoint first, falls back to sandbox if needed.
106
+ *
107
+ * @param receipt - Base64-encoded receipt data from the App Store
108
+ * @param config - Configuration including shared secret and optional settings
109
+ * @returns Validated receipt response from Apple
110
+ * @throws {IAPAppleError} When validation fails or receipt is invalid
111
+ */
112
+ declare function verify(receipt: string, config: IIAPAppleConfig): Promise<IVerifyReceiptResponseBody>;
113
+ /**
114
+ * Checks whether the receipt validation was successful.
115
+ *
116
+ * @param verifyReceiptResponse - Response from Apple's verifyReceipt endpoint
117
+ * @returns True if the receipt status indicates success
118
+ */
119
+ declare const isVerifiedReceipt: (verifyReceiptResponse: IVerifyReceiptResponseBody | null) => boolean;
120
+ /**
121
+ * Determines if a purchased item has expired (cancelled or past expiration date).
122
+ *
123
+ * @param purchasedItem - The purchased item to check
124
+ * @returns True if the item has been cancelled or its expiration date has passed
125
+ * @throws {Error} If purchasedItem is invalid or missing transactionId
126
+ */
127
+ declare const isPurchasedItemExpired: (purchasedItem: PurchasedItem | null) => boolean;
128
+ /**
129
+ * Checks if a purchased item has been cancelled.
130
+ *
131
+ * @param purchasedItem - The purchased item to check
132
+ * @returns True if the item has a cancellation date
133
+ * @throws {Error} If purchasedItem is invalid or missing transactionId
134
+ */
135
+ declare const isPurchasedItemCanceled: (purchasedItem: PurchasedItem) => boolean;
136
+ /**
137
+ * Extracts purchased items from a validated receipt response.
138
+ * Combines in_app and latest_receipt_info, deduplicates by original_transaction_id,
139
+ * and returns items sorted by purchase date (newest first).
140
+ *
141
+ * @param verifyReceiptResponse - Response from Apple's verifyReceipt endpoint
142
+ * @returns Array of purchased items, deduplicated and sorted by purchase date descending
143
+ */
144
+ declare const getPurchasedItems: (verifyReceiptResponse: IVerifyReceiptResponseBody | null) => PurchasedItem[];
145
+
146
+ export { type IAPAppleError, type IIAPAppleConfig, type ILogger, type IPendingRenewalInfo, type IReceiptInAppItem, type IVerifyReceiptResponseBody, type PurchasedItem, RECEIPT_STATUS_ENUM, getPurchasedItems, isPurchasedItemCanceled, isPurchasedItemExpired, isVerifiedReceipt, verify };
package/dist/index.js ADDED
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ RECEIPT_STATUS_ENUM: () => RECEIPT_STATUS_ENUM,
24
+ getPurchasedItems: () => getPurchasedItems,
25
+ isPurchasedItemCanceled: () => isPurchasedItemCanceled,
26
+ isPurchasedItemExpired: () => isPurchasedItemExpired,
27
+ isVerifiedReceipt: () => isVerifiedReceipt,
28
+ verify: () => verify
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/constants/shared/index.ts
33
+ var RECEIPT_STATUS_ENUM = /* @__PURE__ */ ((RECEIPT_STATUS_ENUM2) => {
34
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["SUCCESS"] = 0] = "SUCCESS";
35
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["VALID_NO_PURCHASE"] = 2] = "VALID_NO_PURCHASE";
36
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["CANNOT_READ_JSON"] = 21e3] = "CANNOT_READ_JSON";
37
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["DATA_MALFORMED"] = 21002] = "DATA_MALFORMED";
38
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["RECEIPT_NOT_AUTHENTICATED"] = 21003] = "RECEIPT_NOT_AUTHENTICATED";
39
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["SHARED_SECRET_DOESNT_MATCH"] = 21004] = "SHARED_SECRET_DOESNT_MATCH";
40
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["SERVER_NOT_AVAILABLE"] = 21005] = "SERVER_NOT_AVAILABLE";
41
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["SUBSCRIPTION_EXPIRED"] = 21006] = "SUBSCRIPTION_EXPIRED";
42
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["TEST_ENV_RECEIPT_DETECTED"] = 21007] = "TEST_ENV_RECEIPT_DETECTED";
43
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["PRODUCTION_ENV_RECEIPT_DETECTED"] = 21008] = "PRODUCTION_ENV_RECEIPT_DETECTED";
44
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["INTERNAL_DATA_ACCESS_ERROR"] = 21009] = "INTERNAL_DATA_ACCESS_ERROR";
45
+ RECEIPT_STATUS_ENUM2[RECEIPT_STATUS_ENUM2["USER_ACCOUNT_DELETED"] = 21010] = "USER_ACCOUNT_DELETED";
46
+ return RECEIPT_STATUS_ENUM2;
47
+ })(RECEIPT_STATUS_ENUM || {});
48
+
49
+ // src/constants/internal/index.ts
50
+ var STATUS_TO_MESSAGE_MAP = {
51
+ 21e3: "The App Store could not read the JSON object you provided.",
52
+ 21002: "The data in the receipt-data property was malformed.",
53
+ 21003: "The receipt could not be authenticated.",
54
+ 21004: "The shared secret you provided does not match the shared secret on file for your account.",
55
+ 21005: "The receipt server is not currently available.",
56
+ 21006: "This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response.",
57
+ 21007: "This receipt is a sandbox receipt, but it was sent to the production service for verification.",
58
+ 21008: "This receipt is a production receipt, but it was sent to the sandbox service for verification.",
59
+ 21009: "Internal data access error. Try again later",
60
+ 21010: "The user account cannot be found or has been deleted",
61
+ 2: "The receipt is valid, but purchased nothing.",
62
+ 0: "No error."
63
+ };
64
+ var PROD_PATH = "https://buy.itunes.apple.com/verifyReceipt";
65
+ var SANDBOX_PATH = "https://sandbox.itunes.apple.com/verifyReceipt";
66
+
67
+ // src/lib/internal/index.ts
68
+ function prefixMessage(message) {
69
+ return `[iap-apple] ${message}`;
70
+ }
71
+ function isSubscriptionExpired(responseData) {
72
+ const expirationDates = (responseData.latest_receipt_info || []).filter((lri) => lri.expires_date_ms).map((lri) => parseInt(lri.expires_date_ms, 10));
73
+ if (expirationDates.length === 0) {
74
+ return false;
75
+ }
76
+ const latestExpirationDate = Math.max(...expirationDates);
77
+ return latestExpirationDate < Date.now();
78
+ }
79
+ async function verifyReceiptApple(url, content) {
80
+ const response = await fetch(url, {
81
+ method: "POST",
82
+ headers: {
83
+ "Content-Type": "application/json"
84
+ },
85
+ body: JSON.stringify(content)
86
+ });
87
+ if (!response.ok) {
88
+ throw new Error(`HTTP error: ${response.status}`);
89
+ }
90
+ return await response.json();
91
+ }
92
+ function getPurchaseItem(item, purchase) {
93
+ return {
94
+ quantity: parseInt(item.quantity, 10),
95
+ productId: item.product_id,
96
+ transactionId: item.transaction_id,
97
+ originalTransactionId: item.original_transaction_id,
98
+ bundleId: purchase.receipt.bundle_id,
99
+ appItemId: item.app_item_id,
100
+ originalPurchaseDateMS: parseInt(item.original_purchase_date_ms, 10),
101
+ purchaseDateMS: parseInt(item.purchase_date_ms, 10),
102
+ cancellationDateMS: item.cancellation_date_ms ? parseInt(item.cancellation_date_ms, 10) : void 0,
103
+ isTrialPeriod: item.is_trial_period === "true",
104
+ expirationDateMS: item.expires_date_ms ? parseInt(item.expires_date_ms, 10) : void 0
105
+ };
106
+ }
107
+ var verifyReceipt = async function({
108
+ logger,
109
+ validationEndpoint,
110
+ receiptData,
111
+ appSharedSecret,
112
+ excludeOldTransactions
113
+ }) {
114
+ return new Promise(async (resolve, reject) => {
115
+ const content = {
116
+ "receipt-data": receiptData,
117
+ password: appSharedSecret,
118
+ "exclude-old-transactions": excludeOldTransactions
119
+ };
120
+ logger?.log(prefixMessage(`Validating against: ${validationEndpoint} endpoint`));
121
+ logger?.log(prefixMessage(`Validation data: ${JSON.stringify(content, null, 2)}`));
122
+ try {
123
+ const data = await verifyReceiptApple(validationEndpoint, content);
124
+ logger?.log(prefixMessage(`Endpoint ${validationEndpoint} response: ${JSON.stringify(data, null, 2)}`));
125
+ if (data.status !== 0 /* SUCCESS */ && data.status !== 21007 /* TEST_ENV_RECEIPT_DETECTED */ && data.status !== 21002 /* DATA_MALFORMED */) {
126
+ if (data.status === 21006 /* SUBSCRIPTION_EXPIRED */ && !isSubscriptionExpired(data)) {
127
+ logger?.log(prefixMessage("Valid receipt, but has been cancelled (not expired yet)"));
128
+ resolve({
129
+ ...data,
130
+ status: 0 /* SUCCESS */
131
+ });
132
+ return;
133
+ }
134
+ logger?.error(prefixMessage(`Endpoint ${validationEndpoint} failed: ${JSON.stringify(data, null, 2)}`));
135
+ reject({
136
+ rejectionMessage: STATUS_TO_MESSAGE_MAP[data.status] || "Unknown",
137
+ data
138
+ });
139
+ return;
140
+ }
141
+ if (data.status === 21007 /* TEST_ENV_RECEIPT_DETECTED */) {
142
+ resolve(null);
143
+ return;
144
+ }
145
+ if (data.status === 21002 /* DATA_MALFORMED */) {
146
+ reject({
147
+ rejectionMessage: STATUS_TO_MESSAGE_MAP[data.status] || "Unknown",
148
+ data
149
+ });
150
+ return;
151
+ }
152
+ logger?.log(prefixMessage(`Validation successful: ${JSON.stringify(data, null, 2)}`));
153
+ resolve(data);
154
+ } catch (error) {
155
+ logger?.error(prefixMessage(`Endpoint ${validationEndpoint} failed: ${error}`));
156
+ reject({
157
+ rejectionMessage: error?.message,
158
+ data: null
159
+ });
160
+ }
161
+ });
162
+ };
163
+
164
+ // src/lib/shared/index.ts
165
+ async function verify(receipt, config) {
166
+ const { appleExcludeOldTransactions, logger, test, appSharedSecret } = config;
167
+ return new Promise(async (resolve, reject) => {
168
+ let verifyReceiptResponse = null;
169
+ try {
170
+ if (!test) {
171
+ verifyReceiptResponse = await verifyReceipt({
172
+ logger,
173
+ validationEndpoint: PROD_PATH,
174
+ receiptData: receipt,
175
+ appSharedSecret,
176
+ excludeOldTransactions: Boolean(appleExcludeOldTransactions)
177
+ });
178
+ }
179
+ if (!verifyReceiptResponse) {
180
+ verifyReceiptResponse = await verifyReceipt({
181
+ logger,
182
+ validationEndpoint: SANDBOX_PATH,
183
+ receiptData: receipt,
184
+ appSharedSecret,
185
+ excludeOldTransactions: Boolean(appleExcludeOldTransactions)
186
+ });
187
+ }
188
+ if (!verifyReceiptResponse) {
189
+ reject({
190
+ rejectionMessage: "Unable to validate receipt using appstore endpoints.",
191
+ data: null
192
+ });
193
+ return;
194
+ }
195
+ } catch (err) {
196
+ reject(err);
197
+ return;
198
+ }
199
+ if (verifyReceiptResponse.status === 0 /* SUCCESS */) {
200
+ if (verifyReceiptResponse.receipt?.in_app && verifyReceiptResponse.receipt?.in_app?.length === 0) {
201
+ reject({
202
+ rejectionMessage: "Detected valid receipt, however purchase list is empty",
203
+ data: verifyReceiptResponse
204
+ });
205
+ }
206
+ resolve(verifyReceiptResponse);
207
+ return;
208
+ }
209
+ reject({
210
+ rejectionMessage: STATUS_TO_MESSAGE_MAP[verifyReceiptResponse.status],
211
+ data: verifyReceiptResponse
212
+ });
213
+ });
214
+ }
215
+ var isVerifiedReceipt = function(verifyReceiptResponse) {
216
+ return verifyReceiptResponse?.status === 0 /* SUCCESS */;
217
+ };
218
+ var isPurchasedItemExpired = function(purchasedItem) {
219
+ if (!purchasedItem?.transactionId) {
220
+ throw new Error("Detected invalid purchased item! Make sure object is defined and it has transaction id.");
221
+ }
222
+ if (purchasedItem.cancellationDateMS) {
223
+ return true;
224
+ }
225
+ if (!purchasedItem.expirationDateMS) {
226
+ return false;
227
+ }
228
+ return Date.now().valueOf() - purchasedItem.expirationDateMS >= 0;
229
+ };
230
+ var isPurchasedItemCanceled = function(purchasedItem) {
231
+ if (!purchasedItem?.transactionId) {
232
+ throw new Error("Detected invalid purchased item! Make sure object is defined and it has transaction id.");
233
+ }
234
+ return Boolean(purchasedItem.cancellationDateMS);
235
+ };
236
+ var getPurchasedItems = function(verifyReceiptResponse) {
237
+ if (!verifyReceiptResponse?.receipt) {
238
+ return [];
239
+ }
240
+ const data = [];
241
+ let purchases = verifyReceiptResponse.receipt.in_app || [];
242
+ const lri = verifyReceiptResponse.latest_receipt_info || verifyReceiptResponse.receipt.latest_receipt_info;
243
+ if (Array.isArray(lri)) {
244
+ purchases = purchases.concat(lri);
245
+ }
246
+ purchases.sort((a, b) => parseInt(b.purchase_date_ms, 10) - parseInt(a.purchase_date_ms, 10));
247
+ const transactionIds = {};
248
+ for (let i = 0; i < purchases.length; i++) {
249
+ const item = purchases[i];
250
+ const tid = item.original_transaction_id;
251
+ if (transactionIds[tid]) {
252
+ continue;
253
+ }
254
+ data.push(getPurchaseItem(item, verifyReceiptResponse));
255
+ transactionIds[tid] = true;
256
+ }
257
+ return data;
258
+ };
259
+ // Annotate the CommonJS export names for ESM import in node:
260
+ 0 && (module.exports = {
261
+ RECEIPT_STATUS_ENUM,
262
+ getPurchasedItems,
263
+ isPurchasedItemCanceled,
264
+ isPurchasedItemExpired,
265
+ isVerifiedReceipt,
266
+ verify
267
+ });