iap-apple 1.3.7 → 1.3.9

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 CHANGED
@@ -1,3 +1,12 @@
1
+ ## [1.3.8](https://github.com/ssbarbee/iap-apple/compare/v1.3.7...v1.3.8) (2022-12-22)
2
+
3
+ ## [1.3.7](https://github.com/ssbarbee/iap-apple/compare/v1.3.6...v1.3.7) (2022-12-22)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * add README, CHANGELOG and LICENCE to dist ([deb9be6](https://github.com/ssbarbee/iap-apple/commit/deb9be6090ffba51599ea5424f9fd84ce967a87a))
9
+
1
10
  ## [1.3.6](https://github.com/ssbarbee/iap-apple/compare/v1.3.5...v1.3.6) (2022-12-22)
2
11
 
3
12
 
package/README.md CHANGED
@@ -1,6 +1,175 @@
1
1
  | Statements | Branches | Functions | Lines |
2
2
  | --------------------------- | ----------------------- | ------------------------- | ----------------- |
3
- | ![Statements](https://img.shields.io/badge/statements-74.63%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-51.3%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-73.68%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-72.44%25-red.svg?style=flat) |
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) |
4
4
 
5
5
  # iap-apple
6
- Integration with Apples InAppPurchases in Typescript, available for NodeJS environments.
6
+ 📦🚀 Integration of Apples **validation service** for App Store Receipts, written in Typescript, available for NodeJS environments.
7
+
8
+ A NodeJS module for in-app purchase (in-app billing) and subscription for Apple.
9
+
10
+ ## Overview
11
+
12
+ Create a Typescript package for validation of [App Store Receipts](https://developer.apple.com/documentation/appstorereceipts).
13
+ This package is meant to be used server side to validate receipts from the App Store by talking to Apples servers.
14
+
15
+ ## Installation
16
+
17
+ ### npm
18
+
19
+ ```npm install iap-apple```
20
+
21
+ ### yarn
22
+
23
+ ```yarn add iap-apple```
24
+
25
+ ## API documentation
26
+
27
+ ### verify
28
+
29
+ API used to verify receipt data received during an App Store purchase.
30
+ Requires **appSharedSecret** to be passed as part of the configuration.
31
+ See example for more details.
32
+
33
+ ```typescript
34
+
35
+ import { verify, IAPAppleError, IVerifyReceiptResponseBody } from 'iap-apple';
36
+
37
+ async function verifyAppleReceipt(receipt: string) {
38
+ try {
39
+ const verifyReceiptResponse = await verify(receipt, {
40
+ /*
41
+ Your app's shared secret, which is a hexadecimal string. For more information about the shared secret.
42
+ https://help.apple.com/app-store-connect/#/devf341c0f01
43
+ */
44
+ appSharedSecret,
45
+ /*
46
+ To exclude old transaction, set this to true.
47
+ Default is false.
48
+ */
49
+ excludeOldTransactions: false,
50
+ /*
51
+ Force validation against Apple Sandbox only.
52
+ In effect this means that the validation against Apple Production endpoint won't be used.
53
+ Default is false.
54
+ */
55
+ test: false,
56
+ /*
57
+ Optional can be omitted, pass logger object if you want to debug.
58
+ Default is null object.
59
+ */
60
+ logger: console,
61
+ });
62
+ console.log('verifyReceiptResponse', verifyReceiptResponse);
63
+ } catch(error) {
64
+ const iapAppleError = error as IAPAppleError;
65
+ const rejectionMessage: string = error.rejectionMessage;
66
+ const errorData: IVerifyReceiptResponseBody | null = error.data;
67
+ console.error('Error happened', rejectionMessage);
68
+ console.error('Details', errorData);
69
+ }
70
+ }
71
+ ```
72
+
73
+ ### isVerifiedReceipt
74
+
75
+ API used to verify if response returned by `verify` is verified.
76
+ Requires the output of `verify` to be passed.
77
+ See example for more details.
78
+
79
+ ```typescript
80
+
81
+ import { verify, isVerifiedReceipt, IIAPAppleConfig } from 'iap-apple';
82
+
83
+ async function isVerifiedAppleReceipt(receipt: string, config: IIAPAppleConfig) {
84
+ try {
85
+ const verifyReceiptResponse = await verify(receipt, config);
86
+ const isVerifiedReceipt = isVerifiedReceipt(verifyReceiptResponse);
87
+ } catch(error) {
88
+ const iapAppleError = error as IAPAppleError;
89
+ const rejectionMessage: string = error.rejectionMessage;
90
+ const errorData: IVerifyReceiptResponseBody | null = error.data;
91
+ console.error('Error happened', rejectionMessage);
92
+ console.error('Details', errorData);
93
+ }
94
+ }
95
+ ```
96
+
97
+ ### getPurchasedItems
98
+
99
+ API used to get an array of PurchasedItem objects from the Apple App Store response,
100
+ sort by their purchase date descending.
101
+ Usually what we are interested in is the first item of the purchase.
102
+ Requires the output of `verify` to be passed.
103
+ See example for more details.
104
+
105
+ ```typescript
106
+
107
+ import { verify, getPurchasedItems, IIAPAppleConfig } from 'iap-apple';
108
+
109
+ async function isVerifiedAppleReceipt(receipt: string, config: IIAPAppleConfig) {
110
+ try {
111
+ const verifyReceiptResponse = await verify(receipt, config);
112
+ const purchasedItems = getPurchasedItems(verifyReceiptResponse);
113
+ const latestPurchase = purchasedItems[0];
114
+ } catch(error) {
115
+ const iapAppleError = error as IAPAppleError;
116
+ const rejectionMessage: string = error.rejectionMessage;
117
+ const errorData: IVerifyReceiptResponseBody | null = error.data;
118
+ console.error('Error happened', rejectionMessage);
119
+ console.error('Details', errorData);
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### isPurchasedItemCanceled
125
+
126
+ API used to check if a purchased item is canceled.
127
+ Requires the output of `getPurchasedItems` to be passed.
128
+ See example for more details.
129
+
130
+ ```typescript
131
+
132
+ import { verify, getPurchasedItems, isPurchasedItemCanceled, IIAPAppleConfig } from 'iap-apple';
133
+
134
+ async function isVerifiedAppleReceipt(receipt: string, config: IIAPAppleConfig) {
135
+ try {
136
+ const verifyReceiptResponse = await verify(receipt, config);
137
+ const purchasedItems = getPurchasedItems(verifyReceiptResponse);
138
+ const latestPurchase = purchasedItems[0];
139
+ const isCanceled = isPurchasedItemCanceled(latestPurchase);
140
+ } catch(error) {
141
+ const iapAppleError = error as IAPAppleError;
142
+ const rejectionMessage: string = error.rejectionMessage;
143
+ const errorData: IVerifyReceiptResponseBody | null = error.data;
144
+ console.error('Error happened', rejectionMessage);
145
+ console.error('Details', errorData);
146
+ }
147
+ }
148
+ ```
149
+
150
+
151
+ ### isPurchasedItemExpired
152
+
153
+ API used to check if a purchased item is expired.
154
+ Requires the output of `getPurchasedItems` to be passed.
155
+ See example for more details.
156
+
157
+ ```typescript
158
+
159
+ import { verify, getPurchasedItems, isPurchasedItemExpired, IIAPAppleConfig } from 'iap-apple';
160
+
161
+ async function isVerifiedAppleReceipt(receipt: string, config: IIAPAppleConfig) {
162
+ try {
163
+ const verifyReceiptResponse = await verify(receipt, config);
164
+ const purchasedItems = getPurchasedItems(verifyReceiptResponse);
165
+ const latestPurchase = purchasedItems[0];
166
+ const isExpired = isPurchasedItemExpired(latestPurchase);
167
+ } catch(error) {
168
+ const iapAppleError = error as IAPAppleError;
169
+ const rejectionMessage: string = error.rejectionMessage;
170
+ const errorData: IVerifyReceiptResponseBody | null = error.data;
171
+ console.error('Error happened', rejectionMessage);
172
+ console.error('Details', errorData);
173
+ }
174
+ }
175
+ ```
@@ -1,22 +1,10 @@
1
- import { ILogger, IReceiptInAppItem, IReceiptValidationResponseBody } from '../../types';
2
- export declare function isExpiredAppleResponse(responseData: IReceiptValidationResponseBody): boolean;
3
- export declare function getPurchaseItem(item: IReceiptInAppItem, purchase: IReceiptValidationResponseBody): {
4
- quantity: number;
5
- productId: string;
6
- transactionId: string;
7
- originalTransactionId: string;
8
- bundleId: string;
9
- appItemId: string;
10
- originalPurchaseDate: number;
11
- purchaseDateMS: number;
12
- cancellationDateMS: number | undefined;
13
- isTrialPeriod: boolean;
14
- expirationDateMS: number | undefined;
15
- };
16
- export declare const validateReceipt: ({ logger, validationEndpoint, receipt, password, excludeOldTransactions, }: {
1
+ import { ILogger, IReceiptInAppItem, IVerifyReceiptResponseBody, PurchasedItem } from '../../types';
2
+ export declare function isExpiredReceipt(responseData: IVerifyReceiptResponseBody): boolean;
3
+ export declare function getPurchaseItem(item: IReceiptInAppItem, purchase: IVerifyReceiptResponseBody): PurchasedItem;
4
+ export declare const verifyReceipt: ({ logger, validationEndpoint, receiptData, appSharedSecret, excludeOldTransactions, }: {
17
5
  logger?: ILogger | null | undefined;
18
6
  validationEndpoint: string;
19
- receipt: string;
20
- password: string | undefined;
7
+ receiptData: string;
8
+ appSharedSecret: string;
21
9
  excludeOldTransactions: boolean;
22
- }) => Promise<IReceiptValidationResponseBody | null>;
10
+ }) => Promise<IVerifyReceiptResponseBody | null>;
@@ -47,21 +47,24 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
47
47
  }
48
48
  };
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
- exports.validateReceipt = exports.getPurchaseItem = exports.isExpiredAppleResponse = void 0;
50
+ exports.verifyReceipt = exports.getPurchaseItem = exports.isExpiredReceipt = void 0;
51
51
  var constants_1 = require("../../constants");
52
52
  var request = require("superagent");
53
- function isExpiredAppleResponse(responseData) {
53
+ function prefixMessage(message) {
54
+ return "[iap-apple] ".concat(message);
55
+ }
56
+ function isExpiredReceipt(responseData) {
54
57
  var date = Math.max.apply(Math, (responseData.latest_receipt_info || [])
55
58
  .filter(function (lri) { return lri.expires_date_ms; })
56
59
  .map(function (lri) { return parseInt(lri.expires_date_ms, 10); }));
57
60
  if (date) {
58
- return date > Date.now();
61
+ return date > Date.now().valueOf();
59
62
  }
60
63
  // old receipt
61
64
  return false;
62
65
  }
63
- exports.isExpiredAppleResponse = isExpiredAppleResponse;
64
- function sendRequest(url, content) {
66
+ exports.isExpiredReceipt = isExpiredReceipt;
67
+ function verifyReceiptApple(url, content) {
65
68
  return __awaiter(this, void 0, void 0, function () {
66
69
  return __generator(this, function (_a) {
67
70
  return [2 /*return*/, new Promise(function (resolve, reject) {
@@ -93,7 +96,7 @@ function getPurchaseItem(item, purchase) {
93
96
  originalTransactionId: item.original_transaction_id,
94
97
  bundleId: purchase.receipt.bundle_id,
95
98
  appItemId: item.app_item_id,
96
- originalPurchaseDate: parseInt(item.original_purchase_date_ms, 10),
99
+ originalPurchaseDateMS: parseInt(item.original_purchase_date_ms, 10),
97
100
  purchaseDateMS: parseInt(item.purchase_date_ms, 10),
98
101
  cancellationDateMS: item.cancellation_date_ms ? parseInt(item.cancellation_date_ms, 10) : undefined,
99
102
  isTrialPeriod: item.is_trial_period === 'true',
@@ -101,8 +104,8 @@ function getPurchaseItem(item, purchase) {
101
104
  };
102
105
  }
103
106
  exports.getPurchaseItem = getPurchaseItem;
104
- var validateReceipt = function (_a) {
105
- var logger = _a.logger, validationEndpoint = _a.validationEndpoint, receipt = _a.receipt, password = _a.password, excludeOldTransactions = _a.excludeOldTransactions;
107
+ var verifyReceipt = function (_a) {
108
+ var logger = _a.logger, validationEndpoint = _a.validationEndpoint, receiptData = _a.receiptData, appSharedSecret = _a.appSharedSecret, excludeOldTransactions = _a.excludeOldTransactions;
106
109
  return __awaiter(this, void 0, void 0, function () {
107
110
  var _this = this;
108
111
  return __generator(this, function (_b) {
@@ -112,35 +115,35 @@ var validateReceipt = function (_a) {
112
115
  switch (_a.label) {
113
116
  case 0:
114
117
  content = {
115
- 'receipt-data': receipt,
116
- password: password,
118
+ 'receipt-data': receiptData,
119
+ password: appSharedSecret,
117
120
  'exclude-old-transactions': excludeOldTransactions,
118
121
  };
119
- logger === null || logger === void 0 ? void 0 : logger.log("[iap-apple] Validating against: ".concat(validationEndpoint, " endpoint"));
120
- logger === null || logger === void 0 ? void 0 : logger.log("[iap-apple] Validation data: ".concat(JSON.stringify(content, null, 2)));
122
+ logger === null || logger === void 0 ? void 0 : logger.log(prefixMessage("Validating against: ".concat(validationEndpoint, " endpoint")));
123
+ logger === null || logger === void 0 ? void 0 : logger.log(prefixMessage("Validation data: ".concat(JSON.stringify(content, null, 2))));
121
124
  _a.label = 1;
122
125
  case 1:
123
126
  _a.trys.push([1, 3, , 4]);
124
- return [4 /*yield*/, sendRequest(validationEndpoint, content)];
127
+ return [4 /*yield*/, verifyReceiptApple(validationEndpoint, content)];
125
128
  case 2:
126
129
  data = _a.sent();
127
- logger === null || logger === void 0 ? void 0 : logger.log("[iap-apple] Endpoint ".concat(validationEndpoint, " response: ").concat(JSON.stringify(data, null, 2)));
130
+ logger === null || logger === void 0 ? void 0 : logger.log(prefixMessage("Endpoint ".concat(validationEndpoint, " response: ").concat(JSON.stringify(data, null, 2))));
128
131
  // apple responded with error
129
132
  if (data.status !== constants_1.RECEIPT_STATUS_ENUM.SUCCESS &&
130
133
  data.status !== constants_1.RECEIPT_STATUS_ENUM.TEST_ENV_RECEIPT_DETECTED &&
131
134
  data.status !== constants_1.RECEIPT_STATUS_ENUM.DATA_MALFORMED) {
132
- if (data.status === constants_1.RECEIPT_STATUS_ENUM.SUBSCRIPTION_EXPIRED && !isExpiredAppleResponse(data)) {
135
+ if (data.status === constants_1.RECEIPT_STATUS_ENUM.SUBSCRIPTION_EXPIRED && !isExpiredReceipt(data)) {
133
136
  /*
134
- detected valid subscription receipt,
135
- however it was cancelled, and it has not been expired
136
- status code is 21006 for both expired receipt and cancelled receipt...
137
- */
138
- logger === null || logger === void 0 ? void 0 : logger.log('[iap-apple] Valid receipt, but has been cancelled (not expired yet)');
137
+ detected valid subscription receipt,
138
+ however it was cancelled, and it has not been expired
139
+ status code is 21006 for both expired receipt and cancelled receipt...
140
+ */
141
+ logger === null || logger === void 0 ? void 0 : logger.log(prefixMessage('Valid receipt, but has been cancelled (not expired yet)'));
139
142
  // force status to be SUCCESS
140
143
  resolve(__assign(__assign({}, data), { status: constants_1.RECEIPT_STATUS_ENUM.SUCCESS }));
141
144
  return [2 /*return*/];
142
145
  }
143
- logger === null || logger === void 0 ? void 0 : logger.error("[iap-apple] Endpoint ".concat(validationEndpoint, " failed: ").concat(JSON.stringify(data, null, 2)));
146
+ logger === null || logger === void 0 ? void 0 : logger.error(prefixMessage("Endpoint ".concat(validationEndpoint, " failed: ").concat(JSON.stringify(data, null, 2))));
144
147
  reject({
145
148
  rejectionMessage: constants_1.STATUS_TO_MESSAGE_MAP[data.status] || 'Unknown',
146
149
  data: data,
@@ -160,12 +163,12 @@ var validateReceipt = function (_a) {
160
163
  return [2 /*return*/];
161
164
  }
162
165
  // receipt validated
163
- logger === null || logger === void 0 ? void 0 : logger.log("[iap-apple] Validation successful: ".concat(JSON.stringify(data, null, 2)));
166
+ logger === null || logger === void 0 ? void 0 : logger.log(prefixMessage("Validation successful: ".concat(JSON.stringify(data, null, 2))));
164
167
  resolve(data);
165
168
  return [3 /*break*/, 4];
166
169
  case 3:
167
170
  error_1 = _a.sent();
168
- logger === null || logger === void 0 ? void 0 : logger.error("[iap-apple] Endpoint ".concat(validationEndpoint, " failed: ").concat(error_1));
171
+ logger === null || logger === void 0 ? void 0 : logger.error(prefixMessage("Endpoint ".concat(validationEndpoint, " failed: ").concat(error_1)));
169
172
  reject({
170
173
  rejectionMessage: error_1 === null || error_1 === void 0 ? void 0 : error_1.message,
171
174
  data: null,
@@ -179,4 +182,4 @@ var validateReceipt = function (_a) {
179
182
  });
180
183
  });
181
184
  };
182
- exports.validateReceipt = validateReceipt;
185
+ exports.verifyReceipt = verifyReceipt;
@@ -1,6 +1,35 @@
1
- import { IIAPAppleConfig, IReceiptValidationResponseBody, PurchasedItem } from '../../types';
2
- export declare function validate(receipt: string, config: IIAPAppleConfig): Promise<IReceiptValidationResponseBody>;
3
- export declare const isValidated: (response: IReceiptValidationResponseBody) => boolean;
4
- export declare const isExpired: (purchasedItem: PurchasedItem) => boolean;
5
- export declare const isCanceled: (purchasedItem: PurchasedItem) => boolean;
6
- export declare const getPurchaseData: (purchase?: IReceiptValidationResponseBody) => PurchasedItem[];
1
+ import { IIAPAppleConfig, IVerifyReceiptResponseBody, PurchasedItem } from '../../types';
2
+ /**
3
+ * It takes a receipt data and a config object, and returns a promise that resolves to a validated receipt object or
4
+ * rejects with an error
5
+ * @param {string} receipt - The receipt data.
6
+ * @param {IIAPAppleConfig} config - IIAPAppleConfig
7
+ * @returns a promise.
8
+ */
9
+ export declare function verify(receipt: string, config: IIAPAppleConfig): Promise<IVerifyReceiptResponseBody>;
10
+ /**
11
+ * It checks if the receipt is valid.
12
+ * @param {IVerifyReceiptResponseBody | null} verifyReceiptResponse - IVerifyReceiptResponseBody | null
13
+ * @returns A boolean
14
+ */
15
+ export declare const isVerifiedReceipt: (verifyReceiptResponse: IVerifyReceiptResponseBody | null) => boolean;
16
+ /**
17
+ * If the purchased item has been cancelled or if the expiration date has passed, then it has expired
18
+ * @param {PurchasedItem | null} purchasedItem - PurchasedItem | null
19
+ * @returns A boolean
20
+ */
21
+ export declare const isPurchasedItemExpired: (purchasedItem: PurchasedItem | null) => boolean;
22
+ /**
23
+ * If the purchased item has a cancellation date, then it's canceled.
24
+ * @param {PurchasedItem} purchasedItem - PurchasedItem - this is the purchased item object that you get from the
25
+ * getPurchasedItems() method.
26
+ * @returns A boolean value.
27
+ */
28
+ export declare const isPurchasedItemCanceled: (purchasedItem: PurchasedItem) => boolean;
29
+ /**
30
+ * It takes a response from the Apple App Store and returns an array of PurchasedItem objects sorted by their purchase date in descending order,
31
+ * the latest purchase comes first.
32
+ * @param {IVerifyReceiptResponseBody | null} verifyReceiptResponse - IVerifyReceiptResponseBody | null
33
+ * @returns An array of PurchasedItem objects.
34
+ */
35
+ export declare const getPurchasedItems: (verifyReceiptResponse: IVerifyReceiptResponseBody | null) => PurchasedItem[];
@@ -36,50 +36,57 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
36
36
  }
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.getPurchaseData = exports.isCanceled = exports.isExpired = exports.isValidated = exports.validate = void 0;
39
+ exports.getPurchasedItems = exports.isPurchasedItemCanceled = exports.isPurchasedItemExpired = exports.isVerifiedReceipt = exports.verify = void 0;
40
40
  var internal_1 = require("../internal");
41
41
  var constants_1 = require("../../constants");
42
- function validate(receipt, config) {
42
+ /**
43
+ * It takes a receipt data and a config object, and returns a promise that resolves to a validated receipt object or
44
+ * rejects with an error
45
+ * @param {string} receipt - The receipt data.
46
+ * @param {IIAPAppleConfig} config - IIAPAppleConfig
47
+ * @returns a promise.
48
+ */
49
+ function verify(receipt, config) {
43
50
  return __awaiter(this, void 0, void 0, function () {
44
- var logger;
51
+ var appleExcludeOldTransactions, logger, test, appSharedSecret;
45
52
  var _this = this;
46
53
  return __generator(this, function (_a) {
47
- logger = config.logger;
54
+ appleExcludeOldTransactions = config.appleExcludeOldTransactions, logger = config.logger, test = config.test, appSharedSecret = config.appSharedSecret;
48
55
  return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
49
- var validatedData, err_1;
56
+ var verifyReceiptResponse, err_1;
50
57
  var _a, _b, _c;
51
58
  return __generator(this, function (_d) {
52
59
  switch (_d.label) {
53
60
  case 0:
54
- validatedData = null;
61
+ verifyReceiptResponse = null;
55
62
  _d.label = 1;
56
63
  case 1:
57
64
  _d.trys.push([1, 6, , 7]);
58
- if (!!config.test) return [3 /*break*/, 3];
59
- return [4 /*yield*/, (0, internal_1.validateReceipt)({
65
+ if (!!test) return [3 /*break*/, 3];
66
+ return [4 /*yield*/, (0, internal_1.verifyReceipt)({
60
67
  logger: logger,
61
68
  validationEndpoint: constants_1.PROD_PATH,
62
- receipt: receipt,
63
- password: config.applePassword,
64
- excludeOldTransactions: Boolean(config.appleExcludeOldTransactions),
69
+ receiptData: receipt,
70
+ appSharedSecret: appSharedSecret,
71
+ excludeOldTransactions: Boolean(appleExcludeOldTransactions),
65
72
  })];
66
73
  case 2:
67
- validatedData = _d.sent();
74
+ verifyReceiptResponse = _d.sent();
68
75
  _d.label = 3;
69
76
  case 3:
70
- if (!!validatedData) return [3 /*break*/, 5];
71
- return [4 /*yield*/, (0, internal_1.validateReceipt)({
77
+ if (!!verifyReceiptResponse) return [3 /*break*/, 5];
78
+ return [4 /*yield*/, (0, internal_1.verifyReceipt)({
72
79
  logger: logger,
73
80
  validationEndpoint: constants_1.SANDBOX_PATH,
74
- receipt: receipt,
75
- password: config.applePassword,
76
- excludeOldTransactions: Boolean(config.appleExcludeOldTransactions),
81
+ receiptData: receipt,
82
+ appSharedSecret: appSharedSecret,
83
+ excludeOldTransactions: Boolean(appleExcludeOldTransactions),
77
84
  })];
78
85
  case 4:
79
- validatedData = _d.sent();
86
+ verifyReceiptResponse = _d.sent();
80
87
  _d.label = 5;
81
88
  case 5:
82
- if (!validatedData) {
89
+ if (!verifyReceiptResponse) {
83
90
  reject({
84
91
  rejectionMessage: 'Unable to validate receipt using appstore endpoints.',
85
92
  data: null,
@@ -92,27 +99,27 @@ function validate(receipt, config) {
92
99
  reject(err_1);
93
100
  return [2 /*return*/];
94
101
  case 7:
95
- if (validatedData.status === constants_1.RECEIPT_STATUS_ENUM.SUCCESS) {
96
- if (((_a = validatedData.receipt) === null || _a === void 0 ? void 0 : _a.in_app) && ((_c = (_b = validatedData.receipt) === null || _b === void 0 ? void 0 : _b.in_app) === null || _c === void 0 ? void 0 : _c.length) === 0) {
102
+ if (verifyReceiptResponse.status === constants_1.RECEIPT_STATUS_ENUM.SUCCESS) {
103
+ if (((_a = verifyReceiptResponse.receipt) === null || _a === void 0 ? void 0 : _a.in_app) && ((_c = (_b = verifyReceiptResponse.receipt) === null || _b === void 0 ? void 0 : _b.in_app) === null || _c === void 0 ? void 0 : _c.length) === 0) {
97
104
  /*
98
- Detected valid receipt,
99
- but the receipt bought nothing
100
- probably hacked: https://forums.developer.apple.com/thread/8954
101
- https://developer.apple.com/library/mac/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPT-HOW_DO_I_USE_THE_CANCELLATION_DATE_FIELD_
102
- */
105
+ Detected valid receipt,
106
+ but the receipt bought nothing
107
+ probably hacked: https://forums.developer.apple.com/thread/8954
108
+ https://developer.apple.com/library/mac/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPT-HOW_DO_I_USE_THE_CANCELLATION_DATE_FIELD_
109
+ */
103
110
  reject({
104
111
  rejectionMessage: 'Detected valid receipt, however purchase list is empty',
105
- data: validatedData,
112
+ data: verifyReceiptResponse,
106
113
  });
107
114
  }
108
115
  // validated successfully
109
- resolve(validatedData);
116
+ resolve(verifyReceiptResponse);
110
117
  return [2 /*return*/];
111
118
  }
112
119
  // failed to validate reject with apple message
113
120
  reject({
114
- rejectionMessage: constants_1.STATUS_TO_MESSAGE_MAP[validatedData.status],
115
- data: validatedData,
121
+ rejectionMessage: constants_1.STATUS_TO_MESSAGE_MAP[verifyReceiptResponse.status],
122
+ data: verifyReceiptResponse,
116
123
  });
117
124
  return [2 /*return*/];
118
125
  }
@@ -121,13 +128,23 @@ function validate(receipt, config) {
121
128
  });
122
129
  });
123
130
  }
124
- exports.validate = validate;
125
- var isValidated = function (response) {
126
- return response && response.status === constants_1.RECEIPT_STATUS_ENUM.SUCCESS;
131
+ exports.verify = verify;
132
+ /**
133
+ * It checks if the receipt is valid.
134
+ * @param {IVerifyReceiptResponseBody | null} verifyReceiptResponse - IVerifyReceiptResponseBody | null
135
+ * @returns A boolean
136
+ */
137
+ var isVerifiedReceipt = function (verifyReceiptResponse) {
138
+ return (verifyReceiptResponse === null || verifyReceiptResponse === void 0 ? void 0 : verifyReceiptResponse.status) === constants_1.RECEIPT_STATUS_ENUM.SUCCESS;
127
139
  };
128
- exports.isValidated = isValidated;
129
- var isExpired = function (purchasedItem) {
130
- if (!purchasedItem || !purchasedItem.transactionId) {
140
+ exports.isVerifiedReceipt = isVerifiedReceipt;
141
+ /**
142
+ * If the purchased item has been cancelled or if the expiration date has passed, then it has expired
143
+ * @param {PurchasedItem | null} purchasedItem - PurchasedItem | null
144
+ * @returns A boolean
145
+ */
146
+ var isPurchasedItemExpired = function (purchasedItem) {
147
+ if (!(purchasedItem === null || purchasedItem === void 0 ? void 0 : purchasedItem.transactionId)) {
131
148
  throw new Error('Detected invalid purchased item! Make sure object is defined and it has transaction id.');
132
149
  }
133
150
  // it has been cancelled
@@ -139,35 +156,47 @@ var isExpired = function (purchasedItem) {
139
156
  return false;
140
157
  }
141
158
  // has expired
142
- if (Date.now() - purchasedItem.expirationDateMS >= 0) {
159
+ if (Date.now().valueOf() - purchasedItem.expirationDateMS >= 0) {
143
160
  return true;
144
161
  }
145
162
  // has not expired yet
146
163
  return false;
147
164
  };
148
- exports.isExpired = isExpired;
149
- var isCanceled = function (purchasedItem) {
150
- if (!purchasedItem || !purchasedItem.transactionId) {
165
+ exports.isPurchasedItemExpired = isPurchasedItemExpired;
166
+ /**
167
+ * If the purchased item has a cancellation date, then it's canceled.
168
+ * @param {PurchasedItem} purchasedItem - PurchasedItem - this is the purchased item object that you get from the
169
+ * getPurchasedItems() method.
170
+ * @returns A boolean value.
171
+ */
172
+ var isPurchasedItemCanceled = function (purchasedItem) {
173
+ if (!(purchasedItem === null || purchasedItem === void 0 ? void 0 : purchasedItem.transactionId)) {
151
174
  throw new Error('Detected invalid purchased item! Make sure object is defined and it has transaction id.');
152
175
  }
153
176
  return Boolean(purchasedItem.cancellationDateMS);
154
177
  };
155
- exports.isCanceled = isCanceled;
156
- var getPurchaseData = function (purchase) {
157
- if (!purchase || !purchase.receipt) {
178
+ exports.isPurchasedItemCanceled = isPurchasedItemCanceled;
179
+ /**
180
+ * It takes a response from the Apple App Store and returns an array of PurchasedItem objects sorted by their purchase date in descending order,
181
+ * the latest purchase comes first.
182
+ * @param {IVerifyReceiptResponseBody | null} verifyReceiptResponse - IVerifyReceiptResponseBody | null
183
+ * @returns An array of PurchasedItem objects.
184
+ */
185
+ var getPurchasedItems = function (verifyReceiptResponse) {
186
+ if (!(verifyReceiptResponse === null || verifyReceiptResponse === void 0 ? void 0 : verifyReceiptResponse.receipt)) {
158
187
  return [];
159
188
  }
160
189
  var data = [];
161
- var purchases = purchase.receipt.in_app || [];
162
- var lri = purchase.latest_receipt_info || purchase.receipt.latest_receipt_info;
190
+ var purchases = verifyReceiptResponse.receipt.in_app || [];
191
+ var lri = verifyReceiptResponse.latest_receipt_info || verifyReceiptResponse.receipt.latest_receipt_info;
163
192
  if (Array.isArray(lri)) {
164
193
  purchases = purchases.concat(lri);
165
194
  }
166
195
  /*
167
- we sort purchases by purchase_date_ms to make it easier
168
- to weed out duplicates (items with the same original_transaction_id)
169
- purchase_date_ms DESC
170
- */
196
+ we sort purchases by purchase_date_ms to make it easier
197
+ to weed out duplicates (items with the same original_transaction_id)
198
+ purchase_date_ms DESC
199
+ */
171
200
  purchases.sort(function (a, b) {
172
201
  return parseInt(b.purchase_date_ms, 10) - parseInt(a.purchase_date_ms, 10);
173
202
  });
@@ -179,9 +208,9 @@ var getPurchaseData = function (purchase) {
179
208
  if (transactionIds[tid]) {
180
209
  continue;
181
210
  }
182
- data.push((0, internal_1.getPurchaseItem)(item, purchase));
211
+ data.push((0, internal_1.getPurchaseItem)(item, verifyReceiptResponse));
183
212
  transactionIds[tid] = true;
184
213
  }
185
214
  return data;
186
215
  };
187
- exports.getPurchaseData = getPurchaseData;
216
+ exports.getPurchasedItems = getPurchasedItems;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iap-apple",
3
- "version": "1.3.7",
3
+ "version": "1.3.9",
4
4
  "description": "Integration with Apples InAppPurchases in Typescript, available for NodeJS environments.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,4 +1,4 @@
1
- import { IReceiptInAppItem } from '../../types/internal';
1
+ import { IReceiptInAppItem } from '../internal';
2
2
  import { RECEIPT_STATUS_ENUM } from '../../constants';
3
3
  export interface ILogger {
4
4
  log: (message: string) => void;
@@ -6,8 +6,8 @@ export interface ILogger {
6
6
  error: (message: string) => void;
7
7
  }
8
8
  export interface IIAPAppleConfig {
9
- appleExcludeOldTransactions?: boolean | undefined;
10
- applePassword?: string | undefined;
9
+ appleExcludeOldTransactions?: boolean;
10
+ appSharedSecret: string;
11
11
  test?: boolean | undefined;
12
12
  logger?: ILogger | null;
13
13
  }
@@ -17,7 +17,7 @@ export interface PurchasedItem {
17
17
  originalTransactionId?: string;
18
18
  transactionId: string;
19
19
  productId: string;
20
- originalPurchaseDate?: number;
20
+ originalPurchaseDateMS?: number;
21
21
  expirationDateMS?: number;
22
22
  purchaseDateMS: number;
23
23
  isTrialPeriod: boolean;
@@ -26,9 +26,9 @@ export interface PurchasedItem {
26
26
  }
27
27
  export interface IAPAppleError {
28
28
  rejectionMessage: string;
29
- data?: IReceiptValidationResponseBody | null;
29
+ data?: IVerifyReceiptResponseBody | null;
30
30
  }
31
- export interface IReceiptValidationResponseBody {
31
+ export interface IVerifyReceiptResponseBody {
32
32
  status: RECEIPT_STATUS_ENUM;
33
33
  environment: 'Sandbox' | 'Production';
34
34
  receipt: IReceipt;