Skip to content

Commit

Permalink
Merge pull request #960 from sharetribe/fix-initial-order-error-bug
Browse files Browse the repository at this point in the history
Show Stripe error message if error contains Stripe error
  • Loading branch information
OtterleyW authored Nov 26, 2018
2 parents 5425211 + 24eeb90 commit f485a5e
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
21 changes: 21 additions & 0 deletions src/containers/CheckoutPage/CheckoutPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
isTransactionInitiateListingNotFoundError,
isTransactionInitiateMissingStripeAccountError,
isTransactionInitiateBookingTimeNotAvailableError,
isTransactionZeroPaymentError,
transactionInitiateOrderStripeErrors,
} from '../../util/errors';
import {
AvatarMedium,
Expand Down Expand Up @@ -273,6 +275,7 @@ export class CheckoutPageComponent extends Component {
const isBookingTimeNotAvailableError = isTransactionInitiateBookingTimeNotAvailableError(
initiateOrderError
);
const stripeErrors = transactionInitiateOrderStripeErrors(initiateOrderError);

let initiateOrderErrorMessage = null;

Expand All @@ -288,6 +291,18 @@ export class CheckoutPageComponent extends Component {
<FormattedMessage id="CheckoutPage.bookingTimeNotAvailableMessage" />
</p>
);
} 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 = (
<p className={css.orderError}>
<FormattedMessage
id="CheckoutPage.initiateOrderStripeError"
values={{ stripeErrors: stripeErrorsAsString }}
/>
</p>
);
} else if (!listingNotFound && initiateOrderError) {
initiateOrderErrorMessage = (
<p className={css.orderError}>
Expand Down Expand Up @@ -315,6 +330,12 @@ export class CheckoutPageComponent extends Component {
<FormattedMessage id="CheckoutPage.bookingTimeNotAvailableMessage" />
</p>
);
} else if (isTransactionZeroPaymentError(speculateTransactionError)) {
speculateErrorMessage = (
<p className={css.orderError}>
<FormattedMessage id="CheckoutPage.initiateOrderAmountTooLow" />
</p>
);
} else if (speculateTransactionError) {
speculateErrorMessage = (
<p className={css.orderError}>
Expand Down
1 change: 1 addition & 0 deletions src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
32 changes: 31 additions & 1 deletion src/util/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -134,6 +144,26 @@ export const isTransactionInitiateAmountTooLowError = error => {

return isPaymentFailedError && isAmountTooLow;
});

return isZeroPayment || tooLowAmount;
};

/**
* 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;
};

/**
Expand Down
4 changes: 4 additions & 0 deletions src/util/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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,
Expand Down

0 comments on commit f485a5e

Please sign in to comment.