Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CheckoutPage: submit button was enabled prematurely for onetime payments #111

Merged
merged 3 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 2021-XX-XX

- [fix] CheckoutPage: don't enable submit button when selecting onetime payment. In addition, pass
payment method id to stripe when re-trying with saved payment method after onetime payment.
[#111](https://github.com/sharetribe/ftw-product/pull/111)
- [fix] searchMode is only relevant for multi-enum schema.
[112](https://github.com/sharetribe/ftw-product/pull/112)
- [fix] Fix "Out of Stock" UI flash before current stock is loaded
Expand Down
7 changes: 3 additions & 4 deletions src/containers/CheckoutPage/CheckoutPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,8 @@ export class CheckoutPageComponent extends Component {
const { stripe, card, billingDetails, paymentIntent } = handlePaymentParams;
const stripeElementMaybe = selectedPaymentFlow !== USE_SAVED_CARD ? { card } : {};

// Note: payment_method could be set here for USE_SAVED_CARD flow.
// { payment_method: stripePaymentMethodId }
// However, we have set it already on API side, when PaymentIntent was created.
// Note: For basic USE_SAVED_CARD scenario, we have set it already on API side, when PaymentIntent was created.
// However, the payment_method is save here for USE_SAVED_CARD flow if customer first attempted onetime payment
const paymentParams =
selectedPaymentFlow !== USE_SAVED_CARD
? {
Expand All @@ -407,7 +406,7 @@ export class CheckoutPageComponent extends Component {
card: card,
},
}
: {};
: { payment_method: stripePaymentMethodId };

const params = {
stripePaymentIntentClientSecret,
Expand Down
58 changes: 45 additions & 13 deletions src/containers/CheckoutPage/StripePaymentForm/StripePaymentForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,30 @@ const getPaymentMethod = (selectedPaymentMethod, hasDefaultPaymentMethod) => {
: selectedPaymentMethod;
};

// Should we show onetime payment fields and does StripeElements card need attention
const checkOnetimePaymentFields = (
cardValueValid,
selectedPaymentMethod,
hasDefaultPaymentMethod,
hasHandledCardPayment
) => {
const useDefaultPaymentMethod =
selectedPaymentMethod === 'defaultCard' && hasDefaultPaymentMethod;
// Billing details are known if we have already handled card payment or existing default payment method is used.
const billingDetailsKnown = hasHandledCardPayment || useDefaultPaymentMethod;

// If onetime payment is used, check that the StripeElements card has valid value.
const oneTimePaymentMethods = ['onetimeCardPayment', 'replaceCard'];
const useOnetimePaymentMethod = oneTimePaymentMethods.includes(selectedPaymentMethod);
const onetimePaymentNeedsAttention =
!billingDetailsKnown && !(useOnetimePaymentMethod && cardValueValid);

return {
onetimePaymentNeedsAttention,
showOnetimePaymentFields: useOnetimePaymentMethod,
};
};

const initialState = {
error: null,
cardValueValid: false,
Expand Down Expand Up @@ -276,6 +300,7 @@ class StripePaymentForm extends Component {
this.card.removeEventListener('change', this.handleCardValueChange);
this.card.unmount();
this.card = null;
this.setState({ cardValueValid: false });
}
this.setState({ paymentMethod: changedTo });
if (changedTo === 'defaultCard' && this.finalFormAPI) {
Expand Down Expand Up @@ -319,8 +344,14 @@ class StripePaymentForm extends Component {
} = this.props;
const { initialMessage } = values;
const { cardValueValid, paymentMethod } = this.state;
const billingDetailsKnown = hasHandledCardPayment || defaultPaymentMethod;
const onetimePaymentNeedsAttention = !billingDetailsKnown && !cardValueValid;
const hasDefaultPaymentMethod = defaultPaymentMethod.id;
const selectedPaymentMethod = getPaymentMethod(paymentMethod, hasDefaultPaymentMethod);
const { onetimePaymentNeedsAttention } = checkOnetimePaymentFields(
cardValueValid,
selectedPaymentMethod,
hasDefaultPaymentMethod,
hasHandledCardPayment
);

if (inProgress || onetimePaymentNeedsAttention) {
// Already submitting or card value incomplete/invalid
Expand Down Expand Up @@ -369,8 +400,17 @@ class StripePaymentForm extends Component {

const ensuredDefaultPaymentMethod = ensurePaymentMethodCard(defaultPaymentMethod);
const billingDetailsNeeded = !(hasHandledCardPayment || confirmPaymentError);
const billingDetailsKnown = hasHandledCardPayment || ensuredDefaultPaymentMethod;
const onetimePaymentNeedsAttention = !billingDetailsKnown && !this.state.cardValueValid;

const { cardValueValid, paymentMethod } = this.state;
const hasDefaultPaymentMethod = ensuredDefaultPaymentMethod.id;
const selectedPaymentMethod = getPaymentMethod(paymentMethod, hasDefaultPaymentMethod);
const { onetimePaymentNeedsAttention, showOnetimePaymentFields } = checkOnetimePaymentFields(
cardValueValid,
selectedPaymentMethod,
hasDefaultPaymentMethod,
hasHandledCardPayment
);

const submitDisabled = invalid || onetimePaymentNeedsAttention || submitInProgress;
const hasCardError = this.state.error && !submitInProgress;
const hasPaymentErrors = confirmCardPaymentError || confirmPaymentError;
Expand Down Expand Up @@ -443,14 +483,6 @@ class StripePaymentForm extends Component {
);

const hasStripeKey = config.stripe.publishableKey;
const showPaymentMethodSelector = ensuredDefaultPaymentMethod.id;
const selectedPaymentMethod = getPaymentMethod(
this.state.paymentMethod,
showPaymentMethodSelector
);
const showOnetimePaymentFields = ['onetimeCardPayment', 'replaceCard'].includes(
selectedPaymentMethod
);

const handleSameAddressCheckbox = event => {
const checked = event.target.checked;
Expand All @@ -462,7 +494,7 @@ class StripePaymentForm extends Component {
{shippingOrPickupDetails}
{billingDetailsNeeded && !loadingData ? (
<React.Fragment>
{showPaymentMethodSelector ? (
{hasDefaultPaymentMethod ? (
<PaymentMethodSelector
cardClasses={cardClasses}
formId={formId}
Expand Down