shopify-nuxt 0.0.1
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 +275 -0
- package/dist/module.d.mts +7 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +134 -0
- package/dist/runtime/components/ShopifyAppProvider.d.vue.ts +13 -0
- package/dist/runtime/components/ShopifyAppProvider.vue +11 -0
- package/dist/runtime/components/ShopifyAppProvider.vue.d.ts +13 -0
- package/dist/runtime/components/polaris/ShAppNav.d.vue.ts +13 -0
- package/dist/runtime/components/polaris/ShAppNav.vue +9 -0
- package/dist/runtime/components/polaris/ShAppNav.vue.d.ts +13 -0
- package/dist/runtime/components/polaris/ShAvatar.d.vue.ts +19 -0
- package/dist/runtime/components/polaris/ShAvatar.vue +15 -0
- package/dist/runtime/components/polaris/ShAvatar.vue.d.ts +19 -0
- package/dist/runtime/components/polaris/ShBadge.d.vue.ts +20 -0
- package/dist/runtime/components/polaris/ShBadge.vue +16 -0
- package/dist/runtime/components/polaris/ShBadge.vue.d.ts +20 -0
- package/dist/runtime/components/polaris/ShBanner.d.vue.ts +19 -0
- package/dist/runtime/components/polaris/ShBanner.vue +15 -0
- package/dist/runtime/components/polaris/ShBanner.vue.d.ts +19 -0
- package/dist/runtime/components/polaris/ShBox.d.vue.ts +39 -0
- package/dist/runtime/components/polaris/ShBox.vue +35 -0
- package/dist/runtime/components/polaris/ShBox.vue.d.ts +39 -0
- package/dist/runtime/components/polaris/ShButton.d.vue.ts +28 -0
- package/dist/runtime/components/polaris/ShButton.vue +24 -0
- package/dist/runtime/components/polaris/ShButton.vue.d.ts +28 -0
- package/dist/runtime/components/polaris/ShButtonGroup.d.vue.ts +17 -0
- package/dist/runtime/components/polaris/ShButtonGroup.vue +13 -0
- package/dist/runtime/components/polaris/ShButtonGroup.vue.d.ts +17 -0
- package/dist/runtime/components/polaris/ShCheckbox.d.vue.ts +28 -0
- package/dist/runtime/components/polaris/ShCheckbox.vue +24 -0
- package/dist/runtime/components/polaris/ShCheckbox.vue.d.ts +28 -0
- package/dist/runtime/components/polaris/ShChip.d.vue.ts +17 -0
- package/dist/runtime/components/polaris/ShChip.vue +13 -0
- package/dist/runtime/components/polaris/ShChip.vue.d.ts +17 -0
- package/dist/runtime/components/polaris/ShChoice.d.vue.ts +20 -0
- package/dist/runtime/components/polaris/ShChoice.vue +16 -0
- package/dist/runtime/components/polaris/ShChoice.vue.d.ts +20 -0
- package/dist/runtime/components/polaris/ShChoiceList.d.vue.ts +23 -0
- package/dist/runtime/components/polaris/ShChoiceList.vue +19 -0
- package/dist/runtime/components/polaris/ShChoiceList.vue.d.ts +23 -0
- package/dist/runtime/components/polaris/ShClickable.d.vue.ts +48 -0
- package/dist/runtime/components/polaris/ShClickable.vue +44 -0
- package/dist/runtime/components/polaris/ShClickable.vue.d.ts +48 -0
- package/dist/runtime/components/polaris/ShClickableChip.d.vue.ts +24 -0
- package/dist/runtime/components/polaris/ShClickableChip.vue +20 -0
- package/dist/runtime/components/polaris/ShClickableChip.vue.d.ts +24 -0
- package/dist/runtime/components/polaris/ShColorField.d.vue.ts +29 -0
- package/dist/runtime/components/polaris/ShColorField.vue +25 -0
- package/dist/runtime/components/polaris/ShColorField.vue.d.ts +29 -0
- package/dist/runtime/components/polaris/ShColorPicker.d.vue.ts +19 -0
- package/dist/runtime/components/polaris/ShColorPicker.vue +15 -0
- package/dist/runtime/components/polaris/ShColorPicker.vue.d.ts +19 -0
- package/dist/runtime/components/polaris/ShDateField.d.vue.ts +34 -0
- package/dist/runtime/components/polaris/ShDateField.vue +30 -0
- package/dist/runtime/components/polaris/ShDateField.vue.d.ts +34 -0
- package/dist/runtime/components/polaris/ShDatePicker.d.vue.ts +25 -0
- package/dist/runtime/components/polaris/ShDatePicker.vue +21 -0
- package/dist/runtime/components/polaris/ShDatePicker.vue.d.ts +25 -0
- package/dist/runtime/components/polaris/ShDivider.d.vue.ts +17 -0
- package/dist/runtime/components/polaris/ShDivider.vue +13 -0
- package/dist/runtime/components/polaris/ShDivider.vue.d.ts +17 -0
- package/dist/runtime/components/polaris/ShDropZone.d.vue.ts +25 -0
- package/dist/runtime/components/polaris/ShDropZone.vue +21 -0
- package/dist/runtime/components/polaris/ShDropZone.vue.d.ts +25 -0
- package/dist/runtime/components/polaris/ShEmailField.d.vue.ts +30 -0
- package/dist/runtime/components/polaris/ShEmailField.vue +26 -0
- package/dist/runtime/components/polaris/ShEmailField.vue.d.ts +30 -0
- package/dist/runtime/components/polaris/ShGrid.d.vue.ts +50 -0
- package/dist/runtime/components/polaris/ShGrid.vue +46 -0
- package/dist/runtime/components/polaris/ShGrid.vue.d.ts +50 -0
- package/dist/runtime/components/polaris/ShGridItem.d.vue.ts +41 -0
- package/dist/runtime/components/polaris/ShGridItem.vue +37 -0
- package/dist/runtime/components/polaris/ShGridItem.vue.d.ts +41 -0
- package/dist/runtime/components/polaris/ShHeading.d.vue.ts +18 -0
- package/dist/runtime/components/polaris/ShHeading.vue +14 -0
- package/dist/runtime/components/polaris/ShHeading.vue.d.ts +18 -0
- package/dist/runtime/components/polaris/ShIcon.d.vue.ts +20 -0
- package/dist/runtime/components/polaris/ShIcon.vue +16 -0
- package/dist/runtime/components/polaris/ShIcon.vue.d.ts +20 -0
- package/dist/runtime/components/polaris/ShImage.d.vue.ts +18 -0
- package/dist/runtime/components/polaris/ShImage.vue +14 -0
- package/dist/runtime/components/polaris/ShImage.vue.d.ts +18 -0
- package/dist/runtime/components/polaris/ShLink.d.vue.ts +24 -0
- package/dist/runtime/components/polaris/ShLink.vue +20 -0
- package/dist/runtime/components/polaris/ShLink.vue.d.ts +24 -0
- package/dist/runtime/components/polaris/ShListItem.d.vue.ts +13 -0
- package/dist/runtime/components/polaris/ShListItem.vue +9 -0
- package/dist/runtime/components/polaris/ShListItem.vue.d.ts +13 -0
- package/dist/runtime/components/polaris/ShMenu.d.vue.ts +16 -0
- package/dist/runtime/components/polaris/ShMenu.vue +12 -0
- package/dist/runtime/components/polaris/ShMenu.vue.d.ts +16 -0
- package/dist/runtime/components/polaris/ShModal.d.vue.ts +19 -0
- package/dist/runtime/components/polaris/ShModal.vue +15 -0
- package/dist/runtime/components/polaris/ShModal.vue.d.ts +19 -0
- package/dist/runtime/components/polaris/ShMoneyField.d.vue.ts +30 -0
- package/dist/runtime/components/polaris/ShMoneyField.vue +26 -0
- package/dist/runtime/components/polaris/ShMoneyField.vue.d.ts +30 -0
- package/dist/runtime/components/polaris/ShNumberField.d.vue.ts +34 -0
- package/dist/runtime/components/polaris/ShNumberField.vue +30 -0
- package/dist/runtime/components/polaris/ShNumberField.vue.d.ts +34 -0
- package/dist/runtime/components/polaris/ShOption.d.vue.ts +19 -0
- package/dist/runtime/components/polaris/ShOption.vue +15 -0
- package/dist/runtime/components/polaris/ShOption.vue.d.ts +19 -0
- package/dist/runtime/components/polaris/ShOptionGroup.d.vue.ts +17 -0
- package/dist/runtime/components/polaris/ShOptionGroup.vue +13 -0
- package/dist/runtime/components/polaris/ShOptionGroup.vue.d.ts +17 -0
- package/dist/runtime/components/polaris/ShOrderedList.d.vue.ts +13 -0
- package/dist/runtime/components/polaris/ShOrderedList.vue +9 -0
- package/dist/runtime/components/polaris/ShOrderedList.vue.d.ts +13 -0
- package/dist/runtime/components/polaris/ShPage.d.vue.ts +17 -0
- package/dist/runtime/components/polaris/ShPage.vue +13 -0
- package/dist/runtime/components/polaris/ShPage.vue.d.ts +17 -0
- package/dist/runtime/components/polaris/ShParagraph.d.vue.ts +21 -0
- package/dist/runtime/components/polaris/ShParagraph.vue +17 -0
- package/dist/runtime/components/polaris/ShParagraph.vue.d.ts +21 -0
- package/dist/runtime/components/polaris/ShPasswordField.d.vue.ts +30 -0
- package/dist/runtime/components/polaris/ShPasswordField.vue +26 -0
- package/dist/runtime/components/polaris/ShPasswordField.vue.d.ts +30 -0
- package/dist/runtime/components/polaris/ShPopover.d.vue.ts +21 -0
- package/dist/runtime/components/polaris/ShPopover.vue +17 -0
- package/dist/runtime/components/polaris/ShPopover.vue.d.ts +21 -0
- package/dist/runtime/components/polaris/ShQueryContainer.d.vue.ts +16 -0
- package/dist/runtime/components/polaris/ShQueryContainer.vue +12 -0
- package/dist/runtime/components/polaris/ShQueryContainer.vue.d.ts +16 -0
- package/dist/runtime/components/polaris/ShSearchField.d.vue.ts +30 -0
- package/dist/runtime/components/polaris/ShSearchField.vue +26 -0
- package/dist/runtime/components/polaris/ShSearchField.vue.d.ts +30 -0
- package/dist/runtime/components/polaris/ShSection.d.vue.ts +18 -0
- package/dist/runtime/components/polaris/ShSection.vue +14 -0
- package/dist/runtime/components/polaris/ShSection.vue.d.ts +18 -0
- package/dist/runtime/components/polaris/ShSelect.d.vue.ts +26 -0
- package/dist/runtime/components/polaris/ShSelect.vue +22 -0
- package/dist/runtime/components/polaris/ShSelect.vue.d.ts +26 -0
- package/dist/runtime/components/polaris/ShSpinner.d.vue.ts +17 -0
- package/dist/runtime/components/polaris/ShSpinner.vue +13 -0
- package/dist/runtime/components/polaris/ShSpinner.vue.d.ts +17 -0
- package/dist/runtime/components/polaris/ShStack.d.vue.ts +46 -0
- package/dist/runtime/components/polaris/ShStack.vue +42 -0
- package/dist/runtime/components/polaris/ShStack.vue.d.ts +46 -0
- package/dist/runtime/components/polaris/ShSwitch.d.vue.ts +27 -0
- package/dist/runtime/components/polaris/ShSwitch.vue +23 -0
- package/dist/runtime/components/polaris/ShSwitch.vue.d.ts +27 -0
- package/dist/runtime/components/polaris/ShTable.d.vue.ts +20 -0
- package/dist/runtime/components/polaris/ShTable.vue +16 -0
- package/dist/runtime/components/polaris/ShTable.vue.d.ts +20 -0
- package/dist/runtime/components/polaris/ShTableBody.d.vue.ts +13 -0
- package/dist/runtime/components/polaris/ShTableBody.vue +9 -0
- package/dist/runtime/components/polaris/ShTableBody.vue.d.ts +13 -0
- package/dist/runtime/components/polaris/ShTableCell.d.vue.ts +13 -0
- package/dist/runtime/components/polaris/ShTableCell.vue +9 -0
- package/dist/runtime/components/polaris/ShTableCell.vue.d.ts +13 -0
- package/dist/runtime/components/polaris/ShTableHeader.d.vue.ts +17 -0
- package/dist/runtime/components/polaris/ShTableHeader.vue +13 -0
- package/dist/runtime/components/polaris/ShTableHeader.vue.d.ts +17 -0
- package/dist/runtime/components/polaris/ShTableHeaderRow.d.vue.ts +13 -0
- package/dist/runtime/components/polaris/ShTableHeaderRow.vue +9 -0
- package/dist/runtime/components/polaris/ShTableHeaderRow.vue.d.ts +13 -0
- package/dist/runtime/components/polaris/ShTableRow.d.vue.ts +16 -0
- package/dist/runtime/components/polaris/ShTableRow.vue +12 -0
- package/dist/runtime/components/polaris/ShTableRow.vue.d.ts +16 -0
- package/dist/runtime/components/polaris/ShText.d.vue.ts +22 -0
- package/dist/runtime/components/polaris/ShText.vue +18 -0
- package/dist/runtime/components/polaris/ShText.vue.d.ts +22 -0
- package/dist/runtime/components/polaris/ShTextArea.d.vue.ts +31 -0
- package/dist/runtime/components/polaris/ShTextArea.vue +27 -0
- package/dist/runtime/components/polaris/ShTextArea.vue.d.ts +31 -0
- package/dist/runtime/components/polaris/ShTextField.d.vue.ts +33 -0
- package/dist/runtime/components/polaris/ShTextField.vue +29 -0
- package/dist/runtime/components/polaris/ShTextField.vue.d.ts +33 -0
- package/dist/runtime/components/polaris/ShThumbnail.d.vue.ts +18 -0
- package/dist/runtime/components/polaris/ShThumbnail.vue +14 -0
- package/dist/runtime/components/polaris/ShThumbnail.vue.d.ts +18 -0
- package/dist/runtime/components/polaris/ShTooltip.d.vue.ts +13 -0
- package/dist/runtime/components/polaris/ShTooltip.vue +9 -0
- package/dist/runtime/components/polaris/ShTooltip.vue.d.ts +13 -0
- package/dist/runtime/components/polaris/ShUnorderedList.d.vue.ts +13 -0
- package/dist/runtime/components/polaris/ShUnorderedList.vue +9 -0
- package/dist/runtime/components/polaris/ShUnorderedList.vue.d.ts +13 -0
- package/dist/runtime/components/polaris/ShUrlField.d.vue.ts +30 -0
- package/dist/runtime/components/polaris/ShUrlField.vue +26 -0
- package/dist/runtime/components/polaris/ShUrlField.vue.d.ts +30 -0
- package/dist/runtime/components/polaris/types.d.ts +1 -0
- package/dist/runtime/components/polaris/types.js +0 -0
- package/dist/runtime/composables/useAppBridge.d.ts +8 -0
- package/dist/runtime/composables/useAppBridge.js +21 -0
- package/dist/runtime/composables/useShopifyFetch.d.ts +4 -0
- package/dist/runtime/composables/useShopifyFetch.js +32 -0
- package/dist/runtime/middleware/shopify-auth.d.ts +2 -0
- package/dist/runtime/middleware/shopify-auth.js +11 -0
- package/dist/runtime/plugin.d.ts +1 -0
- package/dist/runtime/plugin.js +1 -0
- package/dist/runtime/plugins/app-bridge.d.ts +12 -0
- package/dist/runtime/plugins/app-bridge.js +28 -0
- package/dist/runtime/plugins/polaris.d.ts +1 -0
- package/dist/runtime/plugins/polaris.js +0 -0
- package/dist/runtime/server/index.d.ts +2 -0
- package/dist/runtime/server/index.js +7 -0
- package/dist/runtime/server/routes/auth-callback.d.ts +6 -0
- package/dist/runtime/server/routes/auth-callback.js +45 -0
- package/dist/runtime/server/routes/auth-exit-iframe.d.ts +6 -0
- package/dist/runtime/server/routes/auth-exit-iframe.js +11 -0
- package/dist/runtime/server/routes/auth-session-token.d.ts +7 -0
- package/dist/runtime/server/routes/auth-session-token.js +27 -0
- package/dist/runtime/server/routes/auth.d.ts +6 -0
- package/dist/runtime/server/routes/auth.js +23 -0
- package/dist/runtime/server/services/shopify.d.ts +38 -0
- package/dist/runtime/server/services/shopify.js +89 -0
- package/dist/runtime/server/test-helpers/index.d.ts +25 -0
- package/dist/runtime/server/test-helpers/index.js +25 -0
- package/dist/runtime/server/test-helpers/package.json +4 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/runtime/server/utils/authenticate-admin.d.ts +17 -0
- package/dist/runtime/server/utils/authenticate-admin.js +197 -0
- package/dist/runtime/server/utils/authenticate-flow.d.ts +17 -0
- package/dist/runtime/server/utils/authenticate-flow.js +70 -0
- package/dist/runtime/server/utils/authenticate-fulfillment-service.d.ts +16 -0
- package/dist/runtime/server/utils/authenticate-fulfillment-service.js +54 -0
- package/dist/runtime/server/utils/authenticate-pos.d.ts +18 -0
- package/dist/runtime/server/utils/authenticate-pos.js +31 -0
- package/dist/runtime/server/utils/authenticate-public.d.ts +17 -0
- package/dist/runtime/server/utils/authenticate-public.js +31 -0
- package/dist/runtime/server/utils/authenticate-webhook.d.ts +26 -0
- package/dist/runtime/server/utils/authenticate-webhook.js +76 -0
- package/dist/runtime/server/utils/clients.d.ts +25 -0
- package/dist/runtime/server/utils/clients.js +36 -0
- package/dist/runtime/server/utils/helpers.d.ts +10 -0
- package/dist/runtime/server/utils/helpers.js +96 -0
- package/dist/runtime/server/utils/login.d.ts +17 -0
- package/dist/runtime/server/utils/login.js +44 -0
- package/dist/runtime/server/utils/register-webhooks.d.ts +19 -0
- package/dist/runtime/server/utils/register-webhooks.js +5 -0
- package/dist/runtime/server/utils/unauthenticated-admin.d.ts +21 -0
- package/dist/runtime/server/utils/unauthenticated-admin.js +15 -0
- package/dist/runtime/server/utils/unauthenticated-storefront.d.ts +24 -0
- package/dist/runtime/server/utils/unauthenticated-storefront.js +21 -0
- package/dist/runtime/types.d.ts +141 -0
- package/dist/runtime/types.js +11 -0
- package/dist/types.d.mts +3 -0
- package/package.json +66 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useNuxtApp } from "#app";
|
|
2
|
+
export function useShopifyFetch() {
|
|
3
|
+
if (import.meta.server) {
|
|
4
|
+
throw new Error("useShopifyFetch() can only be used on the client side");
|
|
5
|
+
}
|
|
6
|
+
const nuxtApp = useNuxtApp();
|
|
7
|
+
return async (url, options = {}) => {
|
|
8
|
+
const shopify = nuxtApp.$shopify;
|
|
9
|
+
if (!shopify) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"Shopify App Bridge is not available. Make sure the app is loaded within the Shopify Admin."
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
const token = await shopify.idToken();
|
|
15
|
+
const headers = new Headers(options.headers || {});
|
|
16
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
17
|
+
const response = await fetch(url, {
|
|
18
|
+
...options,
|
|
19
|
+
headers
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Shopify fetch failed: ${response.status} ${response.statusText}`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
const contentType = response.headers.get("content-type");
|
|
27
|
+
if (contentType?.includes("application/json")) {
|
|
28
|
+
return { data: await response.json(), response };
|
|
29
|
+
}
|
|
30
|
+
return { data: await response.text(), response };
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineNuxtRouteMiddleware } from "#app";
|
|
2
|
+
import { navigateTo } from "nuxt/app";
|
|
3
|
+
export default defineNuxtRouteMiddleware(() => {
|
|
4
|
+
if (import.meta.server || typeof window === "undefined") {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
const shop = window.shopify?.config?.shop;
|
|
8
|
+
if (!shop) {
|
|
9
|
+
return navigateTo("/auth");
|
|
10
|
+
}
|
|
11
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ShopifyGlobal } from '@shopify/app-bridge-types';
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
shopify: ShopifyGlobal;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
declare const _default: import("nuxt/app").Plugin<{
|
|
8
|
+
shopify: ShopifyGlobal;
|
|
9
|
+
}> & import("nuxt/app").ObjectPlugin<{
|
|
10
|
+
shopify: ShopifyGlobal;
|
|
11
|
+
}>;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineNuxtPlugin } from "#app";
|
|
2
|
+
export default defineNuxtPlugin(() => {
|
|
3
|
+
if (import.meta.server || typeof window === "undefined") {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
console.log(
|
|
7
|
+
"[shopify-nuxt] App Bridge plugin initializing, window.shopify available:",
|
|
8
|
+
!!window.shopify
|
|
9
|
+
);
|
|
10
|
+
const bridge = new Proxy({}, {
|
|
11
|
+
get(_target, prop, receiver) {
|
|
12
|
+
if (!window.shopify) {
|
|
13
|
+
console.warn(
|
|
14
|
+
"[shopify-nuxt] window.shopify is not available yet. The CDN script may not have loaded."
|
|
15
|
+
);
|
|
16
|
+
throw new Error(
|
|
17
|
+
"Shopify App Bridge is not available. Ensure the app is loaded inside the Shopify Admin."
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return Reflect.get(window.shopify, prop, receiver);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
provide: {
|
|
25
|
+
shopify: bridge
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle the OAuth callback from Shopify.
|
|
3
|
+
* Validates the callback, stores the session, and redirects to the app.
|
|
4
|
+
*/
|
|
5
|
+
declare const _default: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<import("h3").HTTPResponse>>;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { defineEventHandler, getQuery, sendRedirect, createError } from "h3";
|
|
2
|
+
import {
|
|
3
|
+
getShopifyApi,
|
|
4
|
+
getResolvedConfig,
|
|
5
|
+
getSessionStorage
|
|
6
|
+
} from "../services/shopify.js";
|
|
7
|
+
import { createAdminApiContext } from "../utils/clients.js";
|
|
8
|
+
export default defineEventHandler(async (event) => {
|
|
9
|
+
const api = getShopifyApi();
|
|
10
|
+
const config = getResolvedConfig();
|
|
11
|
+
const sessionStorage = getSessionStorage();
|
|
12
|
+
const query = getQuery(event);
|
|
13
|
+
try {
|
|
14
|
+
const callbackResponse = await api.auth.callback({
|
|
15
|
+
rawRequest: event.req,
|
|
16
|
+
rawResponse: event.res
|
|
17
|
+
});
|
|
18
|
+
const { session } = callbackResponse;
|
|
19
|
+
await sessionStorage.storeSession(session);
|
|
20
|
+
if (config.useOnlineTokens && session.isOnline) {
|
|
21
|
+
}
|
|
22
|
+
if (config.hooks?.afterAuth) {
|
|
23
|
+
const admin = createAdminApiContext(api, session);
|
|
24
|
+
await config.hooks.afterAuth({ session, admin });
|
|
25
|
+
}
|
|
26
|
+
if (config.webhooks) {
|
|
27
|
+
try {
|
|
28
|
+
await api.webhooks.register({ session });
|
|
29
|
+
} catch (e) {
|
|
30
|
+
console.warn("[shopify-nuxt] Failed to register webhooks:", e);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const shop = session.shop;
|
|
34
|
+
const host = query.host || Buffer.from(`${shop}/admin`).toString("base64");
|
|
35
|
+
const redirectUrl = `${config.appUrl}?shop=${shop}&host=${host}`;
|
|
36
|
+
return sendRedirect(event, redirectUrl, 302);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error("[shopify-nuxt] Auth callback error:", error);
|
|
39
|
+
throw createError({
|
|
40
|
+
statusCode: 500,
|
|
41
|
+
statusMessage: "Authentication failed",
|
|
42
|
+
data: { error: error.message }
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineEventHandler, getQuery, setResponseHeader } from "h3";
|
|
2
|
+
import { getResolvedConfig } from "../services/shopify.js";
|
|
3
|
+
import { renderAppBridgePage } from "../utils/helpers.js";
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
const config = getResolvedConfig();
|
|
6
|
+
const query = getQuery(event);
|
|
7
|
+
const destination = query.exitIframe || config.appUrl;
|
|
8
|
+
const html = renderAppBridgePage(config.apiKey, config.appUrl, destination);
|
|
9
|
+
setResponseHeader(event, "content-type", "text/html");
|
|
10
|
+
return html;
|
|
11
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the session token bounce page.
|
|
3
|
+
* For embedded apps, this page loads App Bridge which provides a session token
|
|
4
|
+
* via a search param, then redirects back to the original request.
|
|
5
|
+
*/
|
|
6
|
+
declare const _default: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<string>>;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineEventHandler, setResponseHeader } from "h3";
|
|
2
|
+
import { getResolvedConfig } from "../services/shopify.js";
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const config = getResolvedConfig();
|
|
5
|
+
const html = `<!DOCTYPE html>
|
|
6
|
+
<html>
|
|
7
|
+
<head>
|
|
8
|
+
<meta charset="utf-8" />
|
|
9
|
+
<meta name="shopify-api-key" content="${config.apiKey}" />
|
|
10
|
+
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"><\/script>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<script>
|
|
14
|
+
if (window.shopify && window.shopify.idToken) {
|
|
15
|
+
window.shopify.idToken().then(function(token) {
|
|
16
|
+
var url = new URL(window.location.href);
|
|
17
|
+
url.searchParams.set('id_token', token);
|
|
18
|
+
url.pathname = '/';
|
|
19
|
+
window.location.href = url.toString();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
<\/script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>`;
|
|
25
|
+
setResponseHeader(event, "content-type", "text/html");
|
|
26
|
+
return html;
|
|
27
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle the initial auth request.
|
|
3
|
+
* For token exchange flow (embedded apps), this redirects to Shopify OAuth.
|
|
4
|
+
*/
|
|
5
|
+
declare const _default: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<import("h3").HTTPResponse>>;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineEventHandler, getQuery, sendRedirect, createError } from "h3";
|
|
2
|
+
import { getShopifyApi, getResolvedConfig } from "../services/shopify.js";
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const api = getShopifyApi();
|
|
5
|
+
const config = getResolvedConfig();
|
|
6
|
+
const query = getQuery(event);
|
|
7
|
+
const shop = query.shop;
|
|
8
|
+
if (!shop) {
|
|
9
|
+
throw createError({
|
|
10
|
+
statusCode: 400,
|
|
11
|
+
statusMessage: "Missing shop parameter"
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
const callbackUrl = `${config.appUrl}${config.auth.callbackPath}`;
|
|
15
|
+
const authUrl = await api.auth.begin({
|
|
16
|
+
shop,
|
|
17
|
+
callbackPath: callbackUrl,
|
|
18
|
+
isOnline: false,
|
|
19
|
+
rawRequest: event.req,
|
|
20
|
+
rawResponse: event.res
|
|
21
|
+
});
|
|
22
|
+
return sendRedirect(event, authUrl);
|
|
23
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type Shopify } from '@shopify/shopify-api';
|
|
2
|
+
import type { SessionStorage } from '@shopify/shopify-app-session-storage';
|
|
3
|
+
import type { ShopifyRuntimeConfig, ResolvedConfig } from '../../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Configure Shopify with runtime options that can't be serialized in nuxt.config.ts.
|
|
6
|
+
*
|
|
7
|
+
* Call this in a Nitro plugin (`server/plugins/shopify.ts`):
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { configureShopify } from '#shopify/server'
|
|
11
|
+
*
|
|
12
|
+
* export default defineNitroPlugin(() => {
|
|
13
|
+
* configureShopify({
|
|
14
|
+
* sessionStorage: new PrismaSessionStorage(prisma),
|
|
15
|
+
* hooks: {
|
|
16
|
+
* afterAuth: async ({ session }) => {
|
|
17
|
+
* // seed store data
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* })
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function configureShopify(config: ShopifyRuntimeConfig): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get the initialized Shopify API instance.
|
|
27
|
+
* Lazily creates it on first access using module options + runtime config.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getShopifyApi(): Shopify;
|
|
30
|
+
/**
|
|
31
|
+
* Get the fully resolved configuration (module options + runtime config merged).
|
|
32
|
+
*/
|
|
33
|
+
export declare function getResolvedConfig(): ResolvedConfig;
|
|
34
|
+
/**
|
|
35
|
+
* Get the session storage instance.
|
|
36
|
+
* Throws if not configured and not a ShopifyAdmin app.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getSessionStorage(): SessionStorage;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { nodeAdapterInitialized } from "@shopify/shopify-api/adapters/node";
|
|
2
|
+
import { shopifyApi } from "@shopify/shopify-api";
|
|
3
|
+
import { useRuntimeConfig } from "#imports";
|
|
4
|
+
import { AppDistribution } from "../../types.js";
|
|
5
|
+
if (!nodeAdapterInitialized) {
|
|
6
|
+
throw new Error("Shopify Node adapter failed to initialize");
|
|
7
|
+
}
|
|
8
|
+
let _shopifyApi = null;
|
|
9
|
+
let _resolvedConfig = null;
|
|
10
|
+
let _runtimeConfig = null;
|
|
11
|
+
const LIBRARY_VERSION = "1.0.0";
|
|
12
|
+
export function configureShopify(config) {
|
|
13
|
+
_runtimeConfig = config;
|
|
14
|
+
_shopifyApi = null;
|
|
15
|
+
_resolvedConfig = null;
|
|
16
|
+
}
|
|
17
|
+
export function getShopifyApi() {
|
|
18
|
+
if (_shopifyApi) return _shopifyApi;
|
|
19
|
+
const resolved = getResolvedConfig();
|
|
20
|
+
let appUrl;
|
|
21
|
+
try {
|
|
22
|
+
appUrl = new URL(resolved.appUrl);
|
|
23
|
+
} catch {
|
|
24
|
+
throw new Error(
|
|
25
|
+
resolved.appUrl === "" ? "Detected an empty appUrl configuration. Set NUXT_SHOPIFY_APP_URL or configure shopify.appUrl in nuxt.config.ts." : `Invalid appUrl configuration '${resolved.appUrl}'. Please provide a valid URL.`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
const userAgentPrefix = `Shopify Nuxt Module v${LIBRARY_VERSION}`;
|
|
29
|
+
_shopifyApi = shopifyApi({
|
|
30
|
+
apiKey: resolved.apiKey,
|
|
31
|
+
apiSecretKey: resolved.apiSecretKey,
|
|
32
|
+
scopes: resolved.scopes,
|
|
33
|
+
hostName: appUrl.host,
|
|
34
|
+
hostScheme: appUrl.protocol.replace(":", ""),
|
|
35
|
+
apiVersion: resolved.apiVersion,
|
|
36
|
+
isEmbeddedApp: true,
|
|
37
|
+
isCustomStoreApp: resolved.distribution === AppDistribution.ShopifyAdmin,
|
|
38
|
+
userAgentPrefix,
|
|
39
|
+
billing: resolved.billing,
|
|
40
|
+
future: {
|
|
41
|
+
unstable_managedPricingSupport: true
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
if (resolved.webhooks) {
|
|
45
|
+
_shopifyApi.webhooks.addHandlers(resolved.webhooks);
|
|
46
|
+
}
|
|
47
|
+
return _shopifyApi;
|
|
48
|
+
}
|
|
49
|
+
export function getResolvedConfig() {
|
|
50
|
+
if (_resolvedConfig) return _resolvedConfig;
|
|
51
|
+
const nuxtConfig = useRuntimeConfig();
|
|
52
|
+
const moduleOpts = nuxtConfig.shopify || {};
|
|
53
|
+
const runtime = _runtimeConfig || {};
|
|
54
|
+
const authPathPrefix = moduleOpts.authPathPrefix || "/_shopify/auth";
|
|
55
|
+
const auth = {
|
|
56
|
+
path: authPathPrefix,
|
|
57
|
+
callbackPath: `${authPathPrefix}/callback`,
|
|
58
|
+
exitIframePath: `${authPathPrefix}/exit-iframe`,
|
|
59
|
+
patchSessionTokenPath: `${authPathPrefix}/session-token`,
|
|
60
|
+
loginPath: `${authPathPrefix}/login`
|
|
61
|
+
};
|
|
62
|
+
_resolvedConfig = {
|
|
63
|
+
apiKey: moduleOpts.apiKey || "",
|
|
64
|
+
apiSecretKey: moduleOpts.apiSecretKey || "",
|
|
65
|
+
scopes: moduleOpts.scopes || [],
|
|
66
|
+
appUrl: moduleOpts.appUrl || "",
|
|
67
|
+
apiVersion: moduleOpts.apiVersion || "2025-01",
|
|
68
|
+
authPathPrefix,
|
|
69
|
+
distribution: moduleOpts.distribution || AppDistribution.AppStore,
|
|
70
|
+
useOnlineTokens: moduleOpts.useOnlineTokens ?? false,
|
|
71
|
+
auth,
|
|
72
|
+
sessionStorage: runtime.sessionStorage,
|
|
73
|
+
webhooks: runtime.webhooks,
|
|
74
|
+
hooks: runtime.hooks || {},
|
|
75
|
+
billing: runtime.billing
|
|
76
|
+
};
|
|
77
|
+
return _resolvedConfig;
|
|
78
|
+
}
|
|
79
|
+
export function getSessionStorage() {
|
|
80
|
+
const config = getResolvedConfig();
|
|
81
|
+
if (!config.sessionStorage) {
|
|
82
|
+
if (config.distribution !== AppDistribution.ShopifyAdmin) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
"No session storage configured. Call configureShopify({ sessionStorage: ... }) in a server plugin. See https://github.com/Shopify/shopify-app-js#session-storage-options for options."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return config.sessionStorage;
|
|
89
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Session } from '@shopify/shopify-api';
|
|
2
|
+
/**
|
|
3
|
+
* Test helper that returns a mock configuration for testing.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { testConfig } from 'shopify-nuxt/test-helpers'
|
|
7
|
+
*
|
|
8
|
+
* const config = testConfig()
|
|
9
|
+
* // Use in your test setup
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare function testConfig(overrides?: Record<string, any>): {
|
|
13
|
+
apiKey: string;
|
|
14
|
+
apiSecretKey: string;
|
|
15
|
+
scopes: string[];
|
|
16
|
+
appUrl: string;
|
|
17
|
+
apiVersion: string;
|
|
18
|
+
authPathPrefix: string;
|
|
19
|
+
distribution: string;
|
|
20
|
+
useOnlineTokens: boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Create a mock session for testing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function testSession(overrides?: Partial<Session>): any;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function testConfig(overrides) {
|
|
2
|
+
return {
|
|
3
|
+
apiKey: "test-api-key",
|
|
4
|
+
apiSecretKey: "test-api-secret-key",
|
|
5
|
+
scopes: ["read_products"],
|
|
6
|
+
appUrl: "https://test-app.example.com",
|
|
7
|
+
apiVersion: "2025-01",
|
|
8
|
+
authPathPrefix: "/_shopify/auth",
|
|
9
|
+
distribution: "app_store",
|
|
10
|
+
useOnlineTokens: false,
|
|
11
|
+
...overrides
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function testSession(overrides) {
|
|
15
|
+
return {
|
|
16
|
+
id: "offline_test-shop.myshopify.com",
|
|
17
|
+
shop: "test-shop.myshopify.com",
|
|
18
|
+
state: "active",
|
|
19
|
+
isOnline: false,
|
|
20
|
+
accessToken: "test-access-token",
|
|
21
|
+
scope: "read_products",
|
|
22
|
+
isActive: () => true,
|
|
23
|
+
...overrides
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { AdminContext } from '../../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Authenticate an admin request and return an authenticated admin context.
|
|
5
|
+
*
|
|
6
|
+
* Use this in your server API routes to interact with the Shopify Admin API:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // server/api/products.ts
|
|
10
|
+
* export default defineEventHandler(async (event) => {
|
|
11
|
+
* const { admin, session } = await useShopifyAdmin(event)
|
|
12
|
+
* const response = await admin.graphql(`{ shop { name } }`)
|
|
13
|
+
* return response
|
|
14
|
+
* })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function useShopifyAdmin(event: H3Event): Promise<AdminContext>;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { getQuery, createError } from "h3";
|
|
2
|
+
import {
|
|
3
|
+
getShopifyApi,
|
|
4
|
+
getResolvedConfig,
|
|
5
|
+
getSessionStorage
|
|
6
|
+
} from "../services/shopify.js";
|
|
7
|
+
import { createAdminApiContext } from "./clients.js";
|
|
8
|
+
import {
|
|
9
|
+
getSessionTokenHeader,
|
|
10
|
+
getSessionTokenFromUrlParam,
|
|
11
|
+
validateSessionToken,
|
|
12
|
+
ensureCORSHeaders,
|
|
13
|
+
isBotRequest,
|
|
14
|
+
getShopFromEvent,
|
|
15
|
+
renderAppBridgePage
|
|
16
|
+
} from "./helpers.js";
|
|
17
|
+
import { AppDistribution } from "../../types.js";
|
|
18
|
+
export async function useShopifyAdmin(event) {
|
|
19
|
+
const api = getShopifyApi();
|
|
20
|
+
const config = getResolvedConfig();
|
|
21
|
+
const sessionStorage = getSessionStorage();
|
|
22
|
+
if (isBotRequest(event)) {
|
|
23
|
+
throw createError({
|
|
24
|
+
statusCode: 410,
|
|
25
|
+
statusMessage: "Gone"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (event.method === "OPTIONS") {
|
|
29
|
+
ensureCORSHeaders(event);
|
|
30
|
+
throw createError({
|
|
31
|
+
statusCode: 204,
|
|
32
|
+
statusMessage: "No Content"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const headerToken = getSessionTokenHeader(event);
|
|
36
|
+
const paramToken = getSessionTokenFromUrlParam(event);
|
|
37
|
+
const sessionToken = headerToken || paramToken;
|
|
38
|
+
if (!sessionToken) {
|
|
39
|
+
const shop2 = getShopFromEvent(event);
|
|
40
|
+
if (!shop2) {
|
|
41
|
+
throw createError({
|
|
42
|
+
statusCode: 400,
|
|
43
|
+
statusMessage: "Missing shop parameter"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const query = getQuery(event);
|
|
47
|
+
const host = query.host;
|
|
48
|
+
if (!host) {
|
|
49
|
+
throw createError({
|
|
50
|
+
statusCode: 302,
|
|
51
|
+
statusMessage: "Redirect",
|
|
52
|
+
data: {
|
|
53
|
+
redirect: `${config.auth.path}?shop=${shop2}`
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
throw createError({
|
|
58
|
+
statusCode: 401,
|
|
59
|
+
statusMessage: "Unauthorized - No session token"
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
let payload;
|
|
63
|
+
let shop;
|
|
64
|
+
let sessionId;
|
|
65
|
+
if (config.distribution !== AppDistribution.ShopifyAdmin) {
|
|
66
|
+
payload = await validateSessionToken(sessionToken);
|
|
67
|
+
const dest = new URL(payload.dest);
|
|
68
|
+
shop = dest.hostname;
|
|
69
|
+
sessionId = config.useOnlineTokens ? api.session.getJwtSessionId(shop, payload.sub) : api.session.getOfflineId(shop);
|
|
70
|
+
} else {
|
|
71
|
+
shop = getShopFromEvent(event) || "";
|
|
72
|
+
sessionId = await api.session.getCurrentId({
|
|
73
|
+
isOnline: config.useOnlineTokens,
|
|
74
|
+
rawRequest: event.req,
|
|
75
|
+
rawResponse: event.res
|
|
76
|
+
}) || "";
|
|
77
|
+
}
|
|
78
|
+
const existingSession = sessionId ? await sessionStorage.loadSession(sessionId) : void 0;
|
|
79
|
+
let session;
|
|
80
|
+
if (existingSession && existingSession.isActive(api.config.scopes)) {
|
|
81
|
+
session = existingSession;
|
|
82
|
+
} else {
|
|
83
|
+
if (config.distribution === AppDistribution.ShopifyAdmin) {
|
|
84
|
+
if (!existingSession) {
|
|
85
|
+
throw createError({
|
|
86
|
+
statusCode: 401,
|
|
87
|
+
statusMessage: "No session found for this shop"
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
session = existingSession;
|
|
91
|
+
} else {
|
|
92
|
+
session = await performTokenExchange(
|
|
93
|
+
api,
|
|
94
|
+
config,
|
|
95
|
+
sessionStorage,
|
|
96
|
+
shop,
|
|
97
|
+
sessionToken
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const admin = createAdminApiContext(api, session);
|
|
102
|
+
const billing = createBillingContext(api, config, session, event);
|
|
103
|
+
const cors = (response) => {
|
|
104
|
+
response.headers.set("Access-Control-Allow-Origin", config.appUrl);
|
|
105
|
+
response.headers.set("Access-Control-Allow-Credentials", "true");
|
|
106
|
+
return response;
|
|
107
|
+
};
|
|
108
|
+
const redirect = (url, init) => {
|
|
109
|
+
const target = init?.target || "_self";
|
|
110
|
+
if (url.startsWith("shopify://") || target !== "_self") {
|
|
111
|
+
return new Response(
|
|
112
|
+
renderAppBridgePage(config.apiKey, config.appUrl, url),
|
|
113
|
+
{
|
|
114
|
+
status: 200,
|
|
115
|
+
headers: { "Content-Type": "text/html" }
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
return new Response(null, {
|
|
120
|
+
status: 302,
|
|
121
|
+
headers: { Location: url }
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
session,
|
|
126
|
+
admin,
|
|
127
|
+
sessionToken: payload,
|
|
128
|
+
billing,
|
|
129
|
+
cors,
|
|
130
|
+
redirect
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
async function performTokenExchange(api, config, sessionStorage, shop, sessionToken) {
|
|
134
|
+
const { session: onlineSession } = await api.auth.tokenExchange({
|
|
135
|
+
shop,
|
|
136
|
+
sessionToken,
|
|
137
|
+
requestedTokenType: config.useOnlineTokens ? "online" : "offline"
|
|
138
|
+
});
|
|
139
|
+
await sessionStorage.storeSession(onlineSession);
|
|
140
|
+
if (config.useOnlineTokens) {
|
|
141
|
+
const { session: offlineSession } = await api.auth.tokenExchange({
|
|
142
|
+
shop,
|
|
143
|
+
sessionToken,
|
|
144
|
+
requestedTokenType: "offline"
|
|
145
|
+
});
|
|
146
|
+
await sessionStorage.storeSession(offlineSession);
|
|
147
|
+
}
|
|
148
|
+
if (config.hooks?.afterAuth) {
|
|
149
|
+
const admin = createAdminApiContext(api, onlineSession);
|
|
150
|
+
await config.hooks.afterAuth({ session: onlineSession, admin });
|
|
151
|
+
}
|
|
152
|
+
return onlineSession;
|
|
153
|
+
}
|
|
154
|
+
function createBillingContext(api, config, session, _event) {
|
|
155
|
+
return {
|
|
156
|
+
require: async (options) => {
|
|
157
|
+
if (!config.billing) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
"Billing is not configured. Add billing config to configureShopify()."
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
const plans = Array.isArray(options.plans) ? options.plans : [options.plans];
|
|
163
|
+
const hasPayment = await api.billing.check({ session, plans });
|
|
164
|
+
if (!hasPayment) {
|
|
165
|
+
const confirmationUrl = await api.billing.request({
|
|
166
|
+
session,
|
|
167
|
+
plan: plans[0],
|
|
168
|
+
isTest: true
|
|
169
|
+
});
|
|
170
|
+
throw new Response(null, {
|
|
171
|
+
status: 302,
|
|
172
|
+
headers: { Location: confirmationUrl }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
check: async (options) => {
|
|
177
|
+
if (!config.billing) return { hasActivePayment: false };
|
|
178
|
+
const plans = options?.plans ? Array.isArray(options.plans) ? options.plans : [options.plans] : Object.keys(config.billing);
|
|
179
|
+
return api.billing.check({ session, plans });
|
|
180
|
+
},
|
|
181
|
+
request: async (options) => {
|
|
182
|
+
if (!config.billing) {
|
|
183
|
+
throw new Error("Billing is not configured.");
|
|
184
|
+
}
|
|
185
|
+
const confirmationUrl = await api.billing.request({
|
|
186
|
+
session,
|
|
187
|
+
plan: options.plan,
|
|
188
|
+
isTest: options.isTest ?? true,
|
|
189
|
+
returnUrl: options.returnUrl
|
|
190
|
+
});
|
|
191
|
+
return new Response(null, {
|
|
192
|
+
status: 302,
|
|
193
|
+
headers: { Location: confirmationUrl }
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|