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 +1 -1
- package/dist/index.d.mts +146 -0
- package/dist/index.d.ts +146 -0
- package/dist/index.js +267 -0
- package/dist/index.mjs +235 -0
- package/package.json +61 -53
- package/CHANGELOG.md +0 -73
- package/constants/index.d.ts +0 -2
- package/constants/index.js +0 -18
- package/constants/internal/index.d.ts +0 -4
- package/constants/internal/index.js +0 -19
- package/constants/shared/index.d.ts +0 -14
- package/constants/shared/index.js +0 -31
- package/index.d.ts +0 -3
- package/index.js +0 -19
- package/lib/internal/index.d.ts +0 -10
- package/lib/internal/index.js +0 -185
- package/lib/shared/index.d.ts +0 -35
- package/lib/shared/index.js +0 -216
- package/types/index.d.ts +0 -2
- package/types/index.js +0 -18
- package/types/internal/index.d.ts +0 -18
- package/types/internal/index.js +0 -2
- package/types/shared/index.d.ts +0 -52
- package/types/shared/index.js +0 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
| Statements | Branches | Functions | Lines |
|
|
2
2
|
| --------------------------- | ----------------------- | ------------------------- | ----------------- |
|
|
3
|
-
|  |  |  |  |
|
|
4
4
|
|
|
5
5
|
# iap-apple
|
|
6
6
|
|
package/dist/index.d.mts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
});
|