From e9b8acb575019596c93a8810f69d65522b2e080a Mon Sep 17 00:00:00 2001 From: bc-nick Date: Thu, 22 Aug 2024 13:01:03 +0200 Subject: [PATCH 1/2] feat(payment): PAYPAL-4510 POC --- ...raintree-local-methods-payment-strategy.ts | 75 ++++++++++++++++++- packages/braintree-utils/src/types.ts | 23 ++++++ 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/packages/braintree-integration/src/braintree-local-payment-methods/braintree-local-methods-payment-strategy.ts b/packages/braintree-integration/src/braintree-local-payment-methods/braintree-local-methods-payment-strategy.ts index 7ae6c452b4..54b48055cd 100644 --- a/packages/braintree-integration/src/braintree-local-payment-methods/braintree-local-methods-payment-strategy.ts +++ b/packages/braintree-integration/src/braintree-local-payment-methods/braintree-local-methods-payment-strategy.ts @@ -1,8 +1,9 @@ import { BraintreeInitializationData, - BraintreeIntegrationService, + BraintreeIntegrationService, BraintreeLocalPaymentMethodRedirectAction, LocalPaymentInstance, LocalPaymentsPayload, + NonInstantLocalPaymentMethods, onPaymentStartData, StartPaymentError, } from '@bigcommerce/checkout-sdk/braintree-utils'; @@ -68,9 +69,14 @@ export default class BraintreeLocalMethodsPaymentStrategy implements PaymentStra await this.paymentIntegrationService.loadPaymentMethod(gatewayId); const state = this.paymentIntegrationService.getState(); - const storeConfig = state.getStoreConfigOrThrow(); const paymentMethod = state.getPaymentMethodOrThrow(gatewayId); + + if (this.isNonInstantPaymentMethod(paymentMethod.method)) { + return; + } + const { clientToken, config, initializationData } = paymentMethod; + const storeConfig = state.getStoreConfigOrThrow(); if (!clientToken || !initializationData) { throw new MissingDataError(MissingDataErrorType.MissingPaymentMethod); @@ -104,7 +110,7 @@ export default class BraintreeLocalMethodsPaymentStrategy implements PaymentStra const cart = state.getCartOrThrow(); const sessionId = await this.braintreeIntegrationService.getSessionId(); const billing = state.getBillingAddressOrThrow(); - const { firstName, lastName, countryCode } = billing; + const { firstName, lastName, countryCode, phone } = billing; const { currency, email, lineItems } = cart; const isShippingRequired = lineItems.physicalItems.length > 0; const grandTotal = state.getCheckoutOrThrow().outstandingBalance; @@ -115,6 +121,43 @@ export default class BraintreeLocalMethodsPaymentStrategy implements PaymentStra this.toggleLoadingIndicator(true); + if (this.isNonInstantPaymentMethod(payment.methodId)) { + const { methodId } = payment; + const paymentData = { + formattedPayload: { + vault_payment_instrument: null, + set_as_default_stored_instrument: null, + device_info: sessionId || null, + method_id: methodId, + [`${payment.methodId}_account`]: { + // take phone number from paymentData + phone + }, + }, + }; + + await this.paymentIntegrationService.submitOrder(); + + try { + await this.paymentIntegrationService.submitPayment({ + methodId, + paymentData, + }); + } catch (error) { + if (this.isBraintreeTrustlyRedirectError(error)) { + let redirectUrl = error.body.additional_action_required.data.redirect_url; + + return new Promise(() => window.location.replace(redirectUrl)); + } + + this.toggleLoadingIndicator(false); + + return Promise.reject(error); + } + + return; + } + if (!this.localPaymentInstance) { throw new PaymentMethodInvalidError(); } @@ -216,4 +259,30 @@ export default class BraintreeLocalMethodsPaymentStrategy implements PaymentStra onError(error); } } + + /** + * + * Utils + * + * */ + private isNonInstantPaymentMethod(methodId: string): boolean { + return methodId.toUpperCase() in NonInstantLocalPaymentMethods; + } + + private isBraintreeTrustlyRedirectError(error: unknown) { + if (typeof error !== 'object' || error === null) { + return false; + } + + const { body }: Partial = error; + + if (!body) { + return false; + } + + return ( + body.status === 'additional_action_required' && + !!body.additional_action_required?.data.redirect_url + ); + } } diff --git a/packages/braintree-utils/src/types.ts b/packages/braintree-utils/src/types.ts index c16951adf3..aed487ac5e 100644 --- a/packages/braintree-utils/src/types.ts +++ b/packages/braintree-utils/src/types.ts @@ -472,3 +472,26 @@ export interface BraintreeError extends Error { code: string | BraintreeErrorCode.KountNotEnabled; details?: unknown; } + +/** + * + * Braintree non-instant payment methods + * + */ + +export enum NonInstantLocalPaymentMethods { + TRUSTLY = 'trustly' +} + +export interface BraintreeLocalPaymentMethodRedirectAction { + body: { + additional_action_required: { + type: 'offsite_redirect'; + data: { + redirect_url: string; + }; + }; + status: string; + provider_data?: string; + }; +} From 62cd9ca5a42ff9e1332dd47162f484e062cec726 Mon Sep 17 00:00:00 2001 From: Serhii Tkachenko Date: Thu, 5 Sep 2024 13:50:04 +0300 Subject: [PATCH 2/2] fix(payment): PAYPAL-4510 fixed the issue with error handling to perform additional action in Braintree Trustly payment strategy --- ...raintree-local-methods-payment-strategy.ts | 29 +++++-------------- packages/braintree-utils/src/types.ts | 13 --------- 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/packages/braintree-integration/src/braintree-local-payment-methods/braintree-local-methods-payment-strategy.ts b/packages/braintree-integration/src/braintree-local-payment-methods/braintree-local-methods-payment-strategy.ts index 54b48055cd..84ef2fb579 100644 --- a/packages/braintree-integration/src/braintree-local-payment-methods/braintree-local-methods-payment-strategy.ts +++ b/packages/braintree-integration/src/braintree-local-payment-methods/braintree-local-methods-payment-strategy.ts @@ -1,6 +1,6 @@ import { BraintreeInitializationData, - BraintreeIntegrationService, BraintreeLocalPaymentMethodRedirectAction, + BraintreeIntegrationService, LocalPaymentInstance, LocalPaymentsPayload, NonInstantLocalPaymentMethods, @@ -19,6 +19,7 @@ import { PaymentMethodInvalidError, PaymentRequestOptions, PaymentStrategy, + isRequestError, } from '@bigcommerce/checkout-sdk/payment-integration-api'; import { LoadingIndicator } from '@bigcommerce/checkout-sdk/ui'; @@ -136,18 +137,19 @@ export default class BraintreeLocalMethodsPaymentStrategy implements PaymentStra }, }; - await this.paymentIntegrationService.submitOrder(); - try { + await this.paymentIntegrationService.submitOrder(); await this.paymentIntegrationService.submitPayment({ methodId, paymentData, }); } catch (error) { - if (this.isBraintreeTrustlyRedirectError(error)) { + if (isRequestError(error)) { let redirectUrl = error.body.additional_action_required.data.redirect_url; - return new Promise(() => window.location.replace(redirectUrl)); + if (redirectUrl) { + return new Promise(() => window.location.replace(redirectUrl)); + } } this.toggleLoadingIndicator(false); @@ -268,21 +270,4 @@ export default class BraintreeLocalMethodsPaymentStrategy implements PaymentStra private isNonInstantPaymentMethod(methodId: string): boolean { return methodId.toUpperCase() in NonInstantLocalPaymentMethods; } - - private isBraintreeTrustlyRedirectError(error: unknown) { - if (typeof error !== 'object' || error === null) { - return false; - } - - const { body }: Partial = error; - - if (!body) { - return false; - } - - return ( - body.status === 'additional_action_required' && - !!body.additional_action_required?.data.redirect_url - ); - } } diff --git a/packages/braintree-utils/src/types.ts b/packages/braintree-utils/src/types.ts index aed487ac5e..b8cc549280 100644 --- a/packages/braintree-utils/src/types.ts +++ b/packages/braintree-utils/src/types.ts @@ -482,16 +482,3 @@ export interface BraintreeError extends Error { export enum NonInstantLocalPaymentMethods { TRUSTLY = 'trustly' } - -export interface BraintreeLocalPaymentMethodRedirectAction { - body: { - additional_action_required: { - type: 'offsite_redirect'; - data: { - redirect_url: string; - }; - }; - status: string; - provider_data?: string; - }; -}