@graphcommerce/magento-payment-afterpay 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 +5 -0
- package/README.md +16 -0
- package/components/AfterpayPaymentActionCard/AfterpayPaymentActionCard.tsx +22 -0
- package/components/AfterpayPaymentHandler/AfterpayPaymentHandler.graphql +12 -0
- package/components/AfterpayPaymentHandler/AfterpayPaymentHandler.tsx +58 -0
- package/components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder.graphql +6 -0
- package/components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder.tsx +43 -0
- package/hooks/useAfterpayCartLock.ts +16 -0
- package/icons/afterpay.png +0 -0
- package/index.ts +1 -0
- package/next-env.d.ts +4 -0
- package/package.json +39 -0
- package/plugins/AddAfterpayMethods.tsx +23 -0
- package/tsconfig.json +5 -0
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Magento Payment Afterpay
|
|
2
|
+
|
|
3
|
+
Integrates GraphCommerce with the Magento Afterpay module.
|
|
4
|
+
|
|
5
|
+
It requires [Afterpay](https://github.com/afterpay/afterpay-magento-2) module to
|
|
6
|
+
be installed before using this package.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
1. Find current version of your `@graphcommerce/magento-cart-payment-method` in
|
|
11
|
+
your package.json.
|
|
12
|
+
2. `yarn add @graphcommerce/magento-payment-afterpay@1.2.3` (replace 1.2.3 with
|
|
13
|
+
the version of the step above)
|
|
14
|
+
|
|
15
|
+
This package uses GraphCommerce plugin systems, so there is no code modification
|
|
16
|
+
required.
|
|
@@ -0,0 +1,22 @@
|
|
|
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 afterpayMark from '../../icons/afterpay.png'
|
|
5
|
+
|
|
6
|
+
export function AfterpayPaymentActionCard(props: PaymentMethodActionCardProps) {
|
|
7
|
+
const iconSize = useIconSvgSize('large')
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<ActionCard
|
|
11
|
+
{...props}
|
|
12
|
+
image={
|
|
13
|
+
<Image
|
|
14
|
+
layout='fixed'
|
|
15
|
+
sx={{ width: iconSize, height: iconSize, objectFit: 'contain' }}
|
|
16
|
+
unoptimized
|
|
17
|
+
src={afterpayMark}
|
|
18
|
+
/>
|
|
19
|
+
}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
mutation AfterpayPaymentHandler($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,58 @@
|
|
|
1
|
+
import { ApolloErrorSnackbar } from '@graphcommerce/ecommerce-ui'
|
|
2
|
+
import { useMutation } from '@graphcommerce/graphql'
|
|
3
|
+
import { useCurrentCartId } from '@graphcommerce/magento-cart'
|
|
4
|
+
import type { PaymentHandlerProps } from '@graphcommerce/magento-cart-payment-method'
|
|
5
|
+
import { usePaymentMethodContext } from '@graphcommerce/magento-cart-payment-method'
|
|
6
|
+
import { useRouter } from 'next/router'
|
|
7
|
+
import { useEffect } from 'react'
|
|
8
|
+
import { useAfterpayCartLock } from '../../hooks/useAfterpayCartLock'
|
|
9
|
+
import { AfterpayPaymentHandlerDocument } from './AfterpayPaymentHandler.gql'
|
|
10
|
+
|
|
11
|
+
export function AfterpayPaymentHandler(props: PaymentHandlerProps) {
|
|
12
|
+
const { code } = props
|
|
13
|
+
const { push } = useRouter()
|
|
14
|
+
const [lockStatus, , unlock] = useAfterpayCartLock()
|
|
15
|
+
const { onSuccess } = usePaymentMethodContext()
|
|
16
|
+
const { currentCartId: cartId } = useCurrentCartId()
|
|
17
|
+
|
|
18
|
+
const { orderToken, status, locked, justLocked, method } = lockStatus
|
|
19
|
+
const [placeOrder, { error, called }] = useMutation(AfterpayPaymentHandlerDocument, {
|
|
20
|
+
variables: {
|
|
21
|
+
cartId,
|
|
22
|
+
paymentMethod: {
|
|
23
|
+
code,
|
|
24
|
+
afterpay: {
|
|
25
|
+
afterpay_token: orderToken ?? '',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
errorPolicy: 'all',
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (locked && !justLocked && status === 'CANCELLED')
|
|
34
|
+
void unlock({ orderToken: null, status: null })
|
|
35
|
+
}, [status, code, justLocked, locked, method, unlock])
|
|
36
|
+
|
|
37
|
+
// If successfull we clear it's cart and redirect to the success page.
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!orderToken || status !== 'SUCCESS' || called) return
|
|
40
|
+
if (!cartId) return
|
|
41
|
+
|
|
42
|
+
const fetchData = async () => {
|
|
43
|
+
const res = await placeOrder()
|
|
44
|
+
|
|
45
|
+
if (res.error || !res.data?.placeOrder?.order) {
|
|
46
|
+
await unlock({ orderToken: null, status: null })
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await onSuccess(res.data?.placeOrder?.order.order_number)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
void fetchData()
|
|
54
|
+
}, [status, called, cartId, onSuccess, placeOrder, push, orderToken, unlock])
|
|
55
|
+
|
|
56
|
+
if (error) return <ApolloErrorSnackbar error={error} />
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useFormCompose } from '@graphcommerce/ecommerce-ui'
|
|
2
|
+
import { useFormGqlMutationCart } from '@graphcommerce/magento-cart'
|
|
3
|
+
import type { PaymentPlaceOrderProps } from '@graphcommerce/magento-cart-payment-method'
|
|
4
|
+
import { useRouter } from 'next/router'
|
|
5
|
+
import { useAfterpayCartLock } from '../../hooks/useAfterpayCartLock'
|
|
6
|
+
import { AfterpayPaymentPlaceOrderDocument } from './AfterpayPaymentPlaceOrder.gql'
|
|
7
|
+
|
|
8
|
+
export function AfterpayPaymentPlaceOrder(props: PaymentPlaceOrderProps) {
|
|
9
|
+
const { code, step } = props
|
|
10
|
+
const [, lock] = useAfterpayCartLock()
|
|
11
|
+
const { push } = useRouter()
|
|
12
|
+
|
|
13
|
+
const form = useFormGqlMutationCart(AfterpayPaymentPlaceOrderDocument, {
|
|
14
|
+
onBeforeSubmit: (variables) => ({
|
|
15
|
+
...variables,
|
|
16
|
+
redirect_path: { cancel_path: `checkout/payment`, confirm_path: `checkout/payment` },
|
|
17
|
+
}),
|
|
18
|
+
onComplete: async (result) => {
|
|
19
|
+
if (result.errors) return
|
|
20
|
+
|
|
21
|
+
const start = result.data?.createAfterpayCheckout?.afterpay_redirectCheckoutUrl
|
|
22
|
+
const orderToken = result.data?.createAfterpayCheckout?.afterpay_token
|
|
23
|
+
|
|
24
|
+
if (!start)
|
|
25
|
+
throw Error(
|
|
26
|
+
'Error while starting the Afterpay payment, please try again with a different payment method',
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
await lock({ orderToken, method: code })
|
|
30
|
+
// We are going to redirect, but we're not waiting, because we need to complete the submission to release the buttons
|
|
31
|
+
void push(start)
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const submit = form.handleSubmit(() => {})
|
|
36
|
+
useFormCompose({ form, step, submit, key: `AfterpayPaymentPlaceOrder_${code}` })
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<form onSubmit={submit} noValidate>
|
|
40
|
+
<input type='hidden' value={code} />
|
|
41
|
+
</form>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -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 AfterpayLockState = CartLockState & {
|
|
5
|
+
orderToken?: string | null
|
|
6
|
+
status?: 'SUCCESS' | 'CANCELLED' | 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 useAfterpayCartLock = () => useCartLock<AfterpayLockState>()
|
|
Binary file
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/next-env.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphcommerce/magento-payment-afterpay",
|
|
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/AddAfterpayMethods": "./plugins/AddAfterpayMethods.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-payment-method": "^10.0.0-canary.72",
|
|
25
|
+
"@graphcommerce/magento-store": "^10.0.0-canary.72",
|
|
26
|
+
"@graphcommerce/next-ui": "^10.0.0-canary.72",
|
|
27
|
+
"@graphcommerce/prettier-config-pwa": "^10.0.0-canary.72",
|
|
28
|
+
"@graphcommerce/react-hook-form": "^10.0.0-canary.72",
|
|
29
|
+
"@graphcommerce/typescript-config-pwa": "^10.0.0-canary.72",
|
|
30
|
+
"@lingui/core": "^5",
|
|
31
|
+
"@lingui/macro": "^5",
|
|
32
|
+
"@lingui/react": "^5",
|
|
33
|
+
"@mui/material": "^7.0.0",
|
|
34
|
+
"framer-motion": "^11.0.0",
|
|
35
|
+
"next": "*",
|
|
36
|
+
"react": "^19.2.0",
|
|
37
|
+
"react-dom": "^19.2.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PaymentMethodContextProviderProps } from '@graphcommerce/magento-cart-payment-method'
|
|
2
|
+
import { PaymentMethodOptionsNoop } from '@graphcommerce/magento-cart-payment-method'
|
|
3
|
+
import type { PluginProps } from '@graphcommerce/next-config'
|
|
4
|
+
import { AfterpayPaymentActionCard } from '../components/AfterpayPaymentActionCard/AfterpayPaymentActionCard'
|
|
5
|
+
import { AfterpayPaymentHandler } from '../components/AfterpayPaymentHandler/AfterpayPaymentHandler'
|
|
6
|
+
import { AfterpayPaymentPlaceOrder } from '../components/AfterpayPaymentPlaceOrder/AfterpayPaymentPlaceOrder'
|
|
7
|
+
|
|
8
|
+
export const component = 'PaymentMethodContextProvider'
|
|
9
|
+
export const exported = '@graphcommerce/magento-cart-payment-method'
|
|
10
|
+
|
|
11
|
+
const afterpay = {
|
|
12
|
+
PaymentOptions: PaymentMethodOptionsNoop,
|
|
13
|
+
PaymentActionCard: AfterpayPaymentActionCard,
|
|
14
|
+
PaymentHandler: AfterpayPaymentHandler,
|
|
15
|
+
PaymentPlaceOrder: AfterpayPaymentPlaceOrder,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function AddAfterpayMethods(props: PluginProps<PaymentMethodContextProviderProps>) {
|
|
19
|
+
const { modules, Prev, ...rest } = props
|
|
20
|
+
return <Prev {...rest} modules={{ ...modules, afterpay }} />
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Plugin = AddAfterpayMethods
|