@shopify/shopify-api 5.0.0 → 5.1.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 +11 -0
- package/README.md +1 -0
- package/dist/auth/oauth/oauth.d.ts +2 -0
- package/dist/auth/oauth/oauth.d.ts.map +1 -1
- package/dist/auth/oauth/oauth.js +120 -95
- package/dist/auth/session/storage/redis.d.ts +1 -0
- package/dist/auth/session/storage/redis.d.ts.map +1 -1
- package/dist/auth/session/storage/redis.js +3 -0
- package/dist/base-types.d.ts +2 -0
- package/dist/base-types.d.ts.map +1 -1
- package/dist/billing/check.d.ts +12 -0
- package/dist/billing/check.d.ts.map +1 -0
- package/dist/billing/check.js +34 -0
- package/dist/billing/has_active_payment.d.ts +3 -0
- package/dist/billing/has_active_payment.d.ts.map +1 -0
- package/dist/billing/has_active_payment.js +91 -0
- package/dist/billing/index.d.ts +7 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +9 -0
- package/dist/billing/is_recurring.d.ts +2 -0
- package/dist/billing/is_recurring.d.ts.map +1 -0
- package/dist/billing/is_recurring.js +20 -0
- package/dist/billing/request_payment.d.ts +3 -0
- package/dist/billing/request_payment.d.ts.map +1 -0
- package/dist/billing/request_payment.js +134 -0
- package/dist/billing/types.d.ts +57 -0
- package/dist/billing/types.d.ts.map +1 -0
- package/dist/billing/types.js +9 -0
- package/dist/clients/graphql/graphql_client.d.ts +1 -1
- package/dist/clients/graphql/graphql_client.d.ts.map +1 -1
- package/dist/clients/http_client/http_client.d.ts +5 -5
- package/dist/clients/http_client/http_client.d.ts.map +1 -1
- package/dist/clients/http_client/types.d.ts +2 -2
- package/dist/clients/http_client/types.d.ts.map +1 -1
- package/dist/clients/rest/rest_client.d.ts +1 -1
- package/dist/clients/rest/rest_client.d.ts.map +1 -1
- package/dist/clients/rest/rest_client.js +1 -1
- package/dist/clients/rest/types.d.ts +1 -1
- package/dist/clients/rest/types.d.ts.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +3 -0
- package/dist/error.d.ts +7 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +13 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/utils/decode-session-token.js +1 -1
- package/dist/utils/get-embedded-app-url.d.ts +1 -0
- package/dist/utils/get-embedded-app-url.d.ts.map +1 -1
- package/dist/utils/get-embedded-app-url.js +8 -2
- package/dist/utils/setup-jest.d.ts.map +1 -1
- package/dist/utils/setup-jest.js +1 -0
- package/dist/utils/shop-validator.d.ts.map +1 -1
- package/dist/utils/shop-validator.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/webhooks/registry.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## [5.1.0] - 2022-09-19
|
|
11
|
+
|
|
12
|
+
- Increased tolerance for expired JWT session tokens from 5s to 10s [#462](https://github.com/Shopify/shopify-api-node/pull/462)
|
|
13
|
+
- Add support for billing to the library [#449](https://github.com/Shopify/shopify-api-node/pull/449)
|
|
14
|
+
- Allow dynamically typing the body of REST and GraphQL request responses, so callers don't need to cast it [#447](https://github.com/Shopify/shopify-api-node/pull/447)
|
|
15
|
+
- Rather than create a temporary session in order to store a session id in a cookie for the OAuth transaction, we can store the `state` in the cookie instead, that can then be compared against the `state` provided by Shopify in the callback. [#438](https://github.com/Shopify/shopify-api-node/pull/438)
|
|
16
|
+
|
|
17
|
+
## [5.0.1] - 2022-08-03
|
|
18
|
+
|
|
19
|
+
- Make `sanitizeHost` validation more flexible [#446](https://github.com/Shopify/shopify-api-node/pull/446)
|
|
20
|
+
|
|
10
21
|
## [5.0.0] - 2022-07-28
|
|
11
22
|
|
|
12
23
|
- ⚠️ [Breaking] If a response from a GraphQL query contains an `errors` attribute, `GraphqlClient` will now throw a `GraphqlQueryError`. The caller can check the `error.response` attribute to see what was returned from the GraphQL API. [#431](https://github.com/Shopify/shopify-api-node/pull/431)
|
package/README.md
CHANGED
|
@@ -51,5 +51,6 @@ You can follow our [getting started guide](docs/), which will provide instructio
|
|
|
51
51
|
- [Webhooks](docs/usage/webhooks.md)
|
|
52
52
|
- [Register a Webhook](docs/usage/webhooks.md#register-a-webhook)
|
|
53
53
|
- [Process a Webhook](docs/usage/webhooks.md#process-a-webhook)
|
|
54
|
+
- [Billing](docs/usage/billing.md)
|
|
54
55
|
- [Known issues and caveats](docs/issues.md)
|
|
55
56
|
- [Notes on session handling](docs/issues.md#notes-on-session-handling)
|
|
@@ -3,6 +3,7 @@ import { SessionInterface } from '../session/types';
|
|
|
3
3
|
import { AuthQuery } from './types';
|
|
4
4
|
declare const ShopifyOAuth: {
|
|
5
5
|
SESSION_COOKIE_NAME: string;
|
|
6
|
+
STATE_COOKIE_NAME: string;
|
|
6
7
|
/**
|
|
7
8
|
* Initializes a session and cookie for the OAuth process, and returns the necessary authorization url.
|
|
8
9
|
*
|
|
@@ -24,6 +25,7 @@ declare const ShopifyOAuth: {
|
|
|
24
25
|
* @param response Current HTTP Response
|
|
25
26
|
* @param query Current HTTP Request Query, containing the information to be validated.
|
|
26
27
|
* Depending on framework, this may need to be cast as "unknown" before being passed.
|
|
28
|
+
* @param isOnline Boolean value. Defaults to true (online access).
|
|
27
29
|
* @returns SessionInterface
|
|
28
30
|
*/
|
|
29
31
|
validateAuthCallback(request: http.IncomingMessage, response: http.ServerResponse, query: AuthQuery): Promise<SessionInterface>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAexB,OAAO,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EACL,SAAS,EAIV,MAAM,SAAS,CAAC;AAEjB,QAAA,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAexB,OAAO,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EACL,SAAS,EAIV,MAAM,SAAS,CAAC;AAEjB,QAAA,MAAM,YAAY;;;IAIhB;;;;;;;;;;OAUG;uBAEQ,KAAK,eAAe,YACnB,KAAK,cAAc,QACvB,MAAM,gBACE,MAAM,uBAEnB,QAAQ,MAAM,CAAC;IAkClB;;;;;;;;;;;OAWG;kCAEQ,KAAK,eAAe,YACnB,KAAK,cAAc,SACtB,SAAS,GACf,QAAQ,gBAAgB,CAAC;IAwE5B;;;;;OAKG;gCAEQ,KAAK,eAAe,YACnB,KAAK,cAAc,GAC5B,MAAM,GAAG,SAAS;IAIrB;;;;;OAKG;0BACmB,MAAM,UAAU,MAAM,GAAG,MAAM;IAIrD;;;;OAIG;8BACuB,MAAM,GAAG,MAAM;IAIzC;;;;;;OAMG;iCAEQ,KAAK,eAAe,YACnB,KAAK,cAAc,uBAE5B,MAAM,GAAG,SAAS;CAqCtB,CAAC;AAwGF,OAAO,EAAC,YAAY,EAAC,CAAC"}
|
package/dist/auth/oauth/oauth.js
CHANGED
|
@@ -17,6 +17,7 @@ var ShopifyErrors = tslib_1.__importStar(require("../../error"));
|
|
|
17
17
|
var shop_validator_1 = require("../../utils/shop-validator");
|
|
18
18
|
var ShopifyOAuth = {
|
|
19
19
|
SESSION_COOKIE_NAME: 'shopify_app_session',
|
|
20
|
+
STATE_COOKIE_NAME: 'shopify_app_state',
|
|
20
21
|
/**
|
|
21
22
|
* Initializes a session and cookie for the OAuth process, and returns the necessary authorization url.
|
|
22
23
|
*
|
|
@@ -31,41 +32,31 @@ var ShopifyOAuth = {
|
|
|
31
32
|
beginAuth: function (request, response, shop, redirectPath, isOnline) {
|
|
32
33
|
if (isOnline === void 0) { isOnline = true; }
|
|
33
34
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
34
|
-
var cleanShop, cookies, state,
|
|
35
|
+
var cleanShop, cookies, state, query, queryString;
|
|
35
36
|
return tslib_1.__generator(this, function (_a) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
query = {
|
|
60
|
-
client_id: context_1.Context.API_KEY,
|
|
61
|
-
scope: context_1.Context.SCOPES.toString(),
|
|
62
|
-
redirect_uri: "".concat(context_1.Context.HOST_SCHEME, "://").concat(context_1.Context.HOST_NAME).concat(redirectPath),
|
|
63
|
-
state: state,
|
|
64
|
-
'grant_options[]': isOnline ? 'per-user' : '',
|
|
65
|
-
};
|
|
66
|
-
queryString = querystring_1.default.stringify(query);
|
|
67
|
-
return [2 /*return*/, "https://".concat(cleanShop, "/admin/oauth/authorize?").concat(queryString)];
|
|
68
|
-
}
|
|
37
|
+
context_1.Context.throwIfUninitialized();
|
|
38
|
+
context_1.Context.throwIfPrivateApp('Cannot perform OAuth for private apps');
|
|
39
|
+
cleanShop = (0, shop_validator_1.sanitizeShop)(shop, true);
|
|
40
|
+
cookies = new cookies_1.default(request, response, {
|
|
41
|
+
keys: [context_1.Context.API_SECRET_KEY],
|
|
42
|
+
secure: true,
|
|
43
|
+
});
|
|
44
|
+
state = isOnline ? "online_".concat((0, nonce_1.default)()) : "offline_".concat((0, nonce_1.default)());
|
|
45
|
+
cookies.set(ShopifyOAuth.STATE_COOKIE_NAME, state, {
|
|
46
|
+
signed: true,
|
|
47
|
+
expires: new Date(Date.now() + 60000),
|
|
48
|
+
sameSite: 'lax',
|
|
49
|
+
secure: true,
|
|
50
|
+
});
|
|
51
|
+
query = {
|
|
52
|
+
client_id: context_1.Context.API_KEY,
|
|
53
|
+
scope: context_1.Context.SCOPES.toString(),
|
|
54
|
+
redirect_uri: "".concat(context_1.Context.HOST_SCHEME, "://").concat(context_1.Context.HOST_NAME).concat(redirectPath),
|
|
55
|
+
state: state,
|
|
56
|
+
'grant_options[]': isOnline ? 'per-user' : '',
|
|
57
|
+
};
|
|
58
|
+
queryString = querystring_1.default.stringify(query);
|
|
59
|
+
return [2 /*return*/, "https://".concat(cleanShop, "/admin/oauth/authorize?").concat(queryString)];
|
|
69
60
|
});
|
|
70
61
|
});
|
|
71
62
|
},
|
|
@@ -78,33 +69,26 @@ var ShopifyOAuth = {
|
|
|
78
69
|
* @param response Current HTTP Response
|
|
79
70
|
* @param query Current HTTP Request Query, containing the information to be validated.
|
|
80
71
|
* Depending on framework, this may need to be cast as "unknown" before being passed.
|
|
72
|
+
* @param isOnline Boolean value. Defaults to true (online access).
|
|
81
73
|
* @returns SessionInterface
|
|
82
74
|
*/
|
|
83
75
|
validateAuthCallback: function (request, response, query) {
|
|
84
76
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
85
|
-
var
|
|
77
|
+
var stateFromCookie, isOnline, body, postParams, cleanShop, client, postResponse, session, cookies, sessionStored;
|
|
86
78
|
return tslib_1.__generator(this, function (_a) {
|
|
87
79
|
switch (_a.label) {
|
|
88
80
|
case 0:
|
|
89
81
|
context_1.Context.throwIfUninitialized();
|
|
90
82
|
context_1.Context.throwIfPrivateApp('Cannot perform OAuth for private apps');
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
sessionCookie = this.getCookieSessionId(request, response);
|
|
96
|
-
if (!sessionCookie) {
|
|
83
|
+
stateFromCookie = getValueFromCookie(request, response, this.STATE_COOKIE_NAME);
|
|
84
|
+
deleteCookie(request, response, this.STATE_COOKIE_NAME);
|
|
85
|
+
if (!stateFromCookie) {
|
|
97
86
|
throw new ShopifyErrors.CookieNotFound("Cannot complete OAuth process. Could not find an OAuth cookie for shop url: ".concat(query.shop));
|
|
98
87
|
}
|
|
99
|
-
|
|
100
|
-
case 1:
|
|
101
|
-
currentSession = _a.sent();
|
|
102
|
-
if (!currentSession) {
|
|
103
|
-
throw new ShopifyErrors.SessionNotFound("Cannot complete OAuth process. No session found for the specified shop url: ".concat(query.shop));
|
|
104
|
-
}
|
|
105
|
-
if (!validQuery(query, currentSession)) {
|
|
88
|
+
if (!validQuery(query, stateFromCookie)) {
|
|
106
89
|
throw new ShopifyErrors.InvalidOAuthError('Invalid OAuth callback.');
|
|
107
90
|
}
|
|
91
|
+
isOnline = stateFromCookie.startsWith('online_');
|
|
108
92
|
body = {
|
|
109
93
|
client_id: context_1.Context.API_KEY,
|
|
110
94
|
client_secret: context_1.Context.API_SECRET_KEY,
|
|
@@ -115,50 +99,31 @@ var ShopifyOAuth = {
|
|
|
115
99
|
type: types_1.DataType.JSON,
|
|
116
100
|
data: body,
|
|
117
101
|
};
|
|
118
|
-
|
|
102
|
+
cleanShop = (0, shop_validator_1.sanitizeShop)(query.shop, true);
|
|
103
|
+
client = new http_client_1.HttpClient(cleanShop);
|
|
119
104
|
return [4 /*yield*/, client.post(postParams)];
|
|
120
|
-
case
|
|
105
|
+
case 1:
|
|
121
106
|
postResponse = _a.sent();
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return [4 /*yield*/, context_1.Context.SESSION_STORAGE.deleteSession(currentSession.id)];
|
|
135
|
-
case 3:
|
|
136
|
-
sessionDeleted = _a.sent();
|
|
137
|
-
if (!sessionDeleted) {
|
|
138
|
-
throw new ShopifyErrors.SessionStorageError('OAuth Session could not be deleted. Please check your session storage functionality.');
|
|
107
|
+
session = createSession(postResponse, cleanShop, stateFromCookie, isOnline);
|
|
108
|
+
if (!context_1.Context.IS_EMBEDDED_APP) {
|
|
109
|
+
cookies = new cookies_1.default(request, response, {
|
|
110
|
+
keys: [context_1.Context.API_SECRET_KEY],
|
|
111
|
+
secure: true,
|
|
112
|
+
});
|
|
113
|
+
cookies.set(ShopifyOAuth.SESSION_COOKIE_NAME, session.id, {
|
|
114
|
+
signed: true,
|
|
115
|
+
expires: session.expires,
|
|
116
|
+
sameSite: 'lax',
|
|
117
|
+
secure: true,
|
|
118
|
+
});
|
|
139
119
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
case 4: return [3 /*break*/, 6];
|
|
143
|
-
case 5:
|
|
144
|
-
responseBody = postResponse.body;
|
|
145
|
-
currentSession.accessToken = responseBody.access_token;
|
|
146
|
-
currentSession.scope = responseBody.scope;
|
|
147
|
-
_a.label = 6;
|
|
148
|
-
case 6:
|
|
149
|
-
cookies.set(ShopifyOAuth.SESSION_COOKIE_NAME, currentSession.id, {
|
|
150
|
-
signed: true,
|
|
151
|
-
expires: context_1.Context.IS_EMBEDDED_APP ? new Date() : currentSession.expires,
|
|
152
|
-
sameSite: 'lax',
|
|
153
|
-
secure: true,
|
|
154
|
-
});
|
|
155
|
-
return [4 /*yield*/, context_1.Context.SESSION_STORAGE.storeSession(currentSession)];
|
|
156
|
-
case 7:
|
|
120
|
+
return [4 /*yield*/, context_1.Context.SESSION_STORAGE.storeSession(session)];
|
|
121
|
+
case 2:
|
|
157
122
|
sessionStored = _a.sent();
|
|
158
123
|
if (!sessionStored) {
|
|
159
|
-
throw new ShopifyErrors.SessionStorageError('
|
|
124
|
+
throw new ShopifyErrors.SessionStorageError('Session could not be saved. Please check your session storage functionality.');
|
|
160
125
|
}
|
|
161
|
-
return [2 /*return*/,
|
|
126
|
+
return [2 /*return*/, session];
|
|
162
127
|
}
|
|
163
128
|
});
|
|
164
129
|
});
|
|
@@ -170,11 +135,7 @@ var ShopifyOAuth = {
|
|
|
170
135
|
* @param response HTTP response object
|
|
171
136
|
*/
|
|
172
137
|
getCookieSessionId: function (request, response) {
|
|
173
|
-
|
|
174
|
-
secure: true,
|
|
175
|
-
keys: [context_1.Context.API_SECRET_KEY],
|
|
176
|
-
});
|
|
177
|
-
return cookies.get(this.SESSION_COOKIE_NAME, { signed: true });
|
|
138
|
+
return getValueFromCookie(request, response, this.SESSION_COOKIE_NAME);
|
|
178
139
|
},
|
|
179
140
|
/**
|
|
180
141
|
* Builds a JWT session id from the current shop and user.
|
|
@@ -225,7 +186,7 @@ var ShopifyOAuth = {
|
|
|
225
186
|
// JWT.
|
|
226
187
|
if (!currentSessionId) {
|
|
227
188
|
// We still want to get the offline session id from the cookie to make sure it's validated
|
|
228
|
-
currentSessionId =
|
|
189
|
+
currentSessionId = getValueFromCookie(request, response, this.SESSION_COOKIE_NAME);
|
|
229
190
|
}
|
|
230
191
|
return currentSessionId;
|
|
231
192
|
},
|
|
@@ -235,8 +196,72 @@ exports.ShopifyOAuth = ShopifyOAuth;
|
|
|
235
196
|
* Uses the validation utils validateHmac, and safeCompare to assess whether the callback is valid.
|
|
236
197
|
*
|
|
237
198
|
* @param query Current HTTP Request Query
|
|
238
|
-
* @param
|
|
199
|
+
* @param stateFromCookie state value from the current cookie
|
|
239
200
|
*/
|
|
240
|
-
function validQuery(query,
|
|
241
|
-
return (
|
|
201
|
+
function validQuery(query, stateFromCookie) {
|
|
202
|
+
return (0, hmac_validator_1.default)(query) && (0, safe_compare_1.default)(query.state, stateFromCookie);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Loads a given value from the cookie
|
|
206
|
+
*
|
|
207
|
+
* @param request HTTP request object
|
|
208
|
+
* @param response HTTP response object
|
|
209
|
+
* @param name Name of the cookie to load
|
|
210
|
+
*/
|
|
211
|
+
function getValueFromCookie(request, response, name) {
|
|
212
|
+
var cookies = new cookies_1.default(request, response, {
|
|
213
|
+
secure: true,
|
|
214
|
+
keys: [context_1.Context.API_SECRET_KEY],
|
|
215
|
+
});
|
|
216
|
+
return cookies.get(name, { signed: true });
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Loads a given value from the cookie
|
|
220
|
+
*
|
|
221
|
+
* @param request HTTP request object
|
|
222
|
+
* @param response HTTP response object
|
|
223
|
+
* @param name Name of the cookie to load
|
|
224
|
+
*/
|
|
225
|
+
function deleteCookie(request, response, name) {
|
|
226
|
+
var cookies = new cookies_1.default(request, response, {
|
|
227
|
+
secure: true,
|
|
228
|
+
keys: [context_1.Context.API_SECRET_KEY],
|
|
229
|
+
});
|
|
230
|
+
cookies.set(name);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Creates a new session from the response from the access token request.
|
|
234
|
+
*
|
|
235
|
+
* @param postResponse Response from the access token request
|
|
236
|
+
* @param shop Shop url: {shop}.myshopify.com
|
|
237
|
+
* @param stateFromCookie State from the cookie received by the OAuth callback
|
|
238
|
+
* @param isOnline Boolean indicating if the access token is for online access
|
|
239
|
+
* @returns SessionInterface
|
|
240
|
+
*/
|
|
241
|
+
function createSession(postResponse, shop, stateFromCookie, isOnline) {
|
|
242
|
+
var session;
|
|
243
|
+
if (isOnline) {
|
|
244
|
+
var sessionId = void 0;
|
|
245
|
+
var responseBody = postResponse.body;
|
|
246
|
+
var access_token = responseBody.access_token, scope = responseBody.scope, rest = tslib_1.__rest(responseBody, ["access_token", "scope"]); // eslint-disable-line @typescript-eslint/naming-convention
|
|
247
|
+
var sessionExpiration = new Date(Date.now() + responseBody.expires_in * 1000);
|
|
248
|
+
if (context_1.Context.IS_EMBEDDED_APP) {
|
|
249
|
+
sessionId = ShopifyOAuth.getJwtSessionId(shop, "".concat(rest.associated_user.id));
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
sessionId = (0, uuid_1.v4)();
|
|
253
|
+
}
|
|
254
|
+
session = new session_1.Session(sessionId, shop, stateFromCookie, isOnline);
|
|
255
|
+
session.accessToken = access_token;
|
|
256
|
+
session.scope = scope;
|
|
257
|
+
session.expires = sessionExpiration;
|
|
258
|
+
session.onlineAccessInfo = rest;
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
var responseBody = postResponse.body;
|
|
262
|
+
session = new session_1.Session(ShopifyOAuth.getOfflineSessionId(shop), shop, stateFromCookie, isOnline);
|
|
263
|
+
session.accessToken = responseBody.access_token;
|
|
264
|
+
session.scope = responseBody.scope;
|
|
265
|
+
}
|
|
266
|
+
return session;
|
|
242
267
|
}
|
|
@@ -3,6 +3,7 @@ import { SessionInterface } from '../types';
|
|
|
3
3
|
import { SessionStorage } from '../session_storage';
|
|
4
4
|
export interface RedisSessionStorageOptions extends RedisClientOptions {
|
|
5
5
|
sessionKeyPrefix: string;
|
|
6
|
+
onError?: (...args: any[]) => void;
|
|
6
7
|
}
|
|
7
8
|
export declare class RedisSessionStorage implements SessionStorage {
|
|
8
9
|
private dbUrl;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../../../src/auth/session/storage/redis.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../../../src/auth/session/storage/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,kBAAkB,EAAC,MAAM,OAAO,CAAC;AAEvD,OAAO,EAAC,gBAAgB,EAAC,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAKlD,MAAM,WAAW,0BAA2B,SAAQ,kBAAkB;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;CACpC;AAKD,qBAAa,mBAAoB,YAAW,cAAc;IAuBtD,OAAO,CAAC,KAAK;IAtBf,MAAM,CAAC,eAAe,CACpB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,CAAC,0BAA0B,CAAC;IAY3C,SAAgB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,MAAM,CAAc;gBAGlB,KAAK,EAAE,GAAG,EAClB,IAAI,GAAE,OAAO,CAAC,0BAA0B,CAAM;IASnC,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAUzD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAW9D,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM3C,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAM/C,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAiB7D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxC,OAAO,CAAC,OAAO;YAID,IAAI;CAUnB"}
|
|
@@ -157,6 +157,9 @@ var RedisSessionStorage = /** @class */ (function () {
|
|
|
157
157
|
switch (_a.label) {
|
|
158
158
|
case 0:
|
|
159
159
|
this.client = (0, redis_1.createClient)(tslib_1.__assign(tslib_1.__assign({}, this.options), { url: this.dbUrl.toString() }));
|
|
160
|
+
if (this.options.onError) {
|
|
161
|
+
this.client.on('error', this.options.onError);
|
|
162
|
+
}
|
|
160
163
|
return [4 /*yield*/, this.client.connect()];
|
|
161
164
|
case 1:
|
|
162
165
|
_a.sent();
|
package/dist/base-types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AuthScopes } from './auth/scopes';
|
|
2
2
|
import { SessionStorage } from './auth/session/session_storage';
|
|
3
|
+
import { BillingSettings } from './billing/types';
|
|
3
4
|
export interface ContextParams {
|
|
4
5
|
API_KEY: string;
|
|
5
6
|
API_SECRET_KEY: string;
|
|
@@ -14,6 +15,7 @@ export interface ContextParams {
|
|
|
14
15
|
USER_AGENT_PREFIX?: string;
|
|
15
16
|
PRIVATE_APP_STOREFRONT_ACCESS_TOKEN?: string;
|
|
16
17
|
CUSTOM_SHOP_DOMAINS?: (RegExp | string)[];
|
|
18
|
+
BILLING?: BillingSettings;
|
|
17
19
|
}
|
|
18
20
|
export declare enum ApiVersion {
|
|
19
21
|
October21 = "2021-10",
|
package/dist/base-types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-types.d.ts","sourceRoot":"","sources":["../src/base-types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AACzC,OAAO,EAAC,cAAc,EAAC,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"base-types.d.ts","sourceRoot":"","sources":["../src/base-types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AACzC,OAAO,EAAC,cAAc,EAAC,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,UAAU,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mCAAmC,CAAC,EAAE,MAAM,CAAC;IAC7C,mBAAmB,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC1C,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED,oBAAY,UAAU;IACpB,SAAS,YAAY;IACrB,SAAS,YAAY;IACrB,OAAO,YAAY;IACnB,MAAM,YAAY;IAClB,QAAQ,aAAa;CACtB;AAED,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAEpD,oBAAY,aAAa;IACvB,WAAW,2BAA2B;IACtC,qBAAqB,sCAAsC;IAC3D,IAAI,0BAA0B;IAC9B,KAAK,oBAAoB;IACzB,MAAM,0BAA0B;CACjC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SessionInterface } from '../auth/session/types';
|
|
2
|
+
interface CheckInterface {
|
|
3
|
+
session: SessionInterface;
|
|
4
|
+
isTest?: boolean;
|
|
5
|
+
}
|
|
6
|
+
interface CheckReturn {
|
|
7
|
+
hasPayment: boolean;
|
|
8
|
+
confirmBillingUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function check({ session, isTest, }: CheckInterface): Promise<CheckReturn>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/billing/check.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAKvD,UAAU,cAAc;IACtB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,WAAW;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,KAAK,CAAC,EAC1B,OAAO,EACP,MAAa,GACd,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAgBvC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.check = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var context_1 = require("../context");
|
|
6
|
+
var has_active_payment_1 = require("./has_active_payment");
|
|
7
|
+
var request_payment_1 = require("./request_payment");
|
|
8
|
+
function check(_a) {
|
|
9
|
+
var session = _a.session, _b = _a.isTest, isTest = _b === void 0 ? true : _b;
|
|
10
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
11
|
+
var hasPayment, confirmBillingUrl;
|
|
12
|
+
return tslib_1.__generator(this, function (_c) {
|
|
13
|
+
switch (_c.label) {
|
|
14
|
+
case 0:
|
|
15
|
+
if (!context_1.Context.BILLING) {
|
|
16
|
+
return [2 /*return*/, { hasPayment: true, confirmBillingUrl: undefined }];
|
|
17
|
+
}
|
|
18
|
+
return [4 /*yield*/, (0, has_active_payment_1.hasActivePayment)(session, isTest)];
|
|
19
|
+
case 1:
|
|
20
|
+
if (!_c.sent()) return [3 /*break*/, 2];
|
|
21
|
+
hasPayment = true;
|
|
22
|
+
return [3 /*break*/, 4];
|
|
23
|
+
case 2:
|
|
24
|
+
hasPayment = false;
|
|
25
|
+
return [4 /*yield*/, (0, request_payment_1.requestPayment)(session, isTest)];
|
|
26
|
+
case 3:
|
|
27
|
+
confirmBillingUrl = _c.sent();
|
|
28
|
+
_c.label = 4;
|
|
29
|
+
case 4: return [2 /*return*/, { hasPayment: hasPayment, confirmBillingUrl: confirmBillingUrl }];
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
exports.check = check;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has_active_payment.d.ts","sourceRoot":"","sources":["../../src/billing/has_active_payment.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AASvD,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,OAAO,CAAC,CAMlB"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasActivePayment = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var context_1 = require("../context");
|
|
6
|
+
var error_1 = require("../error");
|
|
7
|
+
var graphql_1 = require("../clients/graphql");
|
|
8
|
+
var is_recurring_1 = require("./is_recurring");
|
|
9
|
+
function hasActivePayment(session, isTest) {
|
|
10
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
11
|
+
return tslib_1.__generator(this, function (_a) {
|
|
12
|
+
if ((0, is_recurring_1.isRecurring)()) {
|
|
13
|
+
return [2 /*return*/, hasActiveSubscription(session, isTest)];
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
return [2 /*return*/, hasActiveOneTimePurchase(session, isTest)];
|
|
17
|
+
}
|
|
18
|
+
return [2 /*return*/];
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
exports.hasActivePayment = hasActivePayment;
|
|
23
|
+
function hasActiveSubscription(session, isTest) {
|
|
24
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
25
|
+
var client, currentInstallations;
|
|
26
|
+
return tslib_1.__generator(this, function (_a) {
|
|
27
|
+
switch (_a.label) {
|
|
28
|
+
case 0:
|
|
29
|
+
if (!context_1.Context.BILLING) {
|
|
30
|
+
throw new error_1.BillingError({
|
|
31
|
+
message: 'Attempted to look for subscriptions without billing configs',
|
|
32
|
+
errorData: [],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
client = new graphql_1.GraphqlClient(session.shop, session.accessToken);
|
|
36
|
+
return [4 /*yield*/, client.query({
|
|
37
|
+
data: RECURRING_PURCHASES_QUERY,
|
|
38
|
+
})];
|
|
39
|
+
case 1:
|
|
40
|
+
currentInstallations = _a.sent();
|
|
41
|
+
return [2 /*return*/, currentInstallations.body.data.currentAppInstallation.activeSubscriptions.some(function (subscription) {
|
|
42
|
+
return subscription.name === context_1.Context.BILLING.chargeName &&
|
|
43
|
+
(isTest || !subscription.test);
|
|
44
|
+
})];
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function hasActiveOneTimePurchase(session, isTest) {
|
|
50
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
51
|
+
var client, installation, endCursor, currentInstallations;
|
|
52
|
+
return tslib_1.__generator(this, function (_a) {
|
|
53
|
+
switch (_a.label) {
|
|
54
|
+
case 0:
|
|
55
|
+
if (!context_1.Context.BILLING) {
|
|
56
|
+
throw new error_1.BillingError({
|
|
57
|
+
message: 'Attempted to look for one time purchases without billing configs',
|
|
58
|
+
errorData: [],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
client = new graphql_1.GraphqlClient(session.shop, session.accessToken);
|
|
62
|
+
endCursor = null;
|
|
63
|
+
_a.label = 1;
|
|
64
|
+
case 1: return [4 /*yield*/, client.query({
|
|
65
|
+
data: {
|
|
66
|
+
query: ONE_TIME_PURCHASES_QUERY,
|
|
67
|
+
variables: { endCursor: endCursor },
|
|
68
|
+
},
|
|
69
|
+
})];
|
|
70
|
+
case 2:
|
|
71
|
+
currentInstallations = _a.sent();
|
|
72
|
+
installation = currentInstallations.body.data.currentAppInstallation;
|
|
73
|
+
if (installation.oneTimePurchases.edges.some(function (purchase) {
|
|
74
|
+
return purchase.node.name === context_1.Context.BILLING.chargeName &&
|
|
75
|
+
(isTest || !purchase.node.test) &&
|
|
76
|
+
purchase.node.status === 'ACTIVE';
|
|
77
|
+
})) {
|
|
78
|
+
return [2 /*return*/, true];
|
|
79
|
+
}
|
|
80
|
+
endCursor = installation.oneTimePurchases.pageInfo.endCursor;
|
|
81
|
+
_a.label = 3;
|
|
82
|
+
case 3:
|
|
83
|
+
if (installation === null || installation === void 0 ? void 0 : installation.oneTimePurchases.pageInfo.hasNextPage) return [3 /*break*/, 1];
|
|
84
|
+
_a.label = 4;
|
|
85
|
+
case 4: return [2 /*return*/, false];
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
var RECURRING_PURCHASES_QUERY = "\n query appSubscription {\n currentAppInstallation {\n activeSubscriptions {\n name\n test\n }\n }\n }\n";
|
|
91
|
+
var ONE_TIME_PURCHASES_QUERY = "\n query appPurchases($endCursor: String) {\n currentAppInstallation {\n oneTimePurchases(first: 250, sortKey: CREATED_AT, after: $endCursor) {\n edges {\n node {\n name\n test\n status\n }\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n }\n";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/billing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,SAAS,CAAC;AAE9B,QAAA,MAAM,cAAc;;CAEnB,CAAC;AAEF,OAAO,EAAC,cAAc,EAAC,CAAC;AACxB,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ShopifyBilling = void 0;
|
|
4
|
+
var check_1 = require("./check");
|
|
5
|
+
var ShopifyBilling = {
|
|
6
|
+
check: check_1.check,
|
|
7
|
+
};
|
|
8
|
+
exports.ShopifyBilling = ShopifyBilling;
|
|
9
|
+
exports.default = ShopifyBilling;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is_recurring.d.ts","sourceRoot":"","sources":["../../src/billing/is_recurring.ts"],"names":[],"mappings":"AAUA,wBAAgB,WAAW,IAAI,OAAO,CASrC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isRecurring = void 0;
|
|
4
|
+
var context_1 = require("../context");
|
|
5
|
+
var error_1 = require("../error");
|
|
6
|
+
var types_1 = require("./types");
|
|
7
|
+
var RECURRING_INTERVALS = [
|
|
8
|
+
types_1.BillingInterval.Every30Days,
|
|
9
|
+
types_1.BillingInterval.Annual,
|
|
10
|
+
];
|
|
11
|
+
function isRecurring() {
|
|
12
|
+
if (!context_1.Context.BILLING) {
|
|
13
|
+
throw new error_1.BillingError({
|
|
14
|
+
message: 'Attempted to request billing without billing configs',
|
|
15
|
+
errorData: [],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return RECURRING_INTERVALS.includes(context_1.Context.BILLING.interval);
|
|
19
|
+
}
|
|
20
|
+
exports.isRecurring = isRecurring;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request_payment.d.ts","sourceRoot":"","sources":["../../src/billing/request_payment.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAYvD,wBAAsB,cAAc,CAClC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA8B7B"}
|