From 381b6d8191ebe2661e1ad8effd49f9deae1bac83 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Thu, 22 Nov 2018 12:29:51 +0200 Subject: [PATCH 1/3] Show Stripe error message if error contains Stripe error --- src/containers/CheckoutPage/CheckoutPage.js | 14 ++++++++++++++ src/translations/en.json | 1 + src/util/errors.js | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/containers/CheckoutPage/CheckoutPage.js b/src/containers/CheckoutPage/CheckoutPage.js index a1d2597d..4b35a1f8 100644 --- a/src/containers/CheckoutPage/CheckoutPage.js +++ b/src/containers/CheckoutPage/CheckoutPage.js @@ -15,6 +15,7 @@ import { isTransactionInitiateAmountTooLowError, isTransactionInitiateListingNotFoundError, isTransactionInitiateMissingStripeAccountError, + transactionInitiateOrderStripeErrors, isTransactionInitiateBookingTimeNotAvailableError, } from '../../util/errors'; import { @@ -273,6 +274,7 @@ export class CheckoutPageComponent extends Component { const isBookingTimeNotAvailableError = isTransactionInitiateBookingTimeNotAvailableError( initiateOrderError ); + const stripeErrors = transactionInitiateOrderStripeErrors(initiateOrderError); let initiateOrderErrorMessage = null; @@ -288,6 +290,18 @@ export class CheckoutPageComponent extends Component {

); + } else if (!listingNotFound && stripeErrors && stripeErrors.length > 0) { + // NOTE: Error messages from Stripes are not part of translations. + // By default they are in English. + const stripeErrorsAsString = stripeErrors.join(', '); + initiateOrderErrorMessage = ( +

+ +

+ ); } else if (!listingNotFound && initiateOrderError) { initiateOrderErrorMessage = (

diff --git a/src/translations/en.json b/src/translations/en.json index d537de3d..1ebd6b19 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -73,6 +73,7 @@ "CheckoutPage.hostedBy": "Hosted by {name}", "CheckoutPage.initiateOrderAmountTooLow": "We are unable to process this payment since the payment amount is too low. Please contact the marketplace administrator.", "CheckoutPage.initiateOrderError": "Payment request failed. Please go back to {listingLink} and try again. If the error persists, try refreshing the page or contacting the marketplace administrator.", + "CheckoutPage.initiateOrderStripeError": "The payment processor gave the following errors: {stripeErrors}", "CheckoutPage.listingNotFoundError": "Unfortunately, the listing is no longer available.", "CheckoutPage.loadingData": "Loading checkout data…", "CheckoutPage.paymentInfo": "You'll only be charged if your request is accepted by the provider.", diff --git a/src/util/errors.js b/src/util/errors.js index 732d52bd..f60fd02c 100644 --- a/src/util/errors.js +++ b/src/util/errors.js @@ -136,6 +136,24 @@ export const isTransactionInitiateAmountTooLowError = error => { }); }; +/** + * Check if the given API error (from `sdk.transaction.initiate()`) is + * due to other error in Stripe. + */ +export const transactionInitiateOrderStripeErrors = error => { + if (error) { + return errorAPIErrors(error).reduce((messages, apiError) => { + const isPaymentFailedError = + apiError.status === 402 && apiError.code === ERROR_CODE_PAYMENT_FAILED; + const hasStripeError = apiError && apiError.meta && apiError.meta.stripeMessage; + const stripeMessageMaybe = + isPaymentFailedError && hasStripeError ? [apiError.meta.stripeMessage] : []; + return [...messages, ...stripeMessageMaybe]; + }, []); + } + return null; +}; + /** * Check if the given API error (from `sdk.transactions.transition(id, transition, params)`) * is due to invalid transition attempt. From 299358f1d265bb86ca947de435784635a736d204 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Fri, 23 Nov 2018 12:53:20 +0200 Subject: [PATCH 2/3] Show error if payment amount is zero --- src/containers/CheckoutPage/CheckoutPage.js | 9 ++++++++- src/util/errors.js | 14 +++++++++++++- src/util/types.js | 4 ++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/containers/CheckoutPage/CheckoutPage.js b/src/containers/CheckoutPage/CheckoutPage.js index 4b35a1f8..abbb57d5 100644 --- a/src/containers/CheckoutPage/CheckoutPage.js +++ b/src/containers/CheckoutPage/CheckoutPage.js @@ -15,8 +15,9 @@ import { isTransactionInitiateAmountTooLowError, isTransactionInitiateListingNotFoundError, isTransactionInitiateMissingStripeAccountError, - transactionInitiateOrderStripeErrors, isTransactionInitiateBookingTimeNotAvailableError, + isTransactionZeroPaymentError, + transactionInitiateOrderStripeErrors, } from '../../util/errors'; import { AvatarMedium, @@ -329,6 +330,12 @@ export class CheckoutPageComponent extends Component {

); + } else if (isTransactionZeroPaymentError(speculateTransactionError)) { + speculateErrorMessage = ( +

+ +

+ ); } else if (speculateTransactionError) { speculateErrorMessage = (

diff --git a/src/util/errors.js b/src/util/errors.js index f60fd02c..903bfddc 100644 --- a/src/util/errors.js +++ b/src/util/errors.js @@ -15,6 +15,7 @@ import { ERROR_CODE_TRANSACTION_ALREADY_REVIEWED_BY_CUSTOMER, ERROR_CODE_TRANSACTION_ALREADY_REVIEWED_BY_PROVIDER, ERROR_CODE_PAYMENT_FAILED, + ERROR_CODE_CHARGE_ZERO_PAYIN, ERROR_CODE_EMAIL_TAKEN, ERROR_CODE_EMAIL_NOT_FOUND, ERROR_CODE_EMAIL_NOT_VERIFIED, @@ -111,12 +112,21 @@ export const isTransactionInitiateMissingStripeAccountError = error => export const isTransactionInitiateBookingTimeNotAvailableError = error => hasErrorWithCode(error, ERROR_CODE_TRANSACTION_BOOKING_TIME_NOT_AVAILABLE); +/** + * Check if the given API error (from `sdk.transaction.initiate()` or + * `sdk.transaction.initiateSpeculative()`) is due to payment being zero. + */ +export const isTransactionZeroPaymentError = error => + hasErrorWithCode(error, ERROR_CODE_CHARGE_ZERO_PAYIN); + /** * Check if the given API error (from `sdk.transaction.initiate()`) is * due to the transaction total amount being too low for Stripe. */ export const isTransactionInitiateAmountTooLowError = error => { - return responseAPIErrors(error).some(apiError => { + const isZeroPayment = isTransactionZeroPaymentError(error); + + const tooLowAmount = errorAPIErrors(error).some(apiError => { const isPaymentFailedError = apiError.status === 402 && apiError.code === ERROR_CODE_PAYMENT_FAILED; let isAmountTooLow = false; @@ -134,6 +144,8 @@ export const isTransactionInitiateAmountTooLowError = error => { return isPaymentFailedError && isAmountTooLow; }); + + return isZeroPayment || tooLowAmount; }; /** diff --git a/src/util/types.js b/src/util/types.js index 2c595e4b..0d1f4d65 100644 --- a/src/util/types.js +++ b/src/util/types.js @@ -446,6 +446,8 @@ export const ERROR_CODE_TRANSACTION_ALREADY_REVIEWED_BY_PROVIDER = export const ERROR_CODE_TRANSACTION_BOOKING_TIME_NOT_AVAILABLE = 'transaction-booking-time-not-available'; export const ERROR_CODE_PAYMENT_FAILED = 'transaction-payment-failed'; +export const ERROR_CODE_CHARGE_ZERO_PAYIN = 'transaction-charge-zero-payin'; +export const ERROR_CODE_CHARGE_ZERO_PAYOUT = 'transaction-charge-zero-payout'; export const ERROR_CODE_EMAIL_TAKEN = 'email-taken'; export const ERROR_CODE_EMAIL_NOT_FOUND = 'email-not-found'; export const ERROR_CODE_EMAIL_NOT_VERIFIED = 'email-unverified'; @@ -463,6 +465,8 @@ const ERROR_CODES = [ ERROR_CODE_TRANSACTION_ALREADY_REVIEWED_BY_CUSTOMER, ERROR_CODE_TRANSACTION_ALREADY_REVIEWED_BY_PROVIDER, ERROR_CODE_PAYMENT_FAILED, + ERROR_CODE_CHARGE_ZERO_PAYIN, + ERROR_CODE_CHARGE_ZERO_PAYOUT, ERROR_CODE_EMAIL_TAKEN, ERROR_CODE_EMAIL_NOT_FOUND, ERROR_CODE_EMAIL_NOT_VERIFIED, From 24eeb90d48deccdb34d24159941ecfb521aefef8 Mon Sep 17 00:00:00 2001 From: Jenni Nurmi Date: Thu, 22 Nov 2018 12:41:04 +0200 Subject: [PATCH 3/3] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 870f79b0..e2cacf6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ way to update this template, but currently, we follow a pattern: ## Upcoming version 2018-11-XX +* [fix] Show Stripe error message on CheckoutPage if payment request fails because of Stripe error. + Also show error if payment amount is zero. + [#960](https://github.com/sharetribe/flex-template-web/pull/960) * [fix] Remove unused translation keys and update PasswordChangePage title [#959](https://github.com/sharetribe/flex-template-web/pull/959)