@graphcommerce/magento-payment-klarna 10.0.0-canary.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1004 -0
- package/README.md +13 -0
- package/components/KlarnaPaymentActionCard/KlarnaPaymentActionCard.tsx +26 -0
- package/components/KlarnaPaymentOptions/KlarnaPaymentOptions.graphql +13 -0
- package/components/KlarnaPaymentOptions/KlarnaPaymentOptions.tsx +42 -0
- package/components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder.graphql +12 -0
- package/components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder.tsx +102 -0
- package/hooks/useKlarnaCartLock.ts +16 -0
- package/icons/klarna.png +0 -0
- package/index.ts +1 -0
- package/klarna.d.ts +24 -0
- package/methods.ts +20 -0
- package/next-env.d.ts +4 -0
- package/package.json +40 -0
- package/plugins/AddKlarnaMethods.tsx +13 -0
- package/tsconfig.json +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Magento Payment Klarna
|
|
2
|
+
|
|
3
|
+
Integrates GraphCommerce with the Magento Klarna module.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
1. Find current version of your `@graphcommerce/magento-cart-payment-method` in
|
|
8
|
+
your package.json.
|
|
9
|
+
2. `yarn add @graphcommerce/magento-payment-klarna@1.2.3` (replace 1.2.3 with
|
|
10
|
+
the version of the step above)
|
|
11
|
+
|
|
12
|
+
This package uses GraphCommerce plugin systems, so there is no code modification
|
|
13
|
+
required.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Image } from '@graphcommerce/image'
|
|
2
|
+
import type { PaymentMethodActionCardProps } from '@graphcommerce/magento-cart-payment-method'
|
|
3
|
+
import { ActionCard, useIconSvgSize } from '@graphcommerce/next-ui'
|
|
4
|
+
import Script from 'next/script'
|
|
5
|
+
import klarnaMark from '../../icons/klarna.png'
|
|
6
|
+
|
|
7
|
+
export function KlarnaPaymentActionCard(props: PaymentMethodActionCardProps) {
|
|
8
|
+
const iconSize = useIconSvgSize('large')
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<>
|
|
12
|
+
<Script src='https://x.klarnacdn.net/kp/lib/v1/api.js' strategy='lazyOnload' />
|
|
13
|
+
<ActionCard
|
|
14
|
+
{...props}
|
|
15
|
+
image={
|
|
16
|
+
<Image
|
|
17
|
+
layout='fixed'
|
|
18
|
+
sx={{ width: iconSize, height: iconSize, objectFit: 'contain' }}
|
|
19
|
+
unoptimized
|
|
20
|
+
src={klarnaMark}
|
|
21
|
+
/>
|
|
22
|
+
}
|
|
23
|
+
/>
|
|
24
|
+
</>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useFormAutoSubmit, useFormCompose } from '@graphcommerce/ecommerce-ui'
|
|
2
|
+
import { useFormGqlMutationCart } from '@graphcommerce/magento-cart'
|
|
3
|
+
import type { PaymentOptionsProps } from '@graphcommerce/magento-cart-payment-method'
|
|
4
|
+
import { KlarnaPaymentOptionsDocument } from './KlarnaPaymentOptions.gql'
|
|
5
|
+
|
|
6
|
+
export function KlarnaPaymentOptions(props: PaymentOptionsProps) {
|
|
7
|
+
const { code, step } = props
|
|
8
|
+
|
|
9
|
+
const form = useFormGqlMutationCart(KlarnaPaymentOptionsDocument, {
|
|
10
|
+
onBeforeSubmit: (variables) => ({
|
|
11
|
+
...variables,
|
|
12
|
+
}),
|
|
13
|
+
onComplete: async (result) => {
|
|
14
|
+
if (result.errors) return
|
|
15
|
+
|
|
16
|
+
const clientToken = result.data?.createKlarnaPaymentsSession?.client_token
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
Klarna.Payments.init({ client_token: clientToken })
|
|
20
|
+
Klarna.Payments.load({ container: '#klarna-payments-container' }, {}, (res) => {
|
|
21
|
+
console.info(res)
|
|
22
|
+
})
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error(e)
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
mode: 'onChange',
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const submit = form.handleSubmit(() => {})
|
|
31
|
+
useFormAutoSubmit({ form, submit, forceInitialSubmit: true })
|
|
32
|
+
|
|
33
|
+
const key = `PaymentMethodOptions_${code}`
|
|
34
|
+
useFormCompose({ form, step, submit, key })
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<form onSubmit={submit} noValidate>
|
|
38
|
+
<input type='hidden' value={code} />
|
|
39
|
+
<div id='klarna-payments-container' />
|
|
40
|
+
</form>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
mutation KlarnaPaymentPlaceOrder($cartId: String!, $paymentMethod: PaymentMethodInput!) {
|
|
2
|
+
setPaymentMethodOnCart(input: { cart_id: $cartId, payment_method: $paymentMethod }) {
|
|
3
|
+
cart {
|
|
4
|
+
...PaymentMethodUpdated
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
placeOrder(input: { cart_id: $cartId }) {
|
|
8
|
+
order {
|
|
9
|
+
order_number
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useFormCompose } from '@graphcommerce/ecommerce-ui'
|
|
2
|
+
import { useCartQuery, useFormGqlMutationCart } from '@graphcommerce/magento-cart'
|
|
3
|
+
import { BillingPageDocument } from '@graphcommerce/magento-cart-checkout'
|
|
4
|
+
import type { PaymentPlaceOrderProps } from '@graphcommerce/magento-cart-payment-method'
|
|
5
|
+
import { usePaymentMethodContext } from '@graphcommerce/magento-cart-payment-method'
|
|
6
|
+
import { useRouter } from 'next/router'
|
|
7
|
+
import { useKlarnaCartLock } from '../../hooks/useKlarnaCartLock'
|
|
8
|
+
import type { KlarnaResponse } from '../../klarna.d'
|
|
9
|
+
import { KlarnaPaymentPlaceOrderDocument } from './KlarnaPaymentPlaceOrder.gql'
|
|
10
|
+
|
|
11
|
+
export function KlarnaPaymentPlaceOrder(props: PaymentPlaceOrderProps) {
|
|
12
|
+
const { code, step } = props
|
|
13
|
+
const [, lock, unlock] = useKlarnaCartLock()
|
|
14
|
+
const { reload } = useRouter()
|
|
15
|
+
const { onSuccess } = usePaymentMethodContext()
|
|
16
|
+
const cart = useCartQuery(BillingPageDocument)
|
|
17
|
+
|
|
18
|
+
const klarnaPaymentsAuthorize = () =>
|
|
19
|
+
new Promise<KlarnaResponse>((resolve, reject) => {
|
|
20
|
+
try {
|
|
21
|
+
Klarna.Payments.authorize(
|
|
22
|
+
{
|
|
23
|
+
payment_method_category: 'pay_later',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
billing_address: {
|
|
27
|
+
given_name: cart.data?.cart?.billing_address?.firstname.toUpperCase(),
|
|
28
|
+
family_name: cart.data?.cart?.billing_address?.lastname.toUpperCase(),
|
|
29
|
+
email: cart.data?.cart?.email ?? undefined,
|
|
30
|
+
street_address: cart.data?.cart?.billing_address?.street.join(' ').toUpperCase(),
|
|
31
|
+
postal_code: cart.data?.cart?.billing_address?.postcode ?? undefined,
|
|
32
|
+
phone: cart.data?.cart?.billing_address?.telephone ?? undefined,
|
|
33
|
+
region: cart.data?.cart?.billing_address?.region?.code,
|
|
34
|
+
city: cart.data?.cart?.billing_address?.city.toUpperCase(),
|
|
35
|
+
country: cart.data?.cart?.billing_address?.country.code,
|
|
36
|
+
},
|
|
37
|
+
shipping_address: {
|
|
38
|
+
given_name: cart.data?.cart?.shipping_addresses[0]?.firstname.toUpperCase(),
|
|
39
|
+
family_name: cart.data?.cart?.shipping_addresses[0]?.lastname.toUpperCase(),
|
|
40
|
+
email: cart.data?.cart?.email ?? undefined,
|
|
41
|
+
street_address: cart.data?.cart?.shipping_addresses[0]?.street
|
|
42
|
+
.join(' ')
|
|
43
|
+
.toUpperCase(),
|
|
44
|
+
postal_code: cart.data?.cart?.shipping_addresses[0]?.postcode ?? undefined,
|
|
45
|
+
phone: cart.data?.cart?.shipping_addresses[0]?.telephone ?? undefined,
|
|
46
|
+
region: cart.data?.cart?.shipping_addresses[0]?.region?.code,
|
|
47
|
+
city: cart.data?.cart?.shipping_addresses[0]?.city.toUpperCase(),
|
|
48
|
+
country: cart.data?.cart?.shipping_addresses[0]?.country.code,
|
|
49
|
+
},
|
|
50
|
+
customer: { type: 'person' },
|
|
51
|
+
},
|
|
52
|
+
(response: KlarnaResponse) => {
|
|
53
|
+
resolve(response)
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
} catch (error) {
|
|
57
|
+
reject(error)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const form = useFormGqlMutationCart(KlarnaPaymentPlaceOrderDocument, {
|
|
62
|
+
onBeforeSubmit: async (variabless) => {
|
|
63
|
+
try {
|
|
64
|
+
await lock({ method: code })
|
|
65
|
+
const response = await klarnaPaymentsAuthorize()
|
|
66
|
+
if (response.approved === false) {
|
|
67
|
+
await unlock({})
|
|
68
|
+
reload()
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
cartId: variabless.cartId,
|
|
72
|
+
paymentMethod: {
|
|
73
|
+
code,
|
|
74
|
+
klarna: {
|
|
75
|
+
authorization_token: response.authorization_token ?? '',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
await unlock({})
|
|
81
|
+
throw error
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
onComplete: async (result) => {
|
|
85
|
+
if (result.errors || !result.data?.placeOrder?.order) {
|
|
86
|
+
await unlock({})
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await onSuccess(result.data?.placeOrder?.order.order_number)
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const submit = form.handleSubmit(() => {})
|
|
95
|
+
useFormCompose({ form, step, submit, key: `KlarnaPaymentPlaceOrder_${code}` })
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<form onSubmit={submit} noValidate>
|
|
99
|
+
<input type='hidden' value={code} />
|
|
100
|
+
</form>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CartLockState } from '@graphcommerce/magento-cart-payment-method'
|
|
2
|
+
import { useCartLock } from '@graphcommerce/magento-cart-payment-method'
|
|
3
|
+
|
|
4
|
+
type KlarnaLockState = CartLockState & {
|
|
5
|
+
clientToken?: string | null
|
|
6
|
+
authorizationToken?: string | null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The cart lock situation is a bit odd since are unable to actually influence the return URL we
|
|
11
|
+
* can't safely remember the cart ID.
|
|
12
|
+
*
|
|
13
|
+
* This is a potential bug because when the customer is returning from an icognito session, the cart
|
|
14
|
+
* ID is not available.
|
|
15
|
+
*/
|
|
16
|
+
export const useKlarnaCartLock = () => useCartLock<KlarnaLockState>()
|
package/icons/klarna.png
ADDED
|
Binary file
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/klarna.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type KlarnaResponse = {
|
|
2
|
+
approved?: boolean
|
|
3
|
+
finalize_required?: boolean
|
|
4
|
+
authorization_token: string
|
|
5
|
+
show_form?: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
namespace Klarna {
|
|
10
|
+
const Payments: {
|
|
11
|
+
init: (options: { client_token: string | null | undefined }) => void
|
|
12
|
+
load: (
|
|
13
|
+
options: { container: string },
|
|
14
|
+
data: Record<string, unknown>,
|
|
15
|
+
callback: (res: unknown) => void,
|
|
16
|
+
) => void
|
|
17
|
+
authorize: (
|
|
18
|
+
options: { payment_method_category: string },
|
|
19
|
+
data: Record<string, unknown>,
|
|
20
|
+
callback: (response: KlarnaResponse) => void,
|
|
21
|
+
) => void
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/methods.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PaymentModule } from '@graphcommerce/magento-cart-payment-method'
|
|
2
|
+
import { KlarnaPaymentActionCard } from './components/KlarnaPaymentActionCard/KlarnaPaymentActionCard'
|
|
3
|
+
import { KlarnaPaymentOptions } from './components/KlarnaPaymentOptions/KlarnaPaymentOptions'
|
|
4
|
+
import { KlarnaPaymentPlaceOrder } from './components/KlarnaPaymentPlaceOrder/KlarnaPaymentPlaceOrder'
|
|
5
|
+
|
|
6
|
+
export const component = 'PaymentMethodContextProvider'
|
|
7
|
+
export const exported = '@graphcommerce/magento-cart-payment-method'
|
|
8
|
+
|
|
9
|
+
const KlarnaModule: PaymentModule = {
|
|
10
|
+
PaymentOptions: KlarnaPaymentOptions,
|
|
11
|
+
PaymentActionCard: KlarnaPaymentActionCard,
|
|
12
|
+
PaymentPlaceOrder: KlarnaPaymentPlaceOrder,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const klarna: Record<string, PaymentModule> = {
|
|
16
|
+
klarna_kco: KlarnaModule,
|
|
17
|
+
klarna_pay_now: KlarnaModule,
|
|
18
|
+
klarna_pay_later: KlarnaModule,
|
|
19
|
+
klarna_pay_over_time: KlarnaModule,
|
|
20
|
+
}
|
package/next-env.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphcommerce/magento-payment-klarna",
|
|
3
|
+
"homepage": "https://www.graphcommerce.org/",
|
|
4
|
+
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
+
"version": "10.0.0-canary.72",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
|
+
"eslintConfig": {
|
|
9
|
+
"extends": "@graphcommerce/eslint-config-pwa",
|
|
10
|
+
"parserOptions": {
|
|
11
|
+
"project": "./tsconfig.json"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": "./index.ts",
|
|
16
|
+
"./plugins/AddKlarnaMethods": "./plugins/AddKlarnaMethods.tsx"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@graphcommerce/ecommerce-ui": "^10.0.0-canary.72",
|
|
20
|
+
"@graphcommerce/eslint-config-pwa": "^10.0.0-canary.72",
|
|
21
|
+
"@graphcommerce/graphql": "^10.0.0-canary.72",
|
|
22
|
+
"@graphcommerce/image": "^10.0.0-canary.72",
|
|
23
|
+
"@graphcommerce/magento-cart": "^10.0.0-canary.72",
|
|
24
|
+
"@graphcommerce/magento-cart-checkout": "^10.0.0-canary.72",
|
|
25
|
+
"@graphcommerce/magento-cart-payment-method": "^10.0.0-canary.72",
|
|
26
|
+
"@graphcommerce/magento-store": "^10.0.0-canary.72",
|
|
27
|
+
"@graphcommerce/next-ui": "^10.0.0-canary.72",
|
|
28
|
+
"@graphcommerce/prettier-config-pwa": "^10.0.0-canary.72",
|
|
29
|
+
"@graphcommerce/react-hook-form": "^10.0.0-canary.72",
|
|
30
|
+
"@graphcommerce/typescript-config-pwa": "^10.0.0-canary.72",
|
|
31
|
+
"@lingui/core": "^5",
|
|
32
|
+
"@lingui/macro": "^5",
|
|
33
|
+
"@lingui/react": "^5",
|
|
34
|
+
"@mui/material": "^7.0.0",
|
|
35
|
+
"framer-motion": "^11.0.0",
|
|
36
|
+
"next": "*",
|
|
37
|
+
"react": "^19.2.0",
|
|
38
|
+
"react-dom": "^19.2.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PaymentMethodContextProviderProps } from '@graphcommerce/magento-cart-payment-method'
|
|
2
|
+
import type { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { klarna } from '../methods'
|
|
4
|
+
|
|
5
|
+
export const component = 'PaymentMethodContextProvider'
|
|
6
|
+
export const exported = '@graphcommerce/magento-cart-payment-method'
|
|
7
|
+
|
|
8
|
+
function AddKlarnaMethods(props: PluginProps<PaymentMethodContextProviderProps>) {
|
|
9
|
+
const { modules, Prev, ...rest } = props
|
|
10
|
+
return <Prev {...rest} modules={{ ...modules, ...klarna }} />
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Plugin = AddKlarnaMethods
|