@graphcommerce/magento-payment-braintree 3.0.4 → 3.0.7

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 CHANGED
@@ -1,5 +1,47 @@
1
1
  # Change Log
2
2
 
3
+ ## 3.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`3c801f45c`](https://github.com/graphcommerce-org/graphcommerce/commit/3c801f45c7df55131acf30ae2fe0d2344830d480), [`b8d04130a`](https://github.com/graphcommerce-org/graphcommerce/commit/b8d04130a1b1cb8fc85308939235140288744465), [`3192fab82`](https://github.com/graphcommerce-org/graphcommerce/commit/3192fab82560e2211dfcacadc3b0b305260527d8), [`104abd14e`](https://github.com/graphcommerce-org/graphcommerce/commit/104abd14e1585ef0d8de77937d25156b8fa1e201), [`0e425e85e`](https://github.com/graphcommerce-org/graphcommerce/commit/0e425e85ee8fed280349317ee0440c7bceea5823), [`2a125b1f9`](https://github.com/graphcommerce-org/graphcommerce/commit/2a125b1f98bb9272d96c3577f21d6c984caad892), [`8a354d1cd`](https://github.com/graphcommerce-org/graphcommerce/commit/8a354d1cd4757497ddfc9b1969a0addbc8ff616b), [`6ebe9d12d`](https://github.com/graphcommerce-org/graphcommerce/commit/6ebe9d12db9fcaa2af67a475e64a08d63e232b46)]:
8
+ - @graphcommerce/next-ui@4.6.0
9
+ - @graphcommerce/magento-cart@4.2.4
10
+ - @graphcommerce/magento-cart-payment-method@3.1.0
11
+ - @graphcommerce/react-hook-form@3.1.0
12
+ - @graphcommerce/image@3.1.4
13
+ - @graphcommerce/magento-store@4.1.6
14
+
15
+ ## 3.0.6
16
+
17
+ ### Patch Changes
18
+
19
+ - [#1378](https://github.com/graphcommerce-org/graphcommerce/pull/1378) [`b610a6e40`](https://github.com/graphcommerce-org/graphcommerce/commit/b610a6e4049e8c9e8b5d2aeff31b8e1bfc24abe5) Thanks [@paales](https://github.com/paales)! - Pin all versions internally so we can’t end up in an unfixable state for the user
20
+
21
+ - Updated dependencies [[`b610a6e40`](https://github.com/graphcommerce-org/graphcommerce/commit/b610a6e4049e8c9e8b5d2aeff31b8e1bfc24abe5), [`22ff9df16`](https://github.com/graphcommerce-org/graphcommerce/commit/22ff9df1677742ae8e07d9b7e5b12fbb487580dc)]:
22
+ - @graphcommerce/graphql@3.0.7
23
+ - @graphcommerce/image@3.1.3
24
+ - @graphcommerce/magento-cart@4.2.3
25
+ - @graphcommerce/magento-cart-payment-method@3.0.7
26
+ - @graphcommerce/magento-store@4.1.5
27
+ - @graphcommerce/next-ui@4.5.1
28
+ - @graphcommerce/react-hook-form@3.0.7
29
+
30
+ ## 3.0.5
31
+
32
+ ### Patch Changes
33
+
34
+ - [#1369](https://github.com/graphcommerce-org/graphcommerce/pull/1369) [`ae6449502`](https://github.com/graphcommerce-org/graphcommerce/commit/ae64495024a455bbe5188588604368c1542840c9) Thanks [@paales](https://github.com/paales)! - Upgraded dependencies
35
+
36
+ - Updated dependencies [[`892018809`](https://github.com/graphcommerce-org/graphcommerce/commit/8920188093d0422ec50580e408dc28ac5f93e46a), [`892018809`](https://github.com/graphcommerce-org/graphcommerce/commit/8920188093d0422ec50580e408dc28ac5f93e46a), [`ae6449502`](https://github.com/graphcommerce-org/graphcommerce/commit/ae64495024a455bbe5188588604368c1542840c9), [`892018809`](https://github.com/graphcommerce-org/graphcommerce/commit/8920188093d0422ec50580e408dc28ac5f93e46a), [`892018809`](https://github.com/graphcommerce-org/graphcommerce/commit/8920188093d0422ec50580e408dc28ac5f93e46a), [`892018809`](https://github.com/graphcommerce-org/graphcommerce/commit/8920188093d0422ec50580e408dc28ac5f93e46a)]:
37
+ - @graphcommerce/graphql@3.0.6
38
+ - @graphcommerce/next-ui@4.5.0
39
+ - @graphcommerce/image@3.1.2
40
+ - @graphcommerce/magento-cart@4.2.2
41
+ - @graphcommerce/magento-cart-payment-method@3.0.6
42
+ - @graphcommerce/magento-store@4.1.4
43
+ - @graphcommerce/react-hook-form@3.0.6
44
+
3
45
  ## 3.0.4
4
46
 
5
47
  ### Patch Changes
@@ -0,0 +1,7 @@
1
+ import { CartLockState, useCartLock } from '@graphcommerce/magento-cart-payment-method'
2
+
3
+ type BraintreeCartLock = CartLockState & {
4
+ payment_id: string | null
5
+ }
6
+
7
+ export const useBraintreeCartLock = () => useCartLock<BraintreeCartLock>()
@@ -12,7 +12,7 @@ type StartPaymentPayload = {
12
12
  type LocalPayment = {
13
13
  closeWindow(): void
14
14
  focusWindow(): void
15
- hasTokenizationParams(): void
15
+ hasTokenizationParams(): boolean
16
16
  startPayment(options: StartPaymentOptions): Promise<StartPaymentPayload>
17
17
  teardown(): Promise<void>
18
18
  tokenize(params: {
@@ -2,16 +2,9 @@
2
2
  import { useFormGqlMutationCart } from '@graphcommerce/magento-cart'
3
3
  import { PaymentOptionsProps } from '@graphcommerce/magento-cart-payment-method'
4
4
  import { useFormCompose } from '@graphcommerce/react-hook-form'
5
- // import { BraintreeError } from 'braintree-web'
6
5
  import { BraintreePaymentMethodOptionsDocument } from '../../BraintreePaymentMethodOptions.gql'
7
6
  import { useBraintreeHostedFields } from '../../hooks/useBraintreeHostedFields'
8
7
 
9
- // const errorTypes = ['CUSTOMER', 'MERCHANT', 'NETWORK', 'INTERNAL', 'UNKNOWN']
10
-
11
- // function isBraintreeError(e: any | BraintreeError): e is BraintreeError {
12
- // return errorTypes.includes((e as BraintreeError).type)
13
- // }
14
-
15
8
  /** It sets the selected payment method on the cart. */
16
9
  export function PaymentMethodOptions(props: PaymentOptionsProps) {
17
10
  const { code, step, Container } = props
@@ -0,0 +1,3 @@
1
+ export function PaymentHandler() {
2
+ return null
3
+ }
@@ -1,10 +1,63 @@
1
1
  import { useCartQuery, useFormGqlMutationCart } from '@graphcommerce/magento-cart'
2
- import { PaymentOptionsProps } from '@graphcommerce/magento-cart-payment-method'
2
+ import {
3
+ PaymentOptionsProps,
4
+ usePaymentMethodContext,
5
+ } from '@graphcommerce/magento-cart-payment-method'
3
6
  import { useFormCompose } from '@graphcommerce/react-hook-form'
7
+ import { useEffect } from 'react'
4
8
  import { BraintreePaymentMethodOptionsDocument } from '../../BraintreePaymentMethodOptions.gql'
5
9
  import { StartPaymentOptions } from '../../hooks/useBraintree'
10
+ import { useBraintreeCartLock } from '../../hooks/useBraintreeCartLock'
6
11
  import { useBraintreeLocalPayment } from '../../hooks/useBraintreeLocalPayment'
7
- import { BraintreeLocalPaymentsCartDocument } from './BraintreeLocalPaymentsCart.gql'
12
+ import { isBraintreeError } from '../../utils/isBraintreeError'
13
+ import {
14
+ BraintreeLocalPaymentsCartDocument,
15
+ BraintreeLocalPaymentsCartQuery,
16
+ } from './BraintreeLocalPaymentsCart.gql'
17
+
18
+ function validateAndBuildStartPaymentParams(cartData: BraintreeLocalPaymentsCartQuery): Partial {
19
+ const cart = cartData?.cart
20
+
21
+ const { email } = cart ?? {}
22
+ if (!email) throw Error('Please provide an email address')
23
+ const { value: amount, currency: currencyCode } = cart?.prices?.grand_total ?? {}
24
+
25
+ if (!cart?.shipping_addresses?.[0]) throw Error('Please provide a shipping method')
26
+ if (!amount || !currencyCode) throw Error('Grand total was not set')
27
+
28
+ const {
29
+ telephone: phone,
30
+ firstname: givenName,
31
+ lastname: surname,
32
+ street,
33
+ city: locality,
34
+ postcode: postalCode,
35
+ region: regionObj,
36
+ country,
37
+ } = cart?.shipping_addresses?.[0] ?? {}
38
+
39
+ const [streetAddress, ...rest] = street ?? []
40
+ const extendedAddress = rest.join('\n')
41
+ if (!streetAddress) throw Error('Please provide a street address')
42
+
43
+ const region = regionObj?.code ?? ''
44
+ if (!postalCode) throw Error('Please provide postalCode')
45
+
46
+ const { code: countryCode } = country ?? {}
47
+ if (!countryCode) throw Error('Please provide countryCode')
48
+
49
+ return {
50
+ amount: amount.toString(),
51
+
52
+ currencyCode,
53
+ shippingAddressRequired: false,
54
+ email: cart?.email ?? '',
55
+ phone,
56
+ givenName,
57
+ surname,
58
+ address: { streetAddress, extendedAddress, locality, postalCode, region, countryCode },
59
+ }
60
+ }
8
61
 
9
62
  /** It sets the selected payment method on the cart. */
10
63
  export function PaymentMethodOptions(props: PaymentOptionsProps) {
@@ -13,6 +66,24 @@ export function PaymentMethodOptions(props: PaymentOptionsProps) {
13
66
  const { code, step, child } = props
14
67
  const paymentType = child as StartPaymentOptions['paymentType']
15
68
  const { data: cartData } = useCartQuery(BraintreeLocalPaymentsCartDocument)
69
+ const [lockState, lock, unlock] = useBraintreeCartLock()
70
+ const { selectedMethod } = usePaymentMethodContext()
71
+
72
+ useEffect(() => {
73
+ if (lockState.locked && !lockState.justLocked) {
74
+ const params = unlock({ payment_id: null })
75
+
76
+ // // eslint-disable-next-line @typescript-eslint/no-floating-promises
77
+ // ;(async () => {
78
+ // const localPayment = await localPaymentPromise
79
+ // if (localPayment.hasTokenizationParams()) {
80
+ // await localPayment.tokenize(({}) => {
81
+ // // do stuff;
82
+ // })
83
+ // }
84
+ // })()
85
+ }
86
+ }, [lockState.justLocked, lockState.locked, unlock])
16
87
 
17
88
  /**
18
89
  * In the this folder you'll also find a PaymentMethodOptionsNoop.graphql document that is
@@ -21,46 +92,40 @@ export function PaymentMethodOptions(props: PaymentOptionsProps) {
21
92
  const form = useFormGqlMutationCart(BraintreePaymentMethodOptionsDocument, {
22
93
  defaultValues: { code },
23
94
  onBeforeSubmit: async () => {
24
- if (!cartData || !paymentType) throw Error('no ready')
95
+ if (!cartData?.cart?.id) throw Error('Cart id is missing')
96
+ if (!paymentType) throw Error('Could not resolve payment type')
97
+ if (!selectedMethod?.code) throw Error('Selected method not found')
98
+ const options = validateAndBuildStartPaymentParams(cartData)
25
99
 
26
- const address = cartData.cart?.shipping_addresses?.[0]
100
+ lock({ payment_id: null, method: selectedMethod?.code })
27
101
 
28
102
  const localPayment = await localPaymentPromise
29
- const paymentResult = await localPayment.startPayment({
30
- paymentType,
31
- amount: cartData.cart?.prices?.grand_total?.value?.toString() ?? '0.00',
32
- fallback: {
33
- buttonText: 'Return to website',
34
- url: window.location.href,
35
- },
36
- currencyCode: cartData.cart?.prices?.grand_total?.currency ?? 'EUR',
37
- shippingAddressRequired: false,
38
- email: cartData?.cart?.email ?? '',
39
- phone: address?.telephone ?? '',
40
- givenName: address?.firstname ?? '',
41
- surname: address?.lastname ?? '',
42
- address: {
43
- streetAddress: address?.street[0] ?? '',
44
- extendedAddress: address?.street.slice(1).join('\n') ?? '',
45
- locality: address?.city ?? '',
46
- postalCode: address?.postcode ?? '',
47
- region: address?.region?.code ?? '',
48
- countryCode: address?.country.code ?? '',
49
- },
50
- onPaymentStart: ({ paymentId }, next) => {
51
- // todo what should we do with the payment id?
52
- // eslint-disable-next-line no-console
53
- console.log(paymentId)
54
- next()
55
- },
56
- })
57
-
58
- return {
59
- cartId: cartData.cart?.id as string,
60
- deviceData: '',
61
- isTokenEnabler: false,
62
- nonce: paymentResult.nonce,
63
- code,
103
+ try {
104
+ const paymentResult = await localPayment.startPayment({
105
+ paymentType,
106
+ ...options,
107
+ fallback: {
108
+ buttonText: 'Return to website',
109
+ url: window.location.href,
110
+ },
111
+ onPaymentStart: ({ paymentId }, next) => {
112
+ lock({ payment_id: paymentId, method: selectedMethod?.code })
113
+ next()
114
+ },
115
+ })
116
+
117
+ await localPayment.teardown()
118
+
119
+ return {
120
+ cartId: cartData?.cart?.id,
121
+ deviceData: '',
122
+ isTokenEnabler: false,
123
+ nonce: paymentResult.nonce,
124
+ code,
125
+ }
126
+ } catch (e) {
127
+ if (isBraintreeError(e)) unlock({ payment_id: null })
128
+ throw e
64
129
  }
65
130
  },
66
131
  })
@@ -78,3 +143,5 @@ export function PaymentMethodOptions(props: PaymentOptionsProps) {
78
143
  </form>
79
144
  )
80
145
  }
146
+
147
+ type Partial = Omit<StartPaymentOptions, 'paymentType' | 'fallback'>
@@ -2,11 +2,13 @@ import {
2
2
  PaymentMethodPlaceOrderNoop,
3
3
  PaymentModule,
4
4
  } from '@graphcommerce/magento-cart-payment-method'
5
+ import { PaymentHandler } from './PaymentHandler'
5
6
  import { PaymentMethodOptions } from './PaymentMethodOptions'
6
7
  import { expandMethods } from './expandMethods'
7
8
 
8
9
  export const braintree_local_payment = {
9
10
  PaymentOptions: PaymentMethodOptions,
10
11
  PaymentPlaceOrder: PaymentMethodPlaceOrderNoop,
12
+ PaymentHandler,
11
13
  expandMethods,
12
14
  } as PaymentModule
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/magento-payment-braintree",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "3.0.4",
5
+ "version": "3.0.7",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -12,31 +12,31 @@
12
12
  }
13
13
  },
14
14
  "devDependencies": {
15
- "@graphcommerce/eslint-config-pwa": "^4.0.5",
16
- "@graphcommerce/magento-cart-shipping-address": "^3.0.4",
17
- "@graphcommerce/magento-product": "^4.0.6",
18
- "@graphcommerce/magento-product-configurable": "^4.0.6",
19
- "@graphcommerce/prettier-config-pwa": "^4.0.3",
15
+ "@graphcommerce/eslint-config-pwa": "^4.1.4",
16
+ "@graphcommerce/magento-cart-shipping-address": "3.0.7",
17
+ "@graphcommerce/magento-product": "4.1.5",
18
+ "@graphcommerce/magento-product-configurable": "4.0.9",
19
+ "@graphcommerce/prettier-config-pwa": "^4.0.5",
20
20
  "@graphcommerce/typescript-config-pwa": "^4.0.2",
21
- "@playwright/test": "^1.19.2",
22
- "@types/braintree-web": "^3.75.19"
21
+ "@playwright/test": "^1.20.1",
22
+ "@types/braintree-web": "^3.75.19",
23
+ "type-fest": "2.12.1"
23
24
  },
24
25
  "dependencies": {
25
- "@graphcommerce/graphql": "^3.0.4",
26
- "@graphcommerce/image": "^3.1.1",
27
- "@graphcommerce/magento-cart": "^4.1.4",
28
- "@graphcommerce/magento-cart-payment-method": "^3.0.5",
29
- "@graphcommerce/magento-store": "^4.1.2",
30
- "@graphcommerce/next-ui": "^4.2.4",
31
- "@graphcommerce/react-hook-form": "^3.0.4",
32
- "braintree-web": "^3.85.2",
33
- "type-fest": "^2.12.0"
26
+ "@graphcommerce/graphql": "3.0.7",
27
+ "@graphcommerce/image": "3.1.4",
28
+ "@graphcommerce/magento-cart": "4.2.4",
29
+ "@graphcommerce/magento-cart-payment-method": "3.1.0",
30
+ "@graphcommerce/magento-store": "4.1.6",
31
+ "@graphcommerce/next-ui": "4.6.0",
32
+ "@graphcommerce/react-hook-form": "3.1.0",
33
+ "braintree-web": "^3.85.2"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "@lingui/macro": "^3.13.2",
37
- "@mui/material": "^5.4.1",
37
+ "@mui/material": "5.5.3",
38
38
  "framer-motion": "^6.2.4",
39
- "next": "^12.0.10",
39
+ "next": "12.1.2",
40
40
  "react": "^17.0.2",
41
41
  "react-dom": "^17.0.2"
42
42
  }
@@ -1,15 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
- import { waitForGraphQlResponse } from '@graphcommerce/graphql/_playwright/apolloClient.fixture'
2
+ import { waitForGraphQlResponse } from '@graphcommerce/graphql/test/apolloClient.fixture'
3
3
  import { PaymentMethodPlaceOrderNoopDocument } from '@graphcommerce/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.gql'
4
- import { fillShippingAddressForm } from '@graphcommerce/magento-cart-shipping-address/_playwright/fillShippingAddressForm'
5
- import { addConfigurableProductToCart } from '@graphcommerce/magento-product-configurable/_playwright/addConfigurableProductToCart'
6
- import { test } from '@graphcommerce/magento-product/_playwright/productURL.fixture'
4
+ import { fillShippingAddressForm } from '@graphcommerce/magento-cart-shipping-address/test/fillShippingAddressForm'
5
+ import { addConfigurableProductToCart } from '@graphcommerce/magento-product-configurable/test/addConfigurableProductToCart'
6
+ import { test } from '@graphcommerce/magento-product/test/productURL.fixture'
7
7
  import { expect } from '@playwright/test'
8
8
 
9
9
  test('place order', async ({ page, productURL }) => {
10
10
  await addConfigurableProductToCart(page, productURL.ConfigurableProduct)
11
11
 
12
- await page.click('a:has-text("View shopping cart")')
12
+ await page.click('#view-shopping-cart-button')
13
13
 
14
14
  await page.click('a[href="/checkout"]:last-of-type')
15
15
 
@@ -19,9 +19,9 @@ test('place order', async ({ page, productURL }) => {
19
19
  await fillShippingAddressForm(page)
20
20
 
21
21
  await page.click('button[value=flatrate-flatrate]')
22
- await page.click('button:has-text("Next")')
22
+ await page.click('#next')
23
23
 
24
- await page.click('button[value=braintree___]')
24
+ await page.click('button[value=braintree]')
25
25
 
26
26
  await page.click('button:has-text("Credit Card")')
27
27
 
@@ -40,7 +40,7 @@ test('place order', async ({ page, productURL }) => {
40
40
  await expirationFrame?.fill('input[name="expiration"]', '102022')
41
41
 
42
42
  // Click button:has-text("Pay (Credit Card)")
43
- await page.click('button[name="placeOrder"]')
43
+ await page.click('#place-order')
44
44
 
45
45
  const result = await waitForGraphQlResponse(page, PaymentMethodPlaceOrderNoopDocument)
46
46
  expect(result.errors).toBeUndefined()
@@ -1,33 +1,28 @@
1
1
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
- import { waitForGraphQlResponse } from '@graphcommerce/graphql/_playwright/apolloClient.fixture'
2
+ import { waitForGraphQlResponse } from '@graphcommerce/graphql/test/apolloClient.fixture'
3
3
  import { PaymentMethodPlaceOrderNoopDocument } from '@graphcommerce/magento-cart-payment-method/PaymentMethodPlaceOrderNoop/PaymentMethodPlaceOrderNoop.gql'
4
- import { fillShippingAddressForm } from '@graphcommerce/magento-cart-shipping-address/_playwright/fillShippingAddressForm'
5
- import { addConfigurableProductToCart } from '@graphcommerce/magento-product-configurable/_playwright/addConfigurableProductToCart'
6
- import { test } from '@graphcommerce/magento-product/_playwright/productURL.fixture'
4
+ import { goToPayment } from '@graphcommerce/magento-cart-payment-method/test/goToPayment'
5
+ import { addConfigurableProductToCart } from '@graphcommerce/magento-product-configurable/test/addConfigurableProductToCart'
6
+ import { test } from '@graphcommerce/magento-product/test/productURL.fixture'
7
7
  import { expect } from '@playwright/test'
8
8
 
9
- test('place order', async ({ page, productURL }) => {
10
- await addConfigurableProductToCart(page, productURL.ConfigurableProduct)
11
-
12
- await page.click('a:has-text("View shopping cart")')
13
-
14
- await page.click('a[href="/checkout"]:last-of-type')
15
-
16
- await page.click('input[name="email"]')
17
- await page.fill('input[name="email"]', 'test@test.com')
9
+ test('place order ideal', async ({ page, productURL, apolloClient, locale }) => {
10
+ test.skip(locale !== 'nl', 'Skip test for non-nl locale')
18
11
 
19
- await fillShippingAddressForm(page)
20
-
21
- await page.click('button[value=flatrate-flatrate]')
22
- await page.click('button:has-text("Next")')
12
+ await addConfigurableProductToCart(page, productURL.ConfigurableProduct)
13
+ await goToPayment(page, apolloClient)
23
14
 
24
15
  await page.click('button[value=braintree_local_payment___ideal]')
25
16
 
17
+ await page.pause()
18
+
26
19
  const [braintreePopup] = await Promise.all([
27
20
  page.waitForEvent('popup'),
28
- page.click('button[name="placeOrder"]'),
21
+ page.click('#place-order'),
29
22
  ])
30
23
 
24
+ await page.pause()
25
+
31
26
  await braintreePopup.click('text=Proceed with Sandbox Purchase')
32
27
  const result = await waitForGraphQlResponse(page, PaymentMethodPlaceOrderNoopDocument)
33
28
  expect(result.errors).toBeUndefined()
@@ -0,0 +1,11 @@
1
+ import { BraintreeError } from 'braintree-web'
2
+
3
+ const errorTypes = ['CUSTOMER', 'MERCHANT', 'NETWORK', 'INTERNAL', 'UNKNOWN']
4
+
5
+ export function isBraintreeError(e: unknown): e is BraintreeError {
6
+ return errorTypes.includes((e as BraintreeError).type) && e instanceof Error
7
+ }
8
+
9
+ export function isBraintreeCustomerError(e: unknown): e is BraintreeError {
10
+ return isBraintreeError(e) && e.type === 'CUSTOMER'
11
+ }