shopify-app-js 0.0.1-security → 1.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.

Potentially problematic release.


This version of shopify-app-js might be problematic. Click here for more details.

Files changed (424) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +14 -0
  3. package/.changeset/flat-clouds-camp.md +5 -0
  4. package/.eslintrc.js +36 -0
  5. package/.github/CODEOWNERS +1 -0
  6. package/.github/ISSUE_TEMPLATE/BUG_REPORT.md +36 -0
  7. package/.github/ISSUE_TEMPLATE/ENHANCEMENT.md +9 -0
  8. package/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +9 -0
  9. package/.github/PULL_REQUEST_TEMPLATE.md +34 -0
  10. package/.github/dependabot.yml +9 -0
  11. package/.github/workflows/changelog.yml +30 -0
  12. package/.github/workflows/ci.yml +22 -0
  13. package/.github/workflows/cla.yml +22 -0
  14. package/.github/workflows/close-waiting-for-response-issues.yml +20 -0
  15. package/.github/workflows/main-release.yml +36 -0
  16. package/.github/workflows/markdown_link_check.yml +14 -0
  17. package/.github/workflows/markdown_link_checker_config.json +9 -0
  18. package/.github/workflows/publish-experimental-build.yml +30 -0
  19. package/.github/workflows/release-candidate.yml +35 -0
  20. package/.github/workflows/remove-labels-on-activity.yml +16 -0
  21. package/.github/workflows/stale.yml +26 -0
  22. package/.prettierignore +5 -0
  23. package/.prettierrc +1 -0
  24. package/CODE_OF_CONDUCT.md +46 -0
  25. package/CONTRIBUTING.md +39 -0
  26. package/LICENSE.md +9 -0
  27. package/README.md +62 -3
  28. package/RELEASING.md +142 -0
  29. package/loom.config.ts +115 -0
  30. package/package.json +44 -3
  31. package/packages/.prettierrc +1 -0
  32. package/packages/shopify-app-express/CHANGELOG.md +316 -0
  33. package/packages/shopify-app-express/LICENSE.md +9 -0
  34. package/packages/shopify-app-express/README.md +93 -0
  35. package/packages/shopify-app-express/docs/reference/README.md +3 -0
  36. package/packages/shopify-app-express/docs/reference/auth.md +76 -0
  37. package/packages/shopify-app-express/docs/reference/cspHeaders.md +24 -0
  38. package/packages/shopify-app-express/docs/reference/ensureInstalledOnShop.md +17 -0
  39. package/packages/shopify-app-express/docs/reference/migrating-app-v6-api-lib-to-express-lib.md +345 -0
  40. package/packages/shopify-app-express/docs/reference/processWebhooks.md +67 -0
  41. package/packages/shopify-app-express/docs/reference/redirectOutOfApp.md +77 -0
  42. package/packages/shopify-app-express/docs/reference/redirectToShopifyOrAppRoot.md +20 -0
  43. package/packages/shopify-app-express/docs/reference/shopifyApp.md +148 -0
  44. package/packages/shopify-app-express/docs/reference/validateAuthenticatedSession.md +30 -0
  45. package/packages/shopify-app-express/loom.config.ts +27 -0
  46. package/packages/shopify-app-express/package.json +54 -0
  47. package/packages/shopify-app-express/src/__tests__/index.test.ts +100 -0
  48. package/packages/shopify-app-express/src/__tests__/integration/oauth.test.ts +445 -0
  49. package/packages/shopify-app-express/src/__tests__/integration/responses.ts +135 -0
  50. package/packages/shopify-app-express/src/__tests__/integration/types.ts +22 -0
  51. package/packages/shopify-app-express/src/__tests__/integration/utils.ts +68 -0
  52. package/packages/shopify-app-express/src/__tests__/integration/webhooks.test.ts +208 -0
  53. package/packages/shopify-app-express/src/__tests__/redirect-to-auth.test.ts +103 -0
  54. package/packages/shopify-app-express/src/__tests__/setup-jest.ts +8 -0
  55. package/packages/shopify-app-express/src/__tests__/test-helper.ts +204 -0
  56. package/packages/shopify-app-express/src/app-installations.ts +42 -0
  57. package/packages/shopify-app-express/src/auth/__tests__/auth.test.ts +303 -0
  58. package/packages/shopify-app-express/src/auth/auth-callback.ts +131 -0
  59. package/packages/shopify-app-express/src/auth/index.ts +32 -0
  60. package/packages/shopify-app-express/src/auth/types.ts +13 -0
  61. package/packages/shopify-app-express/src/config-types.ts +38 -0
  62. package/packages/shopify-app-express/src/error.ts +8 -0
  63. package/packages/shopify-app-express/src/index.ts +171 -0
  64. package/packages/shopify-app-express/src/middlewares/__tests__/csp-headers.test.ts +69 -0
  65. package/packages/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts +193 -0
  66. package/packages/shopify-app-express/src/middlewares/__tests__/redirect-to-shopify-or-app-root.test.ts +59 -0
  67. package/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts +319 -0
  68. package/packages/shopify-app-express/src/middlewares/csp-headers.ts +31 -0
  69. package/packages/shopify-app-express/src/middlewares/ensure-installed-on-shop.ts +176 -0
  70. package/packages/shopify-app-express/src/middlewares/has-valid-access-token.ts +25 -0
  71. package/packages/shopify-app-express/src/middlewares/index.ts +15 -0
  72. package/packages/shopify-app-express/src/middlewares/redirect-to-shopify-or-app-root.ts +39 -0
  73. package/packages/shopify-app-express/src/middlewares/types.ts +6 -0
  74. package/packages/shopify-app-express/src/middlewares/validate-authenticated-session.ts +142 -0
  75. package/packages/shopify-app-express/src/redirect-out-of-app.ts +80 -0
  76. package/packages/shopify-app-express/src/redirect-to-auth.ts +70 -0
  77. package/packages/shopify-app-express/src/types.ts +26 -0
  78. package/packages/shopify-app-express/src/version.ts +1 -0
  79. package/packages/shopify-app-express/src/webhooks/__tests__/process.test.ts +138 -0
  80. package/packages/shopify-app-express/src/webhooks/index.ts +54 -0
  81. package/packages/shopify-app-express/src/webhooks/process.ts +22 -0
  82. package/packages/shopify-app-express/src/webhooks/types.ts +24 -0
  83. package/packages/shopify-app-express/tsconfig.json +10 -0
  84. package/packages/shopify-app-remix/.eslintrc.js +3 -0
  85. package/packages/shopify-app-remix/CHANGELOG.md +569 -0
  86. package/packages/shopify-app-remix/LICENSE.md +9 -0
  87. package/packages/shopify-app-remix/README.md +223 -0
  88. package/packages/shopify-app-remix/docs/build-docs.sh +11 -0
  89. package/packages/shopify-app-remix/docs/generated/generated_docs_data.json +18650 -0
  90. package/packages/shopify-app-remix/docs/generated/generated_static_pages.json +540 -0
  91. package/packages/shopify-app-remix/docs/staticPages/admin.doc.ts +130 -0
  92. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/auth-cors.example.tsx +11 -0
  93. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/auth.example.tsx +19 -0
  94. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/graphql.example.tsx +29 -0
  95. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/headers.example.tsx +10 -0
  96. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/rest-resources.example.tsx +9 -0
  97. package/packages/shopify-app-remix/docs/staticPages/examples/guides/admin/rest.example.tsx +17 -0
  98. package/packages/shopify-app-remix/docs/staticPages/examples/guides/future-flags/config.example.ts +8 -0
  99. package/packages/shopify-app-remix/docs/staticPages/examples/guides/future-flags/unstable.example.ts +9 -0
  100. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/.graphqlrc.ts +16 -0
  101. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.npm.example.sh +2 -0
  102. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.pnpm.example.sh +2 -0
  103. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.yarn.example.sh +2 -0
  104. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/package.json +5 -0
  105. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.npm.example.sh +1 -0
  106. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.pnpm.example.sh +1 -0
  107. package/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.yarn.example.sh +1 -0
  108. package/packages/shopify-app-remix/docs/staticPages/examples/guides/webhooks/config.example.ts +19 -0
  109. package/packages/shopify-app-remix/docs/staticPages/examples/guides/webhooks/endpoint.example.ts +24 -0
  110. package/packages/shopify-app-remix/docs/staticPages/examples/index/app-provider.example.ts +30 -0
  111. package/packages/shopify-app-remix/docs/staticPages/examples/index/boundaries.example.ts +9 -0
  112. package/packages/shopify-app-remix/docs/staticPages/examples/index/create.npm.example.sh +1 -0
  113. package/packages/shopify-app-remix/docs/staticPages/examples/index/create.pnpm.example.sh +1 -0
  114. package/packages/shopify-app-remix/docs/staticPages/examples/index/create.yarn.example.sh +1 -0
  115. package/packages/shopify-app-remix/docs/staticPages/examples/index/embedded-app-auth-strategy-config.example.ts +10 -0
  116. package/packages/shopify-app-remix/docs/staticPages/examples/index/entry-server.example.ts +12 -0
  117. package/packages/shopify-app-remix/docs/staticPages/examples/index/install.npm.example.sh +1 -0
  118. package/packages/shopify-app-remix/docs/staticPages/examples/index/install.pnpm.example.sh +1 -0
  119. package/packages/shopify-app-remix/docs/staticPages/examples/index/install.yarn.example.sh +1 -0
  120. package/packages/shopify-app-remix/docs/staticPages/examples/index/shopify-app.example.ts +14 -0
  121. package/packages/shopify-app-remix/docs/staticPages/examples/index/splat-route.example.ts +11 -0
  122. package/packages/shopify-app-remix/docs/staticPages/future-flags.doc.ts +94 -0
  123. package/packages/shopify-app-remix/docs/staticPages/graphql-types.doc.ts +148 -0
  124. package/packages/shopify-app-remix/docs/staticPages/index.doc.ts +227 -0
  125. package/packages/shopify-app-remix/docs/staticPages/webhooks.doc.ts +64 -0
  126. package/packages/shopify-app-remix/docs/tsconfig.docs.json +9 -0
  127. package/packages/shopify-app-remix/docs/typeOverride.json +1 -0
  128. package/packages/shopify-app-remix/docs/upcoming_changes.md +153 -0
  129. package/packages/shopify-app-remix/loom.config.ts +57 -0
  130. package/packages/shopify-app-remix/package.json +93 -0
  131. package/packages/shopify-app-remix/src/react/.eslintrc.js +16 -0
  132. package/packages/shopify-app-remix/src/react/__tests__/test-helper.ts +22 -0
  133. package/packages/shopify-app-remix/src/react/components/AppProvider/AppProvider.doc.ts +34 -0
  134. package/packages/shopify-app-remix/src/react/components/AppProvider/AppProvider.tsx +121 -0
  135. package/packages/shopify-app-remix/src/react/components/AppProvider/__tests__/AppProvider.test.tsx +68 -0
  136. package/packages/shopify-app-remix/src/react/components/AppProvider/index.ts +1 -0
  137. package/packages/shopify-app-remix/src/react/components/RemixPolarisLink.tsx +14 -0
  138. package/packages/shopify-app-remix/src/react/components/index.ts +1 -0
  139. package/packages/shopify-app-remix/src/react/const.ts +2 -0
  140. package/packages/shopify-app-remix/src/react/index.ts +1 -0
  141. package/packages/shopify-app-remix/src/server/.eslintrc.js +16 -0
  142. package/packages/shopify-app-remix/src/server/__test-helpers/__tests__/request-mock.test.ts +171 -0
  143. package/packages/shopify-app-remix/src/server/__test-helpers/const.ts +11 -0
  144. package/packages/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts +119 -0
  145. package/packages/shopify-app-remix/src/server/__test-helpers/expect-begin-auth-redirect.ts +22 -0
  146. package/packages/shopify-app-remix/src/server/__test-helpers/expect-document-request-headers.ts +25 -0
  147. package/packages/shopify-app-remix/src/server/__test-helpers/expect-exit-iframe.ts +37 -0
  148. package/packages/shopify-app-remix/src/server/__test-helpers/expect-login-redirect.ts +8 -0
  149. package/packages/shopify-app-remix/src/server/__test-helpers/expect-storefront-api-client.ts +50 -0
  150. package/packages/shopify-app-remix/src/server/__test-helpers/get-hmac.ts +10 -0
  151. package/packages/shopify-app-remix/src/server/__test-helpers/get-jwt.ts +31 -0
  152. package/packages/shopify-app-remix/src/server/__test-helpers/get-thrown-response.ts +17 -0
  153. package/packages/shopify-app-remix/src/server/__test-helpers/index.ts +14 -0
  154. package/packages/shopify-app-remix/src/server/__test-helpers/request-mock.ts +169 -0
  155. package/packages/shopify-app-remix/src/server/__test-helpers/setup-valid-session.ts +45 -0
  156. package/packages/shopify-app-remix/src/server/__test-helpers/sign-request-cookie.ts +21 -0
  157. package/packages/shopify-app-remix/src/server/__test-helpers/test-config.ts +112 -0
  158. package/packages/shopify-app-remix/src/server/__tests__/override-logger.test.ts +101 -0
  159. package/packages/shopify-app-remix/src/server/__tests__/shopify-app.test.ts +119 -0
  160. package/packages/shopify-app-remix/src/server/adapters/__tests__/node-app-bridge-url.test.ts +18 -0
  161. package/packages/shopify-app-remix/src/server/adapters/__tests__/node.test.ts +28 -0
  162. package/packages/shopify-app-remix/src/server/adapters/node/__tests__/setup-jest.ts +10 -0
  163. package/packages/shopify-app-remix/src/server/adapters/node/index.ts +20 -0
  164. package/packages/shopify-app-remix/src/server/adapters/vercel/__tests__/setup-jest.ts +12 -0
  165. package/packages/shopify-app-remix/src/server/adapters/vercel/index.ts +13 -0
  166. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts +181 -0
  167. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/doc-request-path.test.ts +151 -0
  168. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/exit-i-frame-path.test.ts +99 -0
  169. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/patch-session-token-path.test.ts +58 -0
  170. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/reject-bot.test.ts +23 -0
  171. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/respond-to-options.test.ts +50 -0
  172. package/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts +91 -0
  173. package/packages/shopify-app-remix/src/server/authenticate/admin/authenticate.admin.doc.ts +37 -0
  174. package/packages/shopify-app-remix/src/server/authenticate/admin/authenticate.ts +201 -0
  175. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/cancel.test.ts +258 -0
  176. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/check.test.ts +254 -0
  177. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/mock-responses.ts +65 -0
  178. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/request.test.ts +373 -0
  179. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/__tests__/require.test.ts +316 -0
  180. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/authenticate.admin.billing.doc.ts +28 -0
  181. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/cancel.ts +37 -0
  182. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/check.ts +38 -0
  183. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/index.ts +4 -0
  184. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/request.ts +101 -0
  185. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/require.ts +56 -0
  186. package/packages/shopify-app-remix/src/server/authenticate/admin/billing/types.ts +383 -0
  187. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/__tests__/redirect.test.ts +313 -0
  188. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/__tests__/validate-redirect-url.test.ts +84 -0
  189. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/begin-auth.ts +17 -0
  190. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts +22 -0
  191. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.ts +18 -0
  192. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.ts +25 -0
  193. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/handle-client-error.ts +43 -0
  194. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/index.ts +14 -0
  195. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-auth-page.ts +28 -0
  196. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-bounce-page.ts +23 -0
  197. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.ts +21 -0
  198. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-with-app-bridge-headers.ts +13 -0
  199. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-with-exitiframe.ts +28 -0
  200. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect.ts +83 -0
  201. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/render-app-bridge.ts +46 -0
  202. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts +28 -0
  203. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/validate-redirect-url.ts +67 -0
  204. package/packages/shopify-app-remix/src/server/authenticate/admin/helpers/validate-shop-and-host-params.ts +28 -0
  205. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts +222 -0
  206. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-callback-path.test.ts +395 -0
  207. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-path.test.ts +83 -0
  208. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts +183 -0
  209. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/ensure-installed-on-shop.test.ts +234 -0
  210. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/session-token-header-path.test.ts +79 -0
  211. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts +189 -0
  212. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts +310 -0
  213. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts +333 -0
  214. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/token-exchange.ts +169 -0
  215. package/packages/shopify-app-remix/src/server/authenticate/admin/strategies/types.ts +29 -0
  216. package/packages/shopify-app-remix/src/server/authenticate/admin/types.ts +199 -0
  217. package/packages/shopify-app-remix/src/server/authenticate/const.ts +15 -0
  218. package/packages/shopify-app-remix/src/server/authenticate/flow/__tests__/authenticate.test.ts +142 -0
  219. package/packages/shopify-app-remix/src/server/authenticate/flow/authenticate.flow.doc.ts +34 -0
  220. package/packages/shopify-app-remix/src/server/authenticate/flow/authenticate.ts +71 -0
  221. package/packages/shopify-app-remix/src/server/authenticate/flow/types.ts +90 -0
  222. package/packages/shopify-app-remix/src/server/authenticate/helpers/__tests__/add-response-headers.test.ts +25 -0
  223. package/packages/shopify-app-remix/src/server/authenticate/helpers/__tests__/app-bridge-url.test.ts +21 -0
  224. package/packages/shopify-app-remix/src/server/authenticate/helpers/__tests__/idempotent-promise-handler.test.ts +104 -0
  225. package/packages/shopify-app-remix/src/server/authenticate/helpers/add-response-headers.ts +43 -0
  226. package/packages/shopify-app-remix/src/server/authenticate/helpers/app-bridge-url.ts +10 -0
  227. package/packages/shopify-app-remix/src/server/authenticate/helpers/ensure-cors-headers.ts +38 -0
  228. package/packages/shopify-app-remix/src/server/authenticate/helpers/get-session-token-header.ts +11 -0
  229. package/packages/shopify-app-remix/src/server/authenticate/helpers/idempotent-promise-handler.ts +45 -0
  230. package/packages/shopify-app-remix/src/server/authenticate/helpers/index.ts +8 -0
  231. package/packages/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts +13 -0
  232. package/packages/shopify-app-remix/src/server/authenticate/helpers/respond-to-invalid-session-token.ts +29 -0
  233. package/packages/shopify-app-remix/src/server/authenticate/helpers/respond-to-options-request.ts +26 -0
  234. package/packages/shopify-app-remix/src/server/authenticate/helpers/validate-session-token.ts +32 -0
  235. package/packages/shopify-app-remix/src/server/authenticate/login/__tests__/login.test.ts +157 -0
  236. package/packages/shopify-app-remix/src/server/authenticate/login/login.ts +53 -0
  237. package/packages/shopify-app-remix/src/server/authenticate/public/__tests__/factory.test.ts +224 -0
  238. package/packages/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts +282 -0
  239. package/packages/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.ts +36 -0
  240. package/packages/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.ts +90 -0
  241. package/packages/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts +164 -0
  242. package/packages/shopify-app-remix/src/server/authenticate/public/checkout/__tests__/authenticate.test.ts +142 -0
  243. package/packages/shopify-app-remix/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.ts +23 -0
  244. package/packages/shopify-app-remix/src/server/authenticate/public/checkout/authenticate.ts +45 -0
  245. package/packages/shopify-app-remix/src/server/authenticate/public/checkout/types.ts +65 -0
  246. package/packages/shopify-app-remix/src/server/authenticate/public/factory.ts +49 -0
  247. package/packages/shopify-app-remix/src/server/authenticate/public/index.ts +1 -0
  248. package/packages/shopify-app-remix/src/server/authenticate/public/types.ts +77 -0
  249. package/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/authenticate.test.ts +207 -0
  250. package/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/mock-responses.ts +45 -0
  251. package/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts +210 -0
  252. package/packages/shopify-app-remix/src/server/authenticate/webhooks/authenticate.ts +113 -0
  253. package/packages/shopify-app-remix/src/server/authenticate/webhooks/authenticate.webhooks.doc.ts +28 -0
  254. package/packages/shopify-app-remix/src/server/authenticate/webhooks/index.ts +1 -0
  255. package/packages/shopify-app-remix/src/server/authenticate/webhooks/register.ts +48 -0
  256. package/packages/shopify-app-remix/src/server/authenticate/webhooks/types.ts +246 -0
  257. package/packages/shopify-app-remix/src/server/boundary/__tests__/error.test.tsx +36 -0
  258. package/packages/shopify-app-remix/src/server/boundary/__tests__/headers.test.ts +66 -0
  259. package/packages/shopify-app-remix/src/server/boundary/error.tsx +14 -0
  260. package/packages/shopify-app-remix/src/server/boundary/headers.ts +15 -0
  261. package/packages/shopify-app-remix/src/server/boundary/index.ts +39 -0
  262. package/packages/shopify-app-remix/src/server/boundary/types.ts +4 -0
  263. package/packages/shopify-app-remix/src/server/clients/admin/authenticate.admin.api.doc.ts +34 -0
  264. package/packages/shopify-app-remix/src/server/clients/admin/factory.ts +30 -0
  265. package/packages/shopify-app-remix/src/server/clients/admin/graphql.ts +39 -0
  266. package/packages/shopify-app-remix/src/server/clients/admin/index.ts +2 -0
  267. package/packages/shopify-app-remix/src/server/clients/admin/rest.ts +157 -0
  268. package/packages/shopify-app-remix/src/server/clients/admin/types.ts +178 -0
  269. package/packages/shopify-app-remix/src/server/clients/index.ts +2 -0
  270. package/packages/shopify-app-remix/src/server/clients/storefront/authenticate.storefront.api.doc.ts +34 -0
  271. package/packages/shopify-app-remix/src/server/clients/storefront/factory.ts +32 -0
  272. package/packages/shopify-app-remix/src/server/clients/storefront/index.ts +2 -0
  273. package/packages/shopify-app-remix/src/server/clients/storefront/types.ts +31 -0
  274. package/packages/shopify-app-remix/src/server/clients/types.ts +27 -0
  275. package/packages/shopify-app-remix/src/server/config-types.ts +293 -0
  276. package/packages/shopify-app-remix/src/server/errors.ts +3 -0
  277. package/packages/shopify-app-remix/src/server/future/flags.ts +57 -0
  278. package/packages/shopify-app-remix/src/server/index.ts +20 -0
  279. package/packages/shopify-app-remix/src/server/override-logger.ts +42 -0
  280. package/packages/shopify-app-remix/src/server/shopify-app.doc.ts +45 -0
  281. package/packages/shopify-app-remix/src/server/shopify-app.ts +198 -0
  282. package/packages/shopify-app-remix/src/server/types.ts +509 -0
  283. package/packages/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts +30 -0
  284. package/packages/shopify-app-remix/src/server/unauthenticated/admin/factory.ts +29 -0
  285. package/packages/shopify-app-remix/src/server/unauthenticated/admin/index.ts +1 -0
  286. package/packages/shopify-app-remix/src/server/unauthenticated/admin/types.ts +117 -0
  287. package/packages/shopify-app-remix/src/server/unauthenticated/admin/unauthenticated.admin.doc.ts +40 -0
  288. package/packages/shopify-app-remix/src/server/unauthenticated/helpers/get-offline-session.ts +13 -0
  289. package/packages/shopify-app-remix/src/server/unauthenticated/helpers/index.ts +1 -0
  290. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/__tests__/factory.test.ts +30 -0
  291. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/factory.ts +28 -0
  292. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/index.ts +1 -0
  293. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/types.ts +39 -0
  294. package/packages/shopify-app-remix/src/server/unauthenticated/storefront/unauthenticated.storefront.doc.ts +40 -0
  295. package/packages/shopify-app-remix/src/server/unauthenticated/types.ts +68 -0
  296. package/packages/shopify-app-remix/src/server/version.ts +1 -0
  297. package/packages/shopify-app-remix/tsconfig.json +11 -0
  298. package/packages/shopify-app-session-storage/CHANGELOG.md +149 -0
  299. package/packages/shopify-app-session-storage/LICENSE.md +9 -0
  300. package/packages/shopify-app-session-storage/README.md +5 -0
  301. package/packages/shopify-app-session-storage/implementing-session-storage.md +214 -0
  302. package/packages/shopify-app-session-storage/loom.config.ts +45 -0
  303. package/packages/shopify-app-session-storage/package.json +44 -0
  304. package/packages/shopify-app-session-storage/src/__tests__/setup-jest.ts +8 -0
  305. package/packages/shopify-app-session-storage/src/abstract-migration-engine.ts +53 -0
  306. package/packages/shopify-app-session-storage/src/index.ts +3 -0
  307. package/packages/shopify-app-session-storage/src/rdbms-session-storage-migrator.ts +59 -0
  308. package/packages/shopify-app-session-storage/src/types.ts +174 -0
  309. package/packages/shopify-app-session-storage/tsconfig.json +10 -0
  310. package/packages/shopify-app-session-storage-dynamodb/CHANGELOG.md +150 -0
  311. package/packages/shopify-app-session-storage-dynamodb/LICENSE.md +9 -0
  312. package/packages/shopify-app-session-storage-dynamodb/README.md +75 -0
  313. package/packages/shopify-app-session-storage-dynamodb/loom.config.ts +27 -0
  314. package/packages/shopify-app-session-storage-dynamodb/package.json +51 -0
  315. package/packages/shopify-app-session-storage-dynamodb/src/__tests__/dynamodb.test.ts +93 -0
  316. package/packages/shopify-app-session-storage-dynamodb/src/__tests__/setup-jest.ts +8 -0
  317. package/packages/shopify-app-session-storage-dynamodb/src/dynamodb.ts +126 -0
  318. package/packages/shopify-app-session-storage-dynamodb/tsconfig.json +10 -0
  319. package/packages/shopify-app-session-storage-kv/CHANGELOG.md +204 -0
  320. package/packages/shopify-app-session-storage-kv/LICENSE.md +9 -0
  321. package/packages/shopify-app-session-storage-kv/README.md +41 -0
  322. package/packages/shopify-app-session-storage-kv/loom.config.ts +27 -0
  323. package/packages/shopify-app-session-storage-kv/package.json +52 -0
  324. package/packages/shopify-app-session-storage-kv/src/__tests__/kv-namespace-dummy-worker.ts +8 -0
  325. package/packages/shopify-app-session-storage-kv/src/__tests__/kv.test.ts +22 -0
  326. package/packages/shopify-app-session-storage-kv/src/__tests__/setup-jest.ts +8 -0
  327. package/packages/shopify-app-session-storage-kv/src/kv.ts +87 -0
  328. package/packages/shopify-app-session-storage-kv/tsconfig.json +11 -0
  329. package/packages/shopify-app-session-storage-memory/CHANGELOG.md +190 -0
  330. package/packages/shopify-app-session-storage-memory/LICENSE.md +9 -0
  331. package/packages/shopify-app-session-storage-memory/README.md +19 -0
  332. package/packages/shopify-app-session-storage-memory/loom.config.ts +27 -0
  333. package/packages/shopify-app-session-storage-memory/package.json +46 -0
  334. package/packages/shopify-app-session-storage-memory/src/__tests__/memory.test.ts +12 -0
  335. package/packages/shopify-app-session-storage-memory/src/__tests__/setup-jest.ts +8 -0
  336. package/packages/shopify-app-session-storage-memory/src/memory.ts +34 -0
  337. package/packages/shopify-app-session-storage-memory/tsconfig.json +10 -0
  338. package/packages/shopify-app-session-storage-mongodb/CHANGELOG.md +208 -0
  339. package/packages/shopify-app-session-storage-mongodb/LICENSE.md +9 -0
  340. package/packages/shopify-app-session-storage-mongodb/README.md +30 -0
  341. package/packages/shopify-app-session-storage-mongodb/loom.config.ts +27 -0
  342. package/packages/shopify-app-session-storage-mongodb/package.json +49 -0
  343. package/packages/shopify-app-session-storage-mongodb/src/__tests__/mongodb.test.ts +54 -0
  344. package/packages/shopify-app-session-storage-mongodb/src/__tests__/setup-jest.ts +8 -0
  345. package/packages/shopify-app-session-storage-mongodb/src/mongodb.ts +120 -0
  346. package/packages/shopify-app-session-storage-mongodb/tsconfig.json +10 -0
  347. package/packages/shopify-app-session-storage-mysql/CHANGELOG.md +220 -0
  348. package/packages/shopify-app-session-storage-mysql/LICENSE.md +9 -0
  349. package/packages/shopify-app-session-storage-mysql/README.md +41 -0
  350. package/packages/shopify-app-session-storage-mysql/loom.config.ts +27 -0
  351. package/packages/shopify-app-session-storage-mysql/package.json +50 -0
  352. package/packages/shopify-app-session-storage-mysql/src/__tests__/mysql.test.ts +137 -0
  353. package/packages/shopify-app-session-storage-mysql/src/__tests__/setup-jest.ts +8 -0
  354. package/packages/shopify-app-session-storage-mysql/src/migrations.ts +18 -0
  355. package/packages/shopify-app-session-storage-mysql/src/mysql-connection.ts +108 -0
  356. package/packages/shopify-app-session-storage-mysql/src/mysql-migrator.ts +53 -0
  357. package/packages/shopify-app-session-storage-mysql/src/mysql.ts +178 -0
  358. package/packages/shopify-app-session-storage-mysql/tsconfig.json +10 -0
  359. package/packages/shopify-app-session-storage-postgresql/CHANGELOG.md +210 -0
  360. package/packages/shopify-app-session-storage-postgresql/LICENSE.md +9 -0
  361. package/packages/shopify-app-session-storage-postgresql/README.md +39 -0
  362. package/packages/shopify-app-session-storage-postgresql/loom.config.ts +27 -0
  363. package/packages/shopify-app-session-storage-postgresql/package.json +52 -0
  364. package/packages/shopify-app-session-storage-postgresql/src/__tests__/migrate-to-case-sensitivity.test.ts +344 -0
  365. package/packages/shopify-app-session-storage-postgresql/src/__tests__/postgresql.test.ts +136 -0
  366. package/packages/shopify-app-session-storage-postgresql/src/__tests__/setup-jest.ts +8 -0
  367. package/packages/shopify-app-session-storage-postgresql/src/migrations.ts +70 -0
  368. package/packages/shopify-app-session-storage-postgresql/src/postgres-connection.ts +94 -0
  369. package/packages/shopify-app-session-storage-postgresql/src/postgres-migrator.ts +27 -0
  370. package/packages/shopify-app-session-storage-postgresql/src/postgresql.ts +178 -0
  371. package/packages/shopify-app-session-storage-postgresql/tsconfig.json +10 -0
  372. package/packages/shopify-app-session-storage-prisma/CHANGELOG.md +148 -0
  373. package/packages/shopify-app-session-storage-prisma/LICENSE.md +9 -0
  374. package/packages/shopify-app-session-storage-prisma/README.md +68 -0
  375. package/packages/shopify-app-session-storage-prisma/loom.config.ts +27 -0
  376. package/packages/shopify-app-session-storage-prisma/package.json +51 -0
  377. package/packages/shopify-app-session-storage-prisma/prisma/migrations/20230425184828_init/migration.sql +11 -0
  378. package/packages/shopify-app-session-storage-prisma/prisma/migrations/20230906155758_mySession/migration.sql +11 -0
  379. package/packages/shopify-app-session-storage-prisma/prisma/migrations/migration_lock.toml +3 -0
  380. package/packages/shopify-app-session-storage-prisma/prisma/schema.prisma +30 -0
  381. package/packages/shopify-app-session-storage-prisma/src/__tests__/prisma.test.ts +66 -0
  382. package/packages/shopify-app-session-storage-prisma/src/__tests__/setup-jest.ts +8 -0
  383. package/packages/shopify-app-session-storage-prisma/src/prisma.ts +145 -0
  384. package/packages/shopify-app-session-storage-prisma/tsconfig.json +10 -0
  385. package/packages/shopify-app-session-storage-redis/CHANGELOG.md +200 -0
  386. package/packages/shopify-app-session-storage-redis/LICENSE.md +9 -0
  387. package/packages/shopify-app-session-storage-redis/README.md +38 -0
  388. package/packages/shopify-app-session-storage-redis/loom.config.ts +27 -0
  389. package/packages/shopify-app-session-storage-redis/package.json +50 -0
  390. package/packages/shopify-app-session-storage-redis/src/__tests__/migration-test-data.ts +46 -0
  391. package/packages/shopify-app-session-storage-redis/src/__tests__/redis.conf +2 -0
  392. package/packages/shopify-app-session-storage-redis/src/__tests__/redis.test.ts +236 -0
  393. package/packages/shopify-app-session-storage-redis/src/__tests__/setup-jest.ts +8 -0
  394. package/packages/shopify-app-session-storage-redis/src/migrations.ts +36 -0
  395. package/packages/shopify-app-session-storage-redis/src/redis-connection.ts +64 -0
  396. package/packages/shopify-app-session-storage-redis/src/redis-migrator.ts +58 -0
  397. package/packages/shopify-app-session-storage-redis/src/redis.ts +167 -0
  398. package/packages/shopify-app-session-storage-redis/tsconfig.json +10 -0
  399. package/packages/shopify-app-session-storage-sqlite/CHANGELOG.md +202 -0
  400. package/packages/shopify-app-session-storage-sqlite/LICENSE.md +9 -0
  401. package/packages/shopify-app-session-storage-sqlite/README.md +25 -0
  402. package/packages/shopify-app-session-storage-sqlite/loom.config.ts +27 -0
  403. package/packages/shopify-app-session-storage-sqlite/package.json +51 -0
  404. package/packages/shopify-app-session-storage-sqlite/src/__tests__/setup-jest.ts +8 -0
  405. package/packages/shopify-app-session-storage-sqlite/src/__tests__/sqlite.test.ts +44 -0
  406. package/packages/shopify-app-session-storage-sqlite/src/migrations.ts +54 -0
  407. package/packages/shopify-app-session-storage-sqlite/src/sqlite-connection.ts +80 -0
  408. package/packages/shopify-app-session-storage-sqlite/src/sqlite-migrator.ts +34 -0
  409. package/packages/shopify-app-session-storage-sqlite/src/sqlite.ts +147 -0
  410. package/packages/shopify-app-session-storage-sqlite/tsconfig.json +10 -0
  411. package/packages/shopify-app-session-storage-test-utils/CHANGELOG.md +185 -0
  412. package/packages/shopify-app-session-storage-test-utils/LICENSE.md +9 -0
  413. package/packages/shopify-app-session-storage-test-utils/loom.config.ts +27 -0
  414. package/packages/shopify-app-session-storage-test-utils/package.json +48 -0
  415. package/packages/shopify-app-session-storage-test-utils/src/__tests__/session-test-utils.test.ts +273 -0
  416. package/packages/shopify-app-session-storage-test-utils/src/battery-of-tests.ts +250 -0
  417. package/packages/shopify-app-session-storage-test-utils/src/index.ts +2 -0
  418. package/packages/shopify-app-session-storage-test-utils/src/session-test-utils.ts +24 -0
  419. package/packages/shopify-app-session-storage-test-utils/src/utils.ts +71 -0
  420. package/packages/shopify-app-session-storage-test-utils/tsconfig.json +10 -0
  421. package/tests/setup/build.js +1 -0
  422. package/tests/setup/setup-jest.ts +8 -0
  423. package/tsconfig.base.json +41 -0
  424. package/tsconfig.json +19 -0
@@ -0,0 +1,373 @@
1
+ import {
2
+ BillingConfigSubscriptionLineItemPlan,
3
+ BillingError,
4
+ BillingInterval,
5
+ HttpResponseError,
6
+ SESSION_COOKIE_NAME,
7
+ } from '@shopify/shopify-api';
8
+
9
+ import {shopifyApp} from '../../../..';
10
+ import {
11
+ APP_URL,
12
+ BASE64_HOST,
13
+ GRAPHQL_URL,
14
+ TEST_SHOP,
15
+ expectBeginAuthRedirect,
16
+ expectExitIframeRedirect,
17
+ getJwt,
18
+ getThrownResponse,
19
+ setUpValidSession,
20
+ signRequestCookie,
21
+ testConfig,
22
+ mockExternalRequest,
23
+ mockExternalRequests,
24
+ } from '../../../../__test-helpers';
25
+ import {REAUTH_URL_HEADER} from '../../../const';
26
+
27
+ import * as responses from './mock-responses';
28
+
29
+ const BILLING_CONFIG = {
30
+ [responses.PLAN_1]: {
31
+ lineItems: [
32
+ {
33
+ amount: 5,
34
+ currencyCode: 'USD',
35
+ interval: BillingInterval.Every30Days,
36
+ },
37
+ ],
38
+ } as BillingConfigSubscriptionLineItemPlan,
39
+ };
40
+
41
+ describe('Billing request', () => {
42
+ it('redirects to payment confirmation URL when successful and at the top level for non-embedded apps', async () => {
43
+ // GIVEN
44
+ const shopify = shopifyApp(
45
+ testConfig({isEmbeddedApp: false, billing: BILLING_CONFIG}),
46
+ );
47
+ const session = await setUpValidSession(shopify.sessionStorage);
48
+
49
+ await mockExternalRequests({
50
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
51
+ response: new Response(responses.PURCHASE_SUBSCRIPTION_RESPONSE),
52
+ });
53
+
54
+ const request = new Request(`${APP_URL}/billing?shop=${TEST_SHOP}`);
55
+ signRequestCookie({
56
+ request,
57
+ cookieName: SESSION_COOKIE_NAME,
58
+ cookieValue: session.id,
59
+ });
60
+
61
+ const {billing} = await shopify.authenticate.admin(request);
62
+
63
+ // WHEN
64
+ const response = await getThrownResponse(
65
+ async () => billing.request({plan: responses.PLAN_1, isTest: true}),
66
+ request,
67
+ );
68
+
69
+ // THEN
70
+ expect(response.status).toEqual(302);
71
+ expect(response.headers.get('Location')).toEqual(
72
+ responses.CONFIRMATION_URL,
73
+ );
74
+ });
75
+
76
+ it('redirects to payment confirmation URL when successful and at the top level for non-embedded apps', async () => {
77
+ // GIVEN
78
+ const shopify = shopifyApp(
79
+ testConfig({isEmbeddedApp: false, billing: BILLING_CONFIG}),
80
+ );
81
+ const session = await setUpValidSession(shopify.sessionStorage);
82
+
83
+ await mockExternalRequests({
84
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
85
+ response: new Response(responses.PURCHASE_SUBSCRIPTION_RESPONSE),
86
+ });
87
+
88
+ const request = new Request(`${APP_URL}/billing?shop=${TEST_SHOP}`);
89
+ signRequestCookie({
90
+ request,
91
+ cookieName: SESSION_COOKIE_NAME,
92
+ cookieValue: session.id,
93
+ });
94
+
95
+ const {billing} = await shopify.authenticate.admin(request);
96
+
97
+ // WHEN
98
+ const response = await getThrownResponse(
99
+ async () => billing.request({plan: responses.PLAN_1, isTest: true}),
100
+ request,
101
+ );
102
+
103
+ // THEN
104
+ expect(response.status).toEqual(302);
105
+ expect(response.headers.get('Location')).toEqual(
106
+ responses.CONFIRMATION_URL,
107
+ );
108
+ });
109
+
110
+ it('redirects to exit-iframe with payment confirmation URL when successful using app bridge when embedded', async () => {
111
+ // GIVEN
112
+ const shopify = shopifyApp(testConfig({billing: BILLING_CONFIG}));
113
+ await setUpValidSession(shopify.sessionStorage);
114
+
115
+ await mockExternalRequest({
116
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
117
+ response: new Response(responses.PURCHASE_SUBSCRIPTION_RESPONSE),
118
+ });
119
+
120
+ const {token} = getJwt();
121
+ const request = new Request(
122
+ `${APP_URL}/billing?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`,
123
+ );
124
+
125
+ const {billing} = await shopify.authenticate.admin(request);
126
+
127
+ // WHEN
128
+ const response = await getThrownResponse(
129
+ async () => billing.request({plan: responses.PLAN_1, isTest: true}),
130
+ request,
131
+ );
132
+
133
+ // THEN
134
+ expectExitIframeRedirect(response, {
135
+ destination: responses.CONFIRMATION_URL,
136
+ addHostToExitIframePath: false,
137
+ });
138
+ });
139
+
140
+ it('returns redirection headers when successful during fetch requests', async () => {
141
+ // GIVEN
142
+ const shopify = shopifyApp(testConfig({billing: BILLING_CONFIG}));
143
+ await setUpValidSession(shopify.sessionStorage);
144
+
145
+ await mockExternalRequest({
146
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
147
+ response: new Response(responses.PURCHASE_SUBSCRIPTION_RESPONSE),
148
+ });
149
+
150
+ const request = new Request(`${APP_URL}/billing`, {
151
+ headers: {
152
+ Authorization: `Bearer ${getJwt().token}`,
153
+ },
154
+ });
155
+
156
+ const {billing} = await shopify.authenticate.admin(request);
157
+
158
+ // WHEN
159
+ const response = await getThrownResponse(
160
+ async () => billing.request({plan: responses.PLAN_1, isTest: true}),
161
+ request,
162
+ );
163
+
164
+ // THEN
165
+ expect(response.status).toEqual(401);
166
+ expect(response.headers.get(REAUTH_URL_HEADER)).toEqual(
167
+ responses.CONFIRMATION_URL,
168
+ );
169
+ });
170
+
171
+ it('redirects to authentication when at the top level when Shopify invalidated the session', async () => {
172
+ // GIVEN
173
+ const config = testConfig();
174
+ const shopify = shopifyApp(
175
+ testConfig({isEmbeddedApp: false, billing: BILLING_CONFIG}),
176
+ );
177
+ const session = await setUpValidSession(shopify.sessionStorage);
178
+
179
+ await mockExternalRequests({
180
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
181
+ response: new Response(undefined, {
182
+ status: 401,
183
+ statusText: 'Unauthorized',
184
+ }),
185
+ });
186
+
187
+ const request = new Request(`${APP_URL}/billing?shop=${TEST_SHOP}`);
188
+ signRequestCookie({
189
+ request,
190
+ cookieName: SESSION_COOKIE_NAME,
191
+ cookieValue: session.id,
192
+ });
193
+
194
+ const {billing} = await shopify.authenticate.admin(request);
195
+
196
+ // WHEN
197
+ const response = await getThrownResponse(
198
+ async () => billing.request({plan: responses.PLAN_1, isTest: true}),
199
+ request,
200
+ );
201
+
202
+ // THEN
203
+ expectBeginAuthRedirect(config, response);
204
+ });
205
+
206
+ it('redirects to exit-iframe with authentication using app bridge when embedded and Shopify invalidated the session', async () => {
207
+ // GIVEN
208
+ const config = testConfig();
209
+ const shopify = shopifyApp({...config, billing: BILLING_CONFIG});
210
+ await setUpValidSession(shopify.sessionStorage);
211
+
212
+ await mockExternalRequest({
213
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
214
+ response: new Response(undefined, {
215
+ status: 401,
216
+ statusText: 'Unauthorized',
217
+ }),
218
+ });
219
+
220
+ const {token} = getJwt();
221
+ const request = new Request(
222
+ `${APP_URL}/billing?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`,
223
+ );
224
+
225
+ const {billing} = await shopify.authenticate.admin(request);
226
+
227
+ // WHEN
228
+ const response = await getThrownResponse(
229
+ async () => billing.request({plan: responses.PLAN_1, isTest: true}),
230
+ request,
231
+ );
232
+
233
+ // THEN
234
+ const shopSessions =
235
+ await config.sessionStorage.findSessionsByShop(TEST_SHOP);
236
+ expect(shopSessions).toStrictEqual([]);
237
+ expectExitIframeRedirect(response);
238
+ });
239
+
240
+ it('returns redirection headers during fetch requests when Shopify invalidated the session', async () => {
241
+ // GIVEN
242
+ const shopify = shopifyApp(testConfig({billing: BILLING_CONFIG}));
243
+ await setUpValidSession(shopify.sessionStorage);
244
+
245
+ await mockExternalRequest({
246
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
247
+ response: new Response(undefined, {
248
+ status: 401,
249
+ statusText: 'Unauthorized',
250
+ }),
251
+ });
252
+
253
+ const request = new Request(`${APP_URL}/billing`, {
254
+ headers: {
255
+ Authorization: `Bearer ${getJwt().token}`,
256
+ },
257
+ });
258
+
259
+ const {billing} = await shopify.authenticate.admin(request);
260
+
261
+ // WHEN
262
+ const response = await getThrownResponse(
263
+ async () => billing.request({plan: responses.PLAN_1, isTest: true}),
264
+ request,
265
+ );
266
+
267
+ // THEN
268
+ expect(response.status).toEqual(401);
269
+
270
+ const reauthUrl = new URL(response.headers.get(REAUTH_URL_HEADER)!);
271
+ expect(reauthUrl.origin).toEqual(APP_URL);
272
+ expect(reauthUrl.pathname).toEqual('/auth');
273
+ });
274
+
275
+ it('throws errors other than authentication errors', async () => {
276
+ // GIVEN
277
+ const shopify = shopifyApp(
278
+ testConfig({isEmbeddedApp: false, billing: BILLING_CONFIG}),
279
+ );
280
+ const session = await setUpValidSession(shopify.sessionStorage);
281
+
282
+ await mockExternalRequests({
283
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
284
+ response: new Response(undefined, {
285
+ status: 500,
286
+ statusText: 'Internal Server Error',
287
+ }),
288
+ });
289
+
290
+ const request = new Request(`${APP_URL}/billing?shop=${TEST_SHOP}`);
291
+ signRequestCookie({
292
+ request,
293
+ cookieName: SESSION_COOKIE_NAME,
294
+ cookieValue: session.id,
295
+ });
296
+
297
+ const {billing} = await shopify.authenticate.admin(request);
298
+
299
+ // THEN
300
+ await expect(
301
+ billing.request({plan: responses.PLAN_1, isTest: true}),
302
+ ).rejects.toThrowError(HttpResponseError);
303
+ });
304
+
305
+ it('throws a BillingError when the response contains user errors', async () => {
306
+ // GIVEN
307
+ const shopify = shopifyApp(testConfig({billing: BILLING_CONFIG}));
308
+ await setUpValidSession(shopify.sessionStorage);
309
+
310
+ await mockExternalRequest({
311
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
312
+ response: new Response(
313
+ responses.PURCHASE_SUBSCRIPTION_RESPONSE_WITH_USER_ERRORS,
314
+ ),
315
+ });
316
+
317
+ const {token} = getJwt();
318
+ const {billing} = await shopify.authenticate.admin(
319
+ new Request(
320
+ `${APP_URL}/billing?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`,
321
+ ),
322
+ );
323
+
324
+ // THEN
325
+ await expect(
326
+ billing.request({plan: responses.PLAN_1, isTest: true}),
327
+ ).rejects.toThrowError(BillingError);
328
+ });
329
+
330
+ it('redirects to payment confirmation URL when successful and at the top level for non-embedded when v3_lineItemBilling is not enabled ', async () => {
331
+ // GIVEN
332
+ const shopify = shopifyApp(
333
+ testConfig({
334
+ isEmbeddedApp: false,
335
+ billing: {
336
+ [responses.PLAN_1]: {
337
+ amount: 5,
338
+ currencyCode: 'USD',
339
+ interval: BillingInterval.Every30Days,
340
+ },
341
+ },
342
+ future: {v3_lineItemBilling: false},
343
+ }),
344
+ );
345
+ const session = await setUpValidSession(shopify.sessionStorage);
346
+
347
+ await mockExternalRequests({
348
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
349
+ response: new Response(responses.PURCHASE_SUBSCRIPTION_RESPONSE),
350
+ });
351
+
352
+ const request = new Request(`${APP_URL}/billing?shop=${TEST_SHOP}`);
353
+ signRequestCookie({
354
+ request,
355
+ cookieName: SESSION_COOKIE_NAME,
356
+ cookieValue: session.id,
357
+ });
358
+
359
+ const {billing} = await shopify.authenticate.admin(request);
360
+
361
+ // WHEN
362
+ const response = await getThrownResponse(
363
+ async () => billing.request({plan: responses.PLAN_1, isTest: true}),
364
+ request,
365
+ );
366
+
367
+ // THEN
368
+ expect(response.status).toEqual(302);
369
+ expect(response.headers.get('Location')).toEqual(
370
+ responses.CONFIRMATION_URL,
371
+ );
372
+ });
373
+ });
@@ -0,0 +1,316 @@
1
+ import {
2
+ BillingConfigSubscriptionLineItemPlan,
3
+ BillingInterval,
4
+ HttpResponseError,
5
+ SESSION_COOKIE_NAME,
6
+ Shopify,
7
+ } from '@shopify/shopify-api';
8
+
9
+ import {shopifyApp} from '../../../..';
10
+ import {
11
+ APP_URL,
12
+ BASE64_HOST,
13
+ GRAPHQL_URL,
14
+ TEST_SHOP,
15
+ expectBeginAuthRedirect,
16
+ expectExitIframeRedirect,
17
+ getJwt,
18
+ getThrownResponse,
19
+ setUpValidSession,
20
+ signRequestCookie,
21
+ testConfig,
22
+ mockExternalRequest,
23
+ } from '../../../../__test-helpers';
24
+ import {REAUTH_URL_HEADER} from '../../../const';
25
+
26
+ import * as responses from './mock-responses';
27
+
28
+ const BILLING_CONFIG = {
29
+ [responses.PLAN_1]: {
30
+ lineItems: [
31
+ {
32
+ amount: 5,
33
+ currencyCode: 'USD',
34
+ interval: BillingInterval.Every30Days,
35
+ },
36
+ ],
37
+ } as BillingConfigSubscriptionLineItemPlan,
38
+ };
39
+
40
+ describe('Billing require', () => {
41
+ it('throws the returned response from onFailure when there is no payment', async () => {
42
+ // GIVEN
43
+ const config = testConfig();
44
+ await setUpValidSession(config.sessionStorage);
45
+ const shopify = shopifyApp({...config, billing: BILLING_CONFIG});
46
+
47
+ await mockExternalRequest({
48
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
49
+ response: new Response(responses.EMPTY_SUBSCRIPTIONS),
50
+ });
51
+
52
+ const {billing} = await shopify.authenticate.admin(
53
+ new Request(`${APP_URL}/billing`, {
54
+ headers: {
55
+ Authorization: `Bearer ${getJwt().token}`,
56
+ },
57
+ }),
58
+ );
59
+
60
+ // WHEN
61
+ try {
62
+ await billing.require({
63
+ plans: [responses.PLAN_1],
64
+ onFailure: async (error) => {
65
+ expect(error.message).toBe('Billing check failed');
66
+ return new Response('Test response', {status: 402});
67
+ },
68
+ });
69
+ } catch (response) {
70
+ expect(response.status).toBe(402);
71
+ expect(await response.text()).toBe('Test response');
72
+ }
73
+ });
74
+
75
+ it('returns true when there is payment', async () => {
76
+ // GIVEN
77
+ const config = testConfig();
78
+ await setUpValidSession(config.sessionStorage);
79
+ const shopify = shopifyApp({...config, billing: BILLING_CONFIG});
80
+
81
+ await mockExternalRequest({
82
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
83
+ response: new Response(responses.EXISTING_SUBSCRIPTION),
84
+ });
85
+
86
+ const {billing} = await shopify.authenticate.admin(
87
+ new Request(`${APP_URL}/billing`, {
88
+ headers: {
89
+ Authorization: `Bearer ${getJwt().token}`,
90
+ },
91
+ }),
92
+ );
93
+
94
+ // WHEN
95
+ const result = await billing.require({
96
+ plans: [responses.PLAN_1],
97
+ onFailure: async () => {
98
+ throw new Error('This should not be called');
99
+ },
100
+ });
101
+
102
+ // THEN
103
+ expect(result.hasActivePayment).toBe(true);
104
+ expect(result.oneTimePurchases).toEqual([]);
105
+ expect(result.appSubscriptions).toEqual([
106
+ {id: 'gid://123', name: responses.PLAN_1, test: true},
107
+ ]);
108
+ });
109
+
110
+ it('returns true when there is payment and v3_lineItemBilling is not enabled', async () => {
111
+ // GIVEN
112
+ const config = testConfig();
113
+ await setUpValidSession(config.sessionStorage);
114
+ const shopify = shopifyApp({
115
+ ...config,
116
+ billing: {
117
+ [responses.PLAN_1]: {
118
+ amount: 5,
119
+ currencyCode: 'USD',
120
+ interval: BillingInterval.Every30Days,
121
+ },
122
+ },
123
+ future: {v3_lineItemBilling: false},
124
+ });
125
+
126
+ await mockExternalRequest({
127
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
128
+ response: new Response(responses.EXISTING_SUBSCRIPTION),
129
+ });
130
+
131
+ const {billing} = await shopify.authenticate.admin(
132
+ new Request(`${APP_URL}/billing`, {
133
+ headers: {
134
+ Authorization: `Bearer ${getJwt().token}`,
135
+ },
136
+ }),
137
+ );
138
+
139
+ // WHEN
140
+ const result = await billing.require({
141
+ plans: [responses.PLAN_1],
142
+ onFailure: async () => {
143
+ throw new Error('This should not be called');
144
+ },
145
+ });
146
+
147
+ // THEN
148
+ expect(result.hasActivePayment).toBe(true);
149
+ expect(result.oneTimePurchases).toEqual([]);
150
+ expect(result.appSubscriptions).toEqual([
151
+ {id: 'gid://123', name: responses.PLAN_1, test: true},
152
+ ]);
153
+ });
154
+
155
+ it('redirects to authentication when at the top level when Shopify invalidated the session', async () => {
156
+ // GIVEN
157
+ const config = testConfig({
158
+ isEmbeddedApp: false,
159
+ billing: BILLING_CONFIG,
160
+ });
161
+ const session = await setUpValidSession(config.sessionStorage);
162
+ const shopify = shopifyApp(config);
163
+
164
+ await mockExternalRequest({
165
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
166
+ response: new Response(undefined, {
167
+ status: 401,
168
+ statusText: 'Unauthorized',
169
+ }),
170
+ });
171
+
172
+ const request = new Request(`${APP_URL}/billing?shop=${TEST_SHOP}`);
173
+ signRequestCookie({
174
+ request,
175
+ cookieName: SESSION_COOKIE_NAME,
176
+ cookieValue: session.id,
177
+ });
178
+
179
+ const {billing} = await shopify.authenticate.admin(request);
180
+
181
+ // WHEN
182
+ const response = await getThrownResponse(
183
+ async () =>
184
+ billing.require({
185
+ plans: [responses.PLAN_1 as unknown as never],
186
+ onFailure: async () => {
187
+ throw new Error('This should not be called');
188
+ },
189
+ }),
190
+ request,
191
+ );
192
+
193
+ // THEN
194
+ expectBeginAuthRedirect(config, response);
195
+ });
196
+
197
+ it('redirects to exit-iframe with authentication using app bridge when embedded and Shopify invalidated the session', async () => {
198
+ // GIVEN
199
+ const config = testConfig();
200
+ await setUpValidSession(config.sessionStorage);
201
+ const shopify = shopifyApp({...config, billing: BILLING_CONFIG});
202
+
203
+ await mockExternalRequest({
204
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
205
+ response: new Response(undefined, {
206
+ status: 401,
207
+ statusText: 'Unauthorized',
208
+ }),
209
+ });
210
+
211
+ const {token} = getJwt();
212
+ const request = new Request(
213
+ `${APP_URL}/billing?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`,
214
+ );
215
+
216
+ const {billing} = await shopify.authenticate.admin(request);
217
+
218
+ // WHEN
219
+ const response = await getThrownResponse(
220
+ async () =>
221
+ billing.require({
222
+ plans: [responses.PLAN_1],
223
+ onFailure: async () => {
224
+ throw new Error('This should not be called');
225
+ },
226
+ }),
227
+ request,
228
+ );
229
+
230
+ // THEN
231
+ const shopSessions =
232
+ await config.sessionStorage.findSessionsByShop(TEST_SHOP);
233
+ expect(shopSessions).toStrictEqual([]);
234
+ expectExitIframeRedirect(response);
235
+ });
236
+
237
+ it('returns redirection headers during fetch requests when Shopify invalidated the session', async () => {
238
+ // GIVEN
239
+ const config = testConfig();
240
+ await setUpValidSession(config.sessionStorage);
241
+ const shopify = shopifyApp({...config, billing: BILLING_CONFIG});
242
+
243
+ await mockExternalRequest({
244
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
245
+ response: new Response(undefined, {
246
+ status: 401,
247
+ statusText: 'Unauthorized',
248
+ }),
249
+ });
250
+
251
+ const request = new Request(`${APP_URL}/billing`, {
252
+ headers: {
253
+ Authorization: `Bearer ${getJwt().token}`,
254
+ },
255
+ });
256
+
257
+ const {billing} = await shopify.authenticate.admin(request);
258
+
259
+ // WHEN
260
+ const response = await getThrownResponse(
261
+ async () =>
262
+ billing.require({
263
+ plans: [responses.PLAN_1],
264
+ onFailure: async () => {
265
+ throw new Error('This should not be called');
266
+ },
267
+ }),
268
+ request,
269
+ );
270
+
271
+ // THEN
272
+ const reauthUrl = new URL(response.headers.get(REAUTH_URL_HEADER)!);
273
+
274
+ expect(response.status).toEqual(401);
275
+ expect(reauthUrl.origin).toEqual(APP_URL);
276
+ expect(reauthUrl.pathname).toEqual('/auth');
277
+ });
278
+
279
+ it('throws errors other than authentication errors', async () => {
280
+ // GIVEN
281
+ const config = testConfig();
282
+ const session = await setUpValidSession(config.sessionStorage);
283
+ const shopify = shopifyApp({
284
+ ...config,
285
+ isEmbeddedApp: false,
286
+ billing: BILLING_CONFIG,
287
+ });
288
+
289
+ await mockExternalRequest({
290
+ request: new Request(GRAPHQL_URL, {method: 'POST', body: 'test'}),
291
+ response: new Response(undefined, {
292
+ status: 500,
293
+ statusText: 'Internal Server Error',
294
+ }),
295
+ });
296
+
297
+ const request = new Request(`${APP_URL}/billing?shop=${TEST_SHOP}`);
298
+ signRequestCookie({
299
+ request,
300
+ cookieName: SESSION_COOKIE_NAME,
301
+ cookieValue: session.id,
302
+ });
303
+
304
+ const {billing} = await shopify.authenticate.admin(request);
305
+
306
+ // THEN
307
+ await expect(
308
+ billing.require({
309
+ plans: [responses.PLAN_1],
310
+ onFailure: async () => {
311
+ throw new Error('This should not be called');
312
+ },
313
+ }),
314
+ ).rejects.toThrowError(HttpResponseError);
315
+ });
316
+ });
@@ -0,0 +1,28 @@
1
+ import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs';
2
+
3
+ const data: ReferenceEntityTemplateSchema = {
4
+ name: 'Billing',
5
+ description:
6
+ 'Contains function used to bill merchants for your app.\n\nThis object is returned on authenticated Admin requests.',
7
+ category: 'APIs',
8
+ type: 'object',
9
+ isVisualComponent: false,
10
+ definitions: [
11
+ {
12
+ title: 'billing',
13
+ description:
14
+ 'Provides utilities that apps can use to request billing for the app using the Admin API.',
15
+ type: 'BillingContext',
16
+ },
17
+ ],
18
+ jsDocTypeExamples: ['BillingContext'],
19
+ related: [
20
+ {
21
+ name: 'Admin context',
22
+ subtitle: 'Authenticate requests from Shopify Admin.',
23
+ url: '/docs/api/shopify-app-remix/authenticate/admin',
24
+ },
25
+ ],
26
+ };
27
+
28
+ export default data;