diff --git a/packages/magento-payment-afterpay/README.md b/packages/magento-payment-afterpay/README.md
new file mode 100644
index 0000000000..65b9796489
--- /dev/null
+++ b/packages/magento-payment-afterpay/README.md
@@ -0,0 +1,16 @@
+# Magento Payment Afterpay
+
+Integrates GraphCommerce with the Magento Afterpay module.
+
+It requires [Afterpay](https://github.com/afterpay/afterpay-magento-2) module to
+be installed before using this package.
+
+## Installation
+
+1. Find current version of your `@graphcommerce/magento-cart-payment-method` in
+ your package.json.
+2. `yarn add @graphcommerce/magento-payment-afterpay@1.2.3` (replace 1.2.3 with
+ the version of the step above)
+
+This package uses GraphCommerce plugin systems, so there is no code modification
+required.
diff --git a/packages/magento-payment-afterpay/components/AfterpayPaymentActionCard/AfterpayPaymentActionCard.tsx b/packages/magento-payment-afterpay/components/AfterpayPaymentActionCard/AfterpayPaymentActionCard.tsx
new file mode 100644
index 0000000000..6e2a72c23f
--- /dev/null
+++ b/packages/magento-payment-afterpay/components/AfterpayPaymentActionCard/AfterpayPaymentActionCard.tsx
@@ -0,0 +1,22 @@
+import { Image } from '@graphcommerce/image'
+import { PaymentMethodActionCardProps } from '@graphcommerce/magento-cart-payment-method'
+import { ActionCard, useIconSvgSize } from '@graphcommerce/next-ui'
+import afterpayMark from '../../icons/afterpay.png'
+
+export function AfterpayPaymentActionCard(props: PaymentMethodActionCardProps) {
+ const iconSize = useIconSvgSize('large')
+
+ return (
+
+ }
+ />
+ )
+}
diff --git a/packages/magento-payment-afterpay/components/AfterpayPaymentHandler/AfterpayPaymentHandler.graphql b/packages/magento-payment-afterpay/components/AfterpayPaymentHandler/AfterpayPaymentHandler.graphql
new file mode 100644
index 0000000000..03a95a9dd8
--- /dev/null
+++ b/packages/magento-payment-afterpay/components/AfterpayPaymentHandler/AfterpayPaymentHandler.graphql
@@ -0,0 +1,12 @@
+mutation AfterpayPaymentHandler($cartId: String!, $paymentMethod: PaymentMethodInput!) {
+ setPaymentMethodOnCart(input: { cart_id: $cartId, payment_method: $paymentMethod }) {
+ cart {
+ ...PaymentMethodUpdated
+ }
+ }
+ placeOrder(input: { cart_id: $cartId }) {
+ order {
+ order_number
+ }
+ }
+}
diff --git a/packages/magento-payment-afterpay/components/AfterpayPaymentHandler/AfterpayPaymentHandler.tsx b/packages/magento-payment-afterpay/components/AfterpayPaymentHandler/AfterpayPaymentHandler.tsx
new file mode 100644
index 0000000000..68c555406a
--- /dev/null
+++ b/packages/magento-payment-afterpay/components/AfterpayPaymentHandler/AfterpayPaymentHandler.tsx
@@ -0,0 +1,62 @@
+import { ApolloErrorSnackbar } from '@graphcommerce/ecommerce-ui'
+import { useMutation } from '@graphcommerce/graphql'
+import { useCurrentCartId } from '@graphcommerce/magento-cart'
+import {
+ PaymentHandlerProps,
+ usePaymentMethodContext,
+} from '@graphcommerce/magento-cart-payment-method'
+import { useRouter } from 'next/router'
+import { useEffect } from 'react'
+import { useAfterpayCartLock } from '../../hooks/useAfterpayCartLock'
+import { AfterpayPaymentHandlerDocument } from './AfterpayPaymentHandler.gql'
+
+export const AfterpayPaymentHandler = (props: PaymentHandlerProps) => {
+ const { code } = props
+ const { push } = useRouter()
+ const [lockStatus, , unlock] = useAfterpayCartLock()
+ const { onSuccess } = usePaymentMethodContext()
+ const { currentCartId: cartId } = useCurrentCartId()
+
+ const { orderToken, status, locked, justLocked, method } = lockStatus
+ const [placeOrder, { error, called }] = useMutation(AfterpayPaymentHandlerDocument, {
+ variables: {
+ cartId,
+ paymentMethod: {
+ code,
+ afterpay: {
+ afterpay_token: orderToken ?? '',
+ },
+ },
+ },
+ errorPolicy: 'all',
+ })
+
+ useEffect(() => {
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
+ if (locked && !justLocked && status === 'CANCELLED') unlock({ orderToken: null, status: null })
+ }, [status, code, justLocked, locked, method, unlock])
+
+ // If successfull we clear it's cart and redirect to the success page.
+ useEffect(() => {
+ if (!orderToken || status !== 'SUCCESS' || called) return
+ if (!cartId) return
+
+ const fetchData = async () => {
+ const res = await placeOrder()
+
+ if (res.errors || !res.data?.placeOrder?.order) {
+ await unlock({ orderToken: null, status: null })
+ return
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
+ onSuccess(res.data?.placeOrder?.order.order_number)
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
+ fetchData()
+ }, [status, called, cartId, onSuccess, placeOrder, push, orderToken, unlock])
+
+ if (error) return
+ return null
+}
diff --git a/packages/magento-payment-afterpay/components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder.graphql b/packages/magento-payment-afterpay/components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder.graphql
new file mode 100644
index 0000000000..6071685fe8
--- /dev/null
+++ b/packages/magento-payment-afterpay/components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder.graphql
@@ -0,0 +1,6 @@
+mutation AfterpayPaymentPlaceOrder($cartId: String!, $redirect_path: AfterpayRedirectPathInput!) {
+ createAfterpayCheckout(input: { cart_id: $cartId, redirect_path: $redirect_path }) {
+ afterpay_redirectCheckoutUrl
+ afterpay_token
+ }
+}
diff --git a/packages/magento-payment-afterpay/components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder.tsx b/packages/magento-payment-afterpay/components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder.tsx
new file mode 100644
index 0000000000..029a654c88
--- /dev/null
+++ b/packages/magento-payment-afterpay/components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder.tsx
@@ -0,0 +1,44 @@
+import { useFormCompose } from '@graphcommerce/ecommerce-ui'
+import { useFormGqlMutationCart } from '@graphcommerce/magento-cart'
+import { PaymentPlaceOrderProps } from '@graphcommerce/magento-cart-payment-method'
+import { useRouter } from 'next/router'
+import { useAfterpayCartLock } from '../../hooks/useAfterpayCartLock'
+import { AfterpayPaymentPlaceOrderDocument } from './AfterpayPaymentPlaceOrder.gql'
+
+export function AfterpayPaymentPlaceOrder(props: PaymentPlaceOrderProps) {
+ const { code, step } = props
+ const [, lock] = useAfterpayCartLock()
+ const { push } = useRouter()
+
+ const form = useFormGqlMutationCart(AfterpayPaymentPlaceOrderDocument, {
+ onBeforeSubmit: (variables) => ({
+ ...variables,
+ redirect_path: { cancel_path: `checkout/payment`, confirm_path: `checkout/payment` },
+ }),
+ onComplete: async (result) => {
+ if (result.errors) return
+
+ const start = result.data?.createAfterpayCheckout?.afterpay_redirectCheckoutUrl
+ const orderToken = result.data?.createAfterpayCheckout?.afterpay_token
+
+ if (!start)
+ throw Error(
+ 'Error while starting the Afterpay payment, please try again with a different payment method',
+ )
+
+ await lock({ orderToken, method: code })
+ // We are going to redirect, but we're not waiting, because we need to complete the submission to release the buttons
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
+ push(start)
+ },
+ })
+
+ const submit = form.handleSubmit(() => {})
+ useFormCompose({ form, step, submit, key: `AfterpayPaymentPlaceOrder_${code}` })
+
+ return (
+
+ )
+}
diff --git a/packages/magento-payment-afterpay/hooks/useAfterpayCartLock.ts b/packages/magento-payment-afterpay/hooks/useAfterpayCartLock.ts
new file mode 100644
index 0000000000..adf0ea4c91
--- /dev/null
+++ b/packages/magento-payment-afterpay/hooks/useAfterpayCartLock.ts
@@ -0,0 +1,15 @@
+import { CartLockState, useCartLock } from '@graphcommerce/magento-cart-payment-method'
+
+type AfterpayLockState = CartLockState & {
+ orderToken?: string | null
+ status?: 'SUCCESS' | 'CANCELLED' | null
+}
+
+/**
+ * The cart lock situation is a bit odd since are unable to actually influence the return URL we
+ * can't safely remember the cart ID.
+ *
+ * This is a potential bug because when the customer is returning from an icognito session, the cart
+ * ID is not available.
+ */
+export const useAfterpayCartLock = () => useCartLock()
diff --git a/packages/magento-payment-afterpay/icons/afterpay.png b/packages/magento-payment-afterpay/icons/afterpay.png
new file mode 100644
index 0000000000..2bd2eca0b1
Binary files /dev/null and b/packages/magento-payment-afterpay/icons/afterpay.png differ
diff --git a/packages/magento-payment-afterpay/index.ts b/packages/magento-payment-afterpay/index.ts
new file mode 100644
index 0000000000..336ce12bb9
--- /dev/null
+++ b/packages/magento-payment-afterpay/index.ts
@@ -0,0 +1 @@
+export {}
diff --git a/packages/magento-payment-afterpay/next-env.d.ts b/packages/magento-payment-afterpay/next-env.d.ts
new file mode 100644
index 0000000000..799156c1ad
--- /dev/null
+++ b/packages/magento-payment-afterpay/next-env.d.ts
@@ -0,0 +1,4 @@
+///
+///
+///
+///
diff --git a/packages/magento-payment-afterpay/package.json b/packages/magento-payment-afterpay/package.json
new file mode 100644
index 0000000000..0fb14dbb04
--- /dev/null
+++ b/packages/magento-payment-afterpay/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@graphcommerce/magento-payment-afterpay",
+ "homepage": "https://www.graphcommerce.org/",
+ "repository": "github:graphcommerce-org/graphcommerce",
+ "version": "9.0.0-canary.80",
+ "private": true,
+ "sideEffects": false,
+ "prettier": "@graphcommerce/prettier-config-pwa",
+ "eslintConfig": {
+ "extends": "@graphcommerce/eslint-config-pwa",
+ "parserOptions": {
+ "project": "./tsconfig.json"
+ }
+ },
+ "peerDependencies": {
+ "@graphcommerce/eslint-config-pwa": "^9.0.0-canary.80",
+ "@graphcommerce/graphql": "^9.0.0-canary.80",
+ "@graphcommerce/image": "^9.0.0-canary.80",
+ "@graphcommerce/magento-cart": "^9.0.0-canary.80",
+ "@graphcommerce/magento-store": "^9.0.0-canary.80",
+ "@graphcommerce/next-ui": "^9.0.0-canary.80",
+ "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.80",
+ "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.80",
+ "@lingui/core": "^4.2.1",
+ "@lingui/macro": "^4.2.1",
+ "@lingui/react": "^4.2.1",
+ "@mui/material": "^5.10.16",
+ "framer-motion": "^10.0.0",
+ "next": "*",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ }
+}
diff --git a/packages/magento-payment-afterpay/plugins/AddAfterpayMethods.tsx b/packages/magento-payment-afterpay/plugins/AddAfterpayMethods.tsx
new file mode 100644
index 0000000000..cbf7ef5a87
--- /dev/null
+++ b/packages/magento-payment-afterpay/plugins/AddAfterpayMethods.tsx
@@ -0,0 +1,25 @@
+import {
+ PaymentMethodContextProviderProps,
+ PaymentMethodOptionsNoop,
+} from '@graphcommerce/magento-cart-payment-method'
+import type { PluginProps } from '@graphcommerce/next-config'
+import { AfterpayPaymentActionCard } from '../components/AfterpayPaymentActionCard/AfterpayPaymentActionCard'
+import { AfterpayPaymentHandler } from '../components/AfterpayPaymentHandler/AfterpayPaymentHandler'
+import { AfterpayPaymentPlaceOrder } from '../components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder'
+
+export const component = 'PaymentMethodContextProvider'
+export const exported = '@graphcommerce/magento-cart-payment-method'
+
+const afterpay = {
+ PaymentOptions: PaymentMethodOptionsNoop,
+ PaymentActionCard: AfterpayPaymentActionCard,
+ PaymentHandler: AfterpayPaymentHandler,
+ PaymentPlaceOrder: AfterpayPaymentPlaceOrder,
+}
+
+function AddAfterpayMethods(props: PluginProps) {
+ const { modules, Prev, ...rest } = props
+ return
+}
+
+export const Plugin = AddAfterpayMethods
diff --git a/packages/magento-payment-afterpay/tsconfig.json b/packages/magento-payment-afterpay/tsconfig.json
new file mode 100644
index 0000000000..1fc364d21b
--- /dev/null
+++ b/packages/magento-payment-afterpay/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "exclude": ["node_modules"],
+ "include": ["**/*.ts", "**/*.tsx"],
+ "extends": "@graphcommerce/typescript-config-pwa/nextjs.json"
+}
diff --git a/packages/magento-payment-klarna/KlarnaPaymentMethod.graphql b/packages/magento-payment-klarna/KlarnaPaymentMethod.graphql
deleted file mode 100644
index 52c24adf6f..0000000000
--- a/packages/magento-payment-klarna/KlarnaPaymentMethod.graphql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation KlarnaPaymentMethod($cartId: String!, $authorizationToken: String!) {
- setPaymentMethodOnCart(
- input: {
- cart_id: $cartId
- payment_method: { code: "klarna", klarna: { authorization_token: $authorizationToken } }
- }
- ) {
- cart {
- ...PaymentMethodUpdated
- }
- }
-}
diff --git a/packages/magento-payment-klarna/README.md b/packages/magento-payment-klarna/README.md
index 2d8280606f..e04694a295 100644
--- a/packages/magento-payment-klarna/README.md
+++ b/packages/magento-payment-klarna/README.md
@@ -2,8 +2,6 @@
Integrates GraphCommerce with the Magento Klarna module.
-Note: Not yet working
-
## Installation
1. Find current version of your `@graphcommerce/magento-cart-payment-method` in
diff --git a/packages/magento-payment-klarna/components/KlarnaPaymentActionCard/KlarnaPaymentActionCard.tsx b/packages/magento-payment-klarna/components/KlarnaPaymentActionCard/KlarnaPaymentActionCard.tsx
new file mode 100644
index 0000000000..ae55b55e9c
--- /dev/null
+++ b/packages/magento-payment-klarna/components/KlarnaPaymentActionCard/KlarnaPaymentActionCard.tsx
@@ -0,0 +1,26 @@
+import Script from 'next/script'
+import { Image } from '@graphcommerce/image'
+import { PaymentMethodActionCardProps } from '@graphcommerce/magento-cart-payment-method'
+import { ActionCard, useIconSvgSize } from '@graphcommerce/next-ui'
+import klarnaMark from '../../icons/klarna.png'
+
+export function KlarnaPaymentActionCard(props: PaymentMethodActionCardProps) {
+ const iconSize = useIconSvgSize('large')
+
+ return (
+ <>
+
+
+ }
+ />
+ >
+ )
+}
diff --git a/packages/magento-payment-klarna/KlarnaPaymentSession.graphql b/packages/magento-payment-klarna/components/KlarnaPaymentOptions/KlarnaPaymentOptions.graphql
similarity index 81%
rename from packages/magento-payment-klarna/KlarnaPaymentSession.graphql
rename to packages/magento-payment-klarna/components/KlarnaPaymentOptions/KlarnaPaymentOptions.graphql
index 7284df1b56..38a8b88b32 100644
--- a/packages/magento-payment-klarna/KlarnaPaymentSession.graphql
+++ b/packages/magento-payment-klarna/components/KlarnaPaymentOptions/KlarnaPaymentOptions.graphql
@@ -1,4 +1,4 @@
-mutation KlarnaPaymentSession($cartId: String!) {
+mutation KlarnaPaymentOptions($cartId: String!) {
createKlarnaPaymentsSession(input: { cart_id: $cartId }) {
client_token
payment_method_categories {
diff --git a/packages/magento-payment-klarna/components/KlarnaPaymentOptions/KlarnaPaymentOptions.tsx b/packages/magento-payment-klarna/components/KlarnaPaymentOptions/KlarnaPaymentOptions.tsx
new file mode 100644
index 0000000000..223ca577fa
--- /dev/null
+++ b/packages/magento-payment-klarna/components/KlarnaPaymentOptions/KlarnaPaymentOptions.tsx
@@ -0,0 +1,50 @@
+import { useFormCompose, useFormAutoSubmit } from '@graphcommerce/ecommerce-ui'
+import { useFormGqlMutationCart } from '@graphcommerce/magento-cart'
+import { PaymentOptionsProps } from '@graphcommerce/magento-cart-payment-method'
+import { KlarnaPaymentOptionsDocument } from './KlarnaPaymentOptions.gql'
+
+declare var Klarna: any
+
+export function KlarnaPaymentOptions(props: PaymentOptionsProps) {
+ const { code, step } = props
+
+ const form = useFormGqlMutationCart(KlarnaPaymentOptionsDocument, {
+ onBeforeSubmit: (variables) => ({
+ ...variables,
+ }),
+ onComplete: async (result) => {
+ if (result.errors) return
+
+ const clientToken = result.data?.createKlarnaPaymentsSession?.client_token
+
+ try {
+ Klarna.Payments.init({ client_token: clientToken })
+ Klarna.Payments.load(
+ {
+ container: '#klarna-payments-container',
+ },
+ {},
+ function (res) {
+ console.debug(res)
+ },
+ )
+ } catch (e) {
+ console.error(e)
+ }
+ },
+ mode: 'onChange',
+ })
+
+ const submit = form.handleSubmit(() => {})
+ useFormAutoSubmit({ form, submit, forceInitialSubmit: true })
+
+ const key = `PaymentMethodOptions_${code}`
+ useFormCompose({ form, step, submit, key })
+
+ return (
+
+ )
+}
diff --git a/packages/magento-payment-klarna/components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder.graphql b/packages/magento-payment-klarna/components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder.graphql
new file mode 100644
index 0000000000..8a440a4af6
--- /dev/null
+++ b/packages/magento-payment-klarna/components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder.graphql
@@ -0,0 +1,12 @@
+mutation KlarnaPaymentPlaceOrder($cartId: String!, $paymentMethod: PaymentMethodInput!) {
+ setPaymentMethodOnCart(input: { cart_id: $cartId, payment_method: $paymentMethod }) {
+ cart {
+ ...PaymentMethodUpdated
+ }
+ }
+ placeOrder(input: { cart_id: $cartId }) {
+ order {
+ order_number
+ }
+ }
+}
diff --git a/packages/magento-payment-klarna/components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder.tsx b/packages/magento-payment-klarna/components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder.tsx
new file mode 100644
index 0000000000..393b8e7a88
--- /dev/null
+++ b/packages/magento-payment-klarna/components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder.tsx
@@ -0,0 +1,116 @@
+import { useState, useEffect } from 'react'
+import { useRouter } from 'next/router'
+import { useCartQuery } from '@graphcommerce/magento-cart'
+import { useFormCompose } from '@graphcommerce/ecommerce-ui'
+import { useFormGqlMutationCart } from '@graphcommerce/magento-cart'
+import {
+ PaymentPlaceOrderProps,
+ usePaymentMethodContext,
+} from '@graphcommerce/magento-cart-payment-method'
+import { useKlarnaCartLock } from '../../hooks/useKlarnaCartLock'
+import { BillingPageDocument } from '@graphcommerce/magento-cart-checkout'
+import { KlarnaPaymentPlaceOrderDocument } from './KlarnaPaymentPlaceOrder.gql'
+
+declare var Klarna: any
+
+export type KlarnaResponse = {
+ approved?: boolean
+ finalize_required?: boolean
+ authorization_token: string
+ show_form?: boolean
+}
+
+export function KlarnaPaymentPlaceOrder(props: PaymentPlaceOrderProps) {
+ const { code, step } = props
+ const [, lock, unlock] = useKlarnaCartLock()
+ const { reload } = useRouter()
+ const { onSuccess } = usePaymentMethodContext()
+ const cart = useCartQuery(BillingPageDocument)
+
+ const klarnaPaymentsAuthorize = () => {
+ return new Promise((resolve, reject) => {
+ try {
+ Klarna.Payments.authorize(
+ {
+ payment_method_category: 'pay_later',
+ },
+ {
+ billing_address: {
+ given_name: cart.data?.cart?.billing_address?.firstname.toUpperCase(),
+ family_name: cart.data?.cart?.billing_address?.lastname.toUpperCase(),
+ email: cart.data?.cart?.email ?? undefined,
+ street_address: cart.data?.cart?.billing_address?.street.join(' ').toUpperCase(),
+ postal_code: cart.data?.cart?.billing_address?.postcode ?? undefined,
+ phone: cart.data?.cart?.billing_address?.telephone ?? undefined,
+ region: cart.data?.cart?.billing_address?.region?.code,
+ city: cart.data?.cart?.billing_address?.city.toUpperCase(),
+ country: cart.data?.cart?.billing_address?.country.code,
+ },
+ shipping_address: {
+ given_name: cart.data?.cart?.shipping_addresses[0]?.firstname.toUpperCase(),
+ family_name: cart.data?.cart?.shipping_addresses[0]?.lastname.toUpperCase(),
+ email: cart.data?.cart?.email ?? undefined,
+ street_address: cart.data?.cart?.shipping_addresses[0]?.street
+ .join(' ')
+ .toUpperCase(),
+ postal_code: cart.data?.cart?.shipping_addresses[0]?.postcode ?? undefined,
+ phone: cart.data?.cart?.shipping_addresses[0]?.telephone ?? undefined,
+ region: cart.data?.cart?.shipping_addresses[0]?.region?.code,
+ city: cart.data?.cart?.shipping_addresses[0]?.city.toUpperCase(),
+ country: cart.data?.cart?.shipping_addresses[0]?.country.code,
+ },
+ customer: { type: 'person' },
+ },
+ function (response: KlarnaResponse) {
+ resolve(response)
+ },
+ )
+ } catch (error) {
+ reject(error)
+ }
+ })
+ }
+
+ const form = useFormGqlMutationCart(KlarnaPaymentPlaceOrderDocument, {
+ onBeforeSubmit: async (variabless) => {
+ try {
+ await lock({ method: code })
+ const response = await klarnaPaymentsAuthorize()
+ if (response.approved === false) {
+ await unlock({})
+ reload()
+ }
+ return {
+ cartId: variabless.cartId,
+ paymentMethod: {
+ code,
+ klarna: {
+ authorization_token: response.authorization_token ?? '',
+ },
+ },
+ }
+ } catch (error) {
+ await unlock({})
+ throw error
+ }
+ },
+ onComplete: async (result) => {
+ if (result.errors || !result.data?.placeOrder?.order) {
+ await unlock({})
+ return
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
+ onSuccess(result.data?.placeOrder?.order.order_number)
+ },
+ })
+
+ const submit = form.handleSubmit(() => {})
+ useFormCompose({ form, step, submit, key: `KlarnaPaymentPlaceOrder_${code}` })
+
+ return (
+
+ )
+}
diff --git a/packages/magento-payment-klarna/hooks/useKlarnaCartLock.ts b/packages/magento-payment-klarna/hooks/useKlarnaCartLock.ts
new file mode 100644
index 0000000000..da9936f059
--- /dev/null
+++ b/packages/magento-payment-klarna/hooks/useKlarnaCartLock.ts
@@ -0,0 +1,15 @@
+import { CartLockState, useCartLock } from '@graphcommerce/magento-cart-payment-method'
+
+type KlarnaLockState = CartLockState & {
+ clientToken?: string | null
+ authorizationToken?: string | null
+}
+
+/**
+ * The cart lock situation is a bit odd since are unable to actually influence the return URL we
+ * can't safely remember the cart ID.
+ *
+ * This is a potential bug because when the customer is returning from an icognito session, the cart
+ * ID is not available.
+ */
+export const useKlarnaCartLock = () => useCartLock()
diff --git a/packages/magento-payment-klarna/icons/klarna.png b/packages/magento-payment-klarna/icons/klarna.png
new file mode 100644
index 0000000000..e048083742
Binary files /dev/null and b/packages/magento-payment-klarna/icons/klarna.png differ
diff --git a/packages/magento-payment-klarna/methods.ts b/packages/magento-payment-klarna/methods.ts
new file mode 100644
index 0000000000..12402aec83
--- /dev/null
+++ b/packages/magento-payment-klarna/methods.ts
@@ -0,0 +1,20 @@
+import { PaymentModule } from '@graphcommerce/magento-cart-payment-method'
+import { KlarnaPaymentActionCard } from './components/KlarnaPaymentActionCard/KlarnaPaymentActionCard'
+import { KlarnaPaymentOptions } from './components/KlarnaPaymentOptions/KlarnaPaymentOptions'
+import { KlarnaPaymentPlaceOrder } from './components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder'
+
+export const component = 'PaymentMethodContextProvider'
+export const exported = '@graphcommerce/magento-cart-payment-method'
+
+const KlarnaModule: PaymentModule = {
+ PaymentOptions: KlarnaPaymentOptions,
+ PaymentActionCard: KlarnaPaymentActionCard,
+ PaymentPlaceOrder: KlarnaPaymentPlaceOrder,
+}
+
+export const klarna: Record = {
+ klarna_kco: KlarnaModule,
+ klarna_pay_now: KlarnaModule,
+ klarna_pay_later: KlarnaModule,
+ klarna_pay_over_time: KlarnaModule,
+}
diff --git a/packages/magento-payment-klarna/package.json b/packages/magento-payment-klarna/package.json
index 2e2988ab71..d86c6a6928 100644
--- a/packages/magento-payment-klarna/package.json
+++ b/packages/magento-payment-klarna/package.json
@@ -13,10 +13,12 @@
}
},
"peerDependencies": {
+ "@graphcommerce/ecommerce-ui": "^9.0.0-canary.104",
"@graphcommerce/eslint-config-pwa": "^9.0.0-canary.104",
"@graphcommerce/graphql": "^9.0.0-canary.104",
"@graphcommerce/image": "^9.0.0-canary.104",
"@graphcommerce/magento-cart": "^9.0.0-canary.104",
+ "@graphcommerce/magento-cart-payment-method": "9.0.0-canary.104",
"@graphcommerce/magento-store": "^9.0.0-canary.104",
"@graphcommerce/next-ui": "^9.0.0-canary.104",
"@graphcommerce/prettier-config-pwa": "^9.0.0-canary.104",
diff --git a/packages/magento-payment-klarna/plugins/AddKlarnaMethods.tsx b/packages/magento-payment-klarna/plugins/AddKlarnaMethods.tsx
new file mode 100644
index 0000000000..01be85596a
--- /dev/null
+++ b/packages/magento-payment-klarna/plugins/AddKlarnaMethods.tsx
@@ -0,0 +1,14 @@
+import { PaymentMethodContextProviderProps } from '@graphcommerce/magento-cart-payment-method'
+import type { PluginProps } from '@graphcommerce/next-config'
+
+import { klarna } from '../methods'
+
+export const component = 'PaymentMethodContextProvider'
+export const exported = '@graphcommerce/magento-cart-payment-method'
+
+function AddKlarnaMethods(props: PluginProps) {
+ const { modules, Prev, ...rest } = props
+ return
+}
+
+export const Plugin = AddKlarnaMethods