@graphcommerce/magento-payment-paypal 1.0.2

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 ADDED
@@ -0,0 +1,34 @@
1
+ # @graphcommerce/magento-payment-paypal
2
+
3
+ ## 1.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1693](https://github.com/graphcommerce-org/graphcommerce/pull/1693) [`63e52a25d`](https://github.com/graphcommerce-org/graphcommerce/commit/63e52a25d35aa9600820155760fce23e91920185) Thanks [@paales](https://github.com/paales)! - Make release public
8
+
9
+ ## 1.0.1
10
+
11
+ ### Patch Changes
12
+
13
+ - [#1692](https://github.com/graphcommerce-org/graphcommerce/pull/1692) [`a7fbe58d4`](https://github.com/graphcommerce-org/graphcommerce/commit/a7fbe58d4bbb43c59fa2ead05935757d2013404c) Thanks [@paales](https://github.com/paales)! - Make sure the cartLock state is refreshed when the page is loaded with the bfcache
14
+
15
+ - [#1692](https://github.com/graphcommerce-org/graphcommerce/pull/1692) [`edbecfbfd`](https://github.com/graphcommerce-org/graphcommerce/commit/edbecfbfd532a6c78ae75ffe850c4bcf898e855d) Thanks [@paales](https://github.com/paales)! - Locking the cart when navigating away from the checkout and unlock the cart when pressing back. Made the justLocked flag a global state.
16
+
17
+ - Updated dependencies [[`a7fbe58d4`](https://github.com/graphcommerce-org/graphcommerce/commit/a7fbe58d4bbb43c59fa2ead05935757d2013404c), [`a26a2d05e`](https://github.com/graphcommerce-org/graphcommerce/commit/a26a2d05eecabeeef70e4d69105343197ae092b7), [`edbecfbfd`](https://github.com/graphcommerce-org/graphcommerce/commit/edbecfbfd532a6c78ae75ffe850c4bcf898e855d)]:
18
+ - @graphcommerce/magento-cart-payment-method@3.6.9
19
+ - @graphcommerce/magento-cart@4.9.5
20
+
21
+ ## 1.0.0
22
+
23
+ ### Major Changes
24
+
25
+ - [#1688](https://github.com/graphcommerce-org/graphcommerce/pull/1688) [`4490bde21`](https://github.com/graphcommerce-org/graphcommerce/commit/4490bde217c49d022d39486b21ca77a7dbc74f4c) Thanks [@paales](https://github.com/paales)! - Created PayPal express integration
26
+
27
+ ### Patch Changes
28
+
29
+ - Updated dependencies [[`8393cb266`](https://github.com/graphcommerce-org/graphcommerce/commit/8393cb2662860be0c2aa5df432447bb73c427d8e), [`f544401c7`](https://github.com/graphcommerce-org/graphcommerce/commit/f544401c7b653fda39c7c260ad0dcfb3bf543b65), [`f105d4223`](https://github.com/graphcommerce-org/graphcommerce/commit/f105d4223aa68df30970149e51ae72897e489bf9)]:
30
+ - @graphcommerce/next-ui@4.29.3
31
+ - @graphcommerce/magento-cart-payment-method@3.6.8
32
+ - @graphcommerce/ecommerce-ui@1.5.8
33
+ - @graphcommerce/magento-cart@4.9.4
34
+ - @graphcommerce/magento-store@4.3.6
@@ -0,0 +1,22 @@
1
+ import { Image } from '@graphcommerce/image'
2
+ import { PaymentMethodActionCardProps } from '@graphcommerce/magento-cart-payment-method'
3
+ import { ActionCard, useIconSvgSize } from '@graphcommerce/next-ui'
4
+ import paypalMark from '../../icons/paypal.jpg'
5
+
6
+ export function PayPalExpressActionCard(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={paypalMark}
18
+ />
19
+ }
20
+ />
21
+ )
22
+ }
@@ -0,0 +1,15 @@
1
+ mutation PayPalPaymentHandler($cartId: String!, $paymentMethod: PaymentMethodInput!) {
2
+ setPaymentMethodOnCart(input: { cart_id: $cartId, payment_method: $paymentMethod }) {
3
+ cart {
4
+ id
5
+ selected_payment_method {
6
+ ...SelectedPaymentMethod
7
+ }
8
+ }
9
+ }
10
+ placeOrder(input: { cart_id: $cartId }) {
11
+ order {
12
+ order_number
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,76 @@
1
+ import { ApolloErrorSnackbar } from '@graphcommerce/ecommerce-ui'
2
+ import { useMutation } from '@graphcommerce/graphql'
3
+ import {
4
+ useAssignCurrentCartId,
5
+ useClearCurrentCartId,
6
+ useCurrentCartId,
7
+ } from '@graphcommerce/magento-cart'
8
+ import { PaymentHandlerProps } from '@graphcommerce/magento-cart-payment-method'
9
+ import { ErrorSnackbar } from '@graphcommerce/next-ui'
10
+ import { Trans } from '@lingui/react'
11
+ import { FormControlLabel } from '@mui/material'
12
+ import { useRouter } from 'next/router'
13
+ import { useEffect } from 'react'
14
+ import { usePayPalCartLock } from '../../hooks/usePayPalCartLock'
15
+ import { PayPalPaymentHandlerDocument } from './PayPalPaymentHandler.gql'
16
+
17
+ export const PayPalPaymentHandler = (props: PaymentHandlerProps) => {
18
+ const { code } = props
19
+ const { push } = useRouter()
20
+ const [lockStatus, , unlock] = usePayPalCartLock()
21
+ const clearCurrentCartId = useClearCurrentCartId()
22
+ const { currentCartId: cartId } = useCurrentCartId()
23
+
24
+ const { token, PayerID, locked, justLocked, method } = lockStatus
25
+ const [placeOrder, { error, called }] = useMutation(PayPalPaymentHandlerDocument, {
26
+ variables: {
27
+ cartId,
28
+ paymentMethod: {
29
+ code,
30
+ paypal_express: {
31
+ payer_id: PayerID ?? '',
32
+ token: token ?? '',
33
+ },
34
+ },
35
+ },
36
+ errorPolicy: 'all',
37
+ })
38
+
39
+ useEffect(() => {
40
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
41
+ if (locked && !justLocked && method === code && !PayerID) unlock({ token: null })
42
+ }, [PayerID, code, justLocked, locked, method, unlock])
43
+
44
+ // If successfull we clear it's cart and redirect to the success page.
45
+ useEffect(() => {
46
+ if (!token || !PayerID || called) return
47
+ if (!cartId) return
48
+
49
+ const fetchData = async () => {
50
+ const res = await placeOrder()
51
+
52
+ if (res.errors) {
53
+ await unlock({ token: null, PayerID: null })
54
+ return
55
+ }
56
+
57
+ // We're done with the current cart, we clear the current cartId.
58
+ clearCurrentCartId()
59
+
60
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
61
+ push({
62
+ pathname: '/checkout/success',
63
+ query: {
64
+ cart_id: cartId,
65
+ order_number: res.data?.placeOrder?.order.order_number,
66
+ },
67
+ })
68
+ }
69
+
70
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
71
+ fetchData()
72
+ }, [PayerID, called, cartId, clearCurrentCartId, placeOrder, push, token, unlock])
73
+
74
+ if (error) return <ApolloErrorSnackbar error={error} />
75
+ return null
76
+ }
@@ -0,0 +1,23 @@
1
+ mutation PayPalPaymentOptions(
2
+ $cartId: String!
3
+ $code: String!
4
+ $expressButton: Boolean = false
5
+ $usePayPalCredit: Boolean = false
6
+ $urls: PaypalExpressUrlsInput!
7
+ ) {
8
+ createPaypalExpressToken(
9
+ input: {
10
+ cart_id: $cartId
11
+ code: $code
12
+ urls: $urls
13
+ express_button: $expressButton
14
+ use_paypal_credit: $usePayPalCredit
15
+ }
16
+ ) {
17
+ paypal_urls {
18
+ edit
19
+ start
20
+ }
21
+ token
22
+ }
23
+ }
@@ -0,0 +1,46 @@
1
+ import { useFormCompose } from '@graphcommerce/ecommerce-ui'
2
+ import { useFormGqlMutationCart } from '@graphcommerce/magento-cart'
3
+ import { PaymentOptionsProps } from '@graphcommerce/magento-cart-payment-method'
4
+ import { useRouter } from 'next/router'
5
+ import { usePayPalCartLock } from '../../hooks/usePayPalCartLock'
6
+ import { PayPalPaymentOptionsDocument } from './PayPalPaymentOptions.gql'
7
+
8
+ /** It sets the selected payment method on the cart. */
9
+ export function PayPalPaymentOptions(props: PaymentOptionsProps) {
10
+ const { code, step } = props
11
+ const [, lock] = usePayPalCartLock()
12
+ const { push } = useRouter()
13
+
14
+ const form = useFormGqlMutationCart(PayPalPaymentOptionsDocument, {
15
+ onBeforeSubmit: (variables) => ({
16
+ ...variables,
17
+ code,
18
+ urls: { cancel_url: `checkout/payment`, return_url: `checkout/payment` },
19
+ }),
20
+ onComplete: async (result) => {
21
+ if (result.errors) return
22
+
23
+ const start = result.data?.createPaypalExpressToken?.paypal_urls?.start
24
+ const token = result.data?.createPaypalExpressToken?.token
25
+
26
+ if (!start)
27
+ throw Error(
28
+ 'Error while starting the PayPal payment, please try again with a different payment method',
29
+ )
30
+
31
+ await lock({ token, method: code })
32
+ // We are going to redirect, but we're not waiting, because we need to complete the submission to release the buttons
33
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
34
+ push(start)
35
+ },
36
+ })
37
+
38
+ const submit = form.handleSubmit(() => {})
39
+ useFormCompose({ form, step, submit, key: `PaymentMethodOptions_${code}` })
40
+
41
+ return (
42
+ <form onSubmit={submit} noValidate>
43
+ <input type='hidden' value={code} {...form.register('code')} />
44
+ </form>
45
+ )
46
+ }
@@ -0,0 +1,15 @@
1
+ import { CartLockState, useCartLock } from '@graphcommerce/magento-cart-payment-method'
2
+
3
+ type PayPalLockState = CartLockState & {
4
+ token?: string | null
5
+ PayerID?: string | null
6
+ }
7
+
8
+ /**
9
+ * The cart lock situation is a bit odd since are unable to actually influence the return URL we
10
+ * can't safely remember the cart ID.
11
+ *
12
+ * This is a potential bug because when the customer is returning from an icognito session, the cart
13
+ * ID is not available.
14
+ */
15
+ export const usePayPalCartLock = () => useCartLock<PayPalLockState>()
Binary file
@@ -0,0 +1,55 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5
+ width="124px" height="33px" viewBox="0 0 124 33" enable-background="new 0 0 124 33" xml:space="preserve">
6
+ <path fill="#253B80" d="M46.211,6.749h-6.839c-0.468,0-0.866,0.34-0.939,0.802l-2.766,17.537c-0.055,0.346,0.213,0.658,0.564,0.658
7
+ h3.265c0.468,0,0.866-0.34,0.939-0.803l0.746-4.73c0.072-0.463,0.471-0.803,0.938-0.803h2.165c4.505,0,7.105-2.18,7.784-6.5
8
+ c0.306-1.89,0.013-3.375-0.872-4.415C50.224,7.353,48.5,6.749,46.211,6.749z M47,13.154c-0.374,2.454-2.249,2.454-4.062,2.454
9
+ h-1.032l0.724-4.583c0.043-0.277,0.283-0.481,0.563-0.481h0.473c1.235,0,2.4,0,3.002,0.704C47.027,11.668,47.137,12.292,47,13.154z"
10
+ />
11
+ <path fill="#253B80" d="M66.654,13.075h-3.275c-0.279,0-0.52,0.204-0.563,0.481l-0.145,0.916l-0.229-0.332
12
+ c-0.709-1.029-2.29-1.373-3.868-1.373c-3.619,0-6.71,2.741-7.312,6.586c-0.313,1.918,0.132,3.752,1.22,5.031
13
+ c0.998,1.176,2.426,1.666,4.125,1.666c2.916,0,4.533-1.875,4.533-1.875l-0.146,0.91c-0.055,0.348,0.213,0.66,0.562,0.66h2.95
14
+ c0.469,0,0.865-0.34,0.939-0.803l1.77-11.209C67.271,13.388,67.004,13.075,66.654,13.075z M62.089,19.449
15
+ c-0.316,1.871-1.801,3.127-3.695,3.127c-0.951,0-1.711-0.305-2.199-0.883c-0.484-0.574-0.668-1.391-0.514-2.301
16
+ c0.295-1.855,1.805-3.152,3.67-3.152c0.93,0,1.686,0.309,2.184,0.892C62.034,17.721,62.232,18.543,62.089,19.449z"/>
17
+ <path fill="#253B80" d="M84.096,13.075h-3.291c-0.314,0-0.609,0.156-0.787,0.417l-4.539,6.686l-1.924-6.425
18
+ c-0.121-0.402-0.492-0.678-0.912-0.678h-3.234c-0.393,0-0.666,0.384-0.541,0.754l3.625,10.638l-3.408,4.811
19
+ c-0.268,0.379,0.002,0.9,0.465,0.9h3.287c0.312,0,0.604-0.152,0.781-0.408L84.564,13.97C84.826,13.592,84.557,13.075,84.096,13.075z
20
+ "/>
21
+ <path fill="#179BD7" d="M94.992,6.749h-6.84c-0.467,0-0.865,0.34-0.938,0.802l-2.766,17.537c-0.055,0.346,0.213,0.658,0.562,0.658
22
+ h3.51c0.326,0,0.605-0.238,0.656-0.562l0.785-4.971c0.072-0.463,0.471-0.803,0.938-0.803h2.164c4.506,0,7.105-2.18,7.785-6.5
23
+ c0.307-1.89,0.012-3.375-0.873-4.415C99.004,7.353,97.281,6.749,94.992,6.749z M95.781,13.154c-0.373,2.454-2.248,2.454-4.062,2.454
24
+ h-1.031l0.725-4.583c0.043-0.277,0.281-0.481,0.562-0.481h0.473c1.234,0,2.4,0,3.002,0.704
25
+ C95.809,11.668,95.918,12.292,95.781,13.154z"/>
26
+ <path fill="#179BD7" d="M115.434,13.075h-3.273c-0.281,0-0.52,0.204-0.562,0.481l-0.145,0.916l-0.23-0.332
27
+ c-0.709-1.029-2.289-1.373-3.867-1.373c-3.619,0-6.709,2.741-7.311,6.586c-0.312,1.918,0.131,3.752,1.219,5.031
28
+ c1,1.176,2.426,1.666,4.125,1.666c2.916,0,4.533-1.875,4.533-1.875l-0.146,0.91c-0.055,0.348,0.213,0.66,0.564,0.66h2.949
29
+ c0.467,0,0.865-0.34,0.938-0.803l1.771-11.209C116.053,13.388,115.785,13.075,115.434,13.075z M110.869,19.449
30
+ c-0.314,1.871-1.801,3.127-3.695,3.127c-0.949,0-1.711-0.305-2.199-0.883c-0.484-0.574-0.666-1.391-0.514-2.301
31
+ c0.297-1.855,1.805-3.152,3.67-3.152c0.93,0,1.686,0.309,2.184,0.892C110.816,17.721,111.014,18.543,110.869,19.449z"/>
32
+ <path fill="#179BD7" d="M119.295,7.23l-2.807,17.858c-0.055,0.346,0.213,0.658,0.562,0.658h2.822c0.469,0,0.867-0.34,0.939-0.803
33
+ l2.768-17.536c0.055-0.346-0.213-0.659-0.562-0.659h-3.16C119.578,6.749,119.338,6.953,119.295,7.23z"/>
34
+ <path fill="#253B80" d="M7.266,29.154l0.523-3.322l-1.165-0.027H1.061L4.927,1.292C4.939,1.218,4.978,1.149,5.035,1.1
35
+ c0.057-0.049,0.13-0.076,0.206-0.076h9.38c3.114,0,5.263,0.648,6.385,1.927c0.526,0.6,0.861,1.227,1.023,1.917
36
+ c0.17,0.724,0.173,1.589,0.007,2.644l-0.012,0.077v0.676l0.526,0.298c0.443,0.235,0.795,0.504,1.065,0.812
37
+ c0.45,0.513,0.741,1.165,0.864,1.938c0.127,0.795,0.085,1.741-0.123,2.812c-0.24,1.232-0.628,2.305-1.152,3.183
38
+ c-0.482,0.809-1.096,1.48-1.825,2c-0.696,0.494-1.523,0.869-2.458,1.109c-0.906,0.236-1.939,0.355-3.072,0.355h-0.73
39
+ c-0.522,0-1.029,0.188-1.427,0.525c-0.399,0.344-0.663,0.814-0.744,1.328l-0.055,0.299l-0.924,5.855l-0.042,0.215
40
+ c-0.011,0.068-0.03,0.102-0.058,0.125c-0.025,0.021-0.061,0.035-0.096,0.035H7.266z"/>
41
+ <path fill="#179BD7" d="M23.048,7.667L23.048,7.667L23.048,7.667c-0.028,0.179-0.06,0.362-0.096,0.55
42
+ c-1.237,6.351-5.469,8.545-10.874,8.545H9.326c-0.661,0-1.218,0.48-1.321,1.132l0,0l0,0L6.596,26.83l-0.399,2.533
43
+ c-0.067,0.428,0.263,0.814,0.695,0.814h4.881c0.578,0,1.069-0.42,1.16-0.99l0.048-0.248l0.919-5.832l0.059-0.32
44
+ c0.09-0.572,0.582-0.992,1.16-0.992h0.73c4.729,0,8.431-1.92,9.513-7.476c0.452-2.321,0.218-4.259-0.978-5.622
45
+ C24.022,8.286,23.573,7.945,23.048,7.667z"/>
46
+ <path fill="#222D65" d="M21.754,7.151c-0.189-0.055-0.384-0.105-0.584-0.15c-0.201-0.044-0.407-0.083-0.619-0.117
47
+ c-0.742-0.12-1.555-0.177-2.426-0.177h-7.352c-0.181,0-0.353,0.041-0.507,0.115C9.927,6.985,9.675,7.306,9.614,7.699L8.05,17.605
48
+ l-0.045,0.289c0.103-0.652,0.66-1.132,1.321-1.132h2.752c5.405,0,9.637-2.195,10.874-8.545c0.037-0.188,0.068-0.371,0.096-0.55
49
+ c-0.313-0.166-0.652-0.308-1.017-0.429C21.941,7.208,21.848,7.179,21.754,7.151z"/>
50
+ <path fill="#253B80" d="M9.614,7.699c0.061-0.393,0.313-0.714,0.652-0.876c0.155-0.074,0.326-0.115,0.507-0.115h7.352
51
+ c0.871,0,1.684,0.057,2.426,0.177c0.212,0.034,0.418,0.073,0.619,0.117c0.2,0.045,0.395,0.095,0.584,0.15
52
+ c0.094,0.028,0.187,0.057,0.278,0.086c0.365,0.121,0.704,0.264,1.017,0.429c0.368-2.347-0.003-3.945-1.272-5.392
53
+ C20.378,0.682,17.853,0,14.622,0h-9.38c-0.66,0-1.223,0.48-1.325,1.133L0.01,25.898c-0.077,0.49,0.301,0.932,0.795,0.932h5.791
54
+ l1.454-9.225L9.614,7.699z"/>
55
+ </svg>
package/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { PaymentModule } from '@graphcommerce/magento-cart-payment-method'
2
+ import { PayPalExpressActionCard } from './components/PayPalPaymentActionCard/PayPalPaymentActionCard'
3
+ import { PayPalPaymentHandler } from './components/PayPalPaymentHandler/PayPalPaymentHandler'
4
+ import { PayPalPaymentOptions } from './components/PayPalPaymentOptionsAndPlaceOrder/PayPalPaymentOptions'
5
+
6
+ const mspModule: PaymentModule = {
7
+ PaymentOptions: PayPalPaymentOptions,
8
+ PaymentActionCard: PayPalExpressActionCard,
9
+ PaymentHandler: PayPalPaymentHandler,
10
+ PaymentPlaceOrder: () => null,
11
+ }
12
+
13
+ export const paypal: Record<string, PaymentModule> = {
14
+ paypal_express: mspModule,
15
+ }
package/next-env.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/types/global" />
3
+ /// <reference types="next/image-types/global" />
4
+ /// <reference types="@graphcommerce/next-ui/types" />
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@graphcommerce/magento-payment-paypal",
3
+ "homepage": "https://www.graphcommerce.org/",
4
+ "repository": "github:graphcommerce-org/graphcommerce",
5
+ "version": "1.0.2",
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
+ "devDependencies": {
15
+ "@graphcommerce/eslint-config-pwa": "^4.1.10",
16
+ "@graphcommerce/prettier-config-pwa": "^4.0.6",
17
+ "@graphcommerce/typescript-config-pwa": "^4.0.4",
18
+ "@playwright/test": "^1.21.1",
19
+ "type-fest": "^2.12.2"
20
+ },
21
+ "dependencies": {
22
+ "@graphcommerce/ecommerce-ui": "1.5.8",
23
+ "@graphcommerce/graphql": "3.5.0",
24
+ "@graphcommerce/image": "3.1.10",
25
+ "@graphcommerce/magento-cart": "4.9.5",
26
+ "@graphcommerce/magento-cart-payment-method": "3.6.9",
27
+ "@graphcommerce/magento-store": "4.3.6",
28
+ "@graphcommerce/next-ui": "4.29.3"
29
+ },
30
+ "peerDependencies": {
31
+ "@lingui/react": "^3.13.2",
32
+ "@lingui/core": "^3.13.2",
33
+ "@mui/material": "5.5.3",
34
+ "framer-motion": "^6.2.4",
35
+ "next": "^12.1.2",
36
+ "react": "^18.0.0",
37
+ "react-dom": "^18.0.0"
38
+ }
39
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "exclude": ["node_modules"],
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "extends": "@graphcommerce/typescript-config-pwa/nextjs.json"
5
+ }