简体   繁体   中英

Adding complete Stripe payment requirement to form

I am creating a platform, where friends can buy friends Starbucks.

Users create a request & other users can fulfill these requests by paying $5, then the requesting user receives a Starbucks gift card of $5.

Right now the only requirement to fulfill a request is the message text form (submitting without will throw an error) & I need to add the requirement of completing the Stripe payment before submitting the fulfill request form.

Ideally, I would like the fulfill the request button to be disabled until the Stripe payment is complete.

Fulfill Request form:

import { connect } from 'react-redux';
import { fulfillRequest, clearErrors } from 'redux/actions/dataActions';

import { Elements } from 'react-stripe-elements';
import CheckoutForm from '../FulfillRequest/Stripe/CheckoutForm';

class FulfillRequest extends Component {
  state = {
    open: false,
    body: '',
    errors: {}
  };

  componentWillReceiveProps(nextProps) {
    if (nextProps.UI.errors) {
      this.setState({
        errors: nextProps.UI.errors
      });
    }
    if (!nextProps.UI.errors && !nextProps.UI.loading) {
      this.setState({ body: '', open: false, errors: {} });
    }
  }

  handleOpen = () => {
    this.setState({ open: true });
  };
  handleClose = () => {
    this.props.clearErrors();
    this.setState({ open: false, errors: {} });
  };
  handleChange = event => {
    this.setState({ [event.target.name]: event.target.value });
  };
  handleSubmit = event => {
    event.preventDefault();
    this.props.fulfillRequest(this.props.requestId, { body: this.state.body });
  };
  render() {
    const {
      classes,
      UI: { loading }
    } = this.props;
    const { errors } = this.state;

    return (
      <Fragment>
        <IconButton onClick={this.handleOpen} color="inherit">
          <TagFaces />
        </IconButton>

        <Dialog
          open={this.state.open}
          onClose={this.handleClose}
          aria-labelledby="form-dialog-title"
          fullWidth
          maxWidth="sm">
          <DialogTitle id="form-dialog-title">Fulfill request</DialogTitle>
          <DialogContent>

            <Elements>
              <CheckoutForm />
            </Elements>

            <Grid
              container
              spacing={0}
              direction="column"
              alignItems="center"
              justify="center">
              {errors.error && (
                <FormHelperText error className={classes.customError}>
                  {errors.error}
                </FormHelperText>
              )}
            </Grid>
            <form onSubmit={this.handleSubmit}>
              <TextField
                autoFocus
                margin="dense"
                multiline
                placeholder="say something"
                id="name"
                name="body"
                label="say something"
                type="text"
                fullWidth
                error={errors.body ? true : false}
                helperText={errors.body}
                onChange={this.handleChange}
              />
              <DialogActions>
                <Button onClick={this.handleClose} color="primary">
                  Cancel
                </Button>
                <Button type="submit" color="primary" disabled={loading}>
                  Fulfill Request
                  {loading && <CircularProgress />}
                </Button>
              </DialogActions>
            </form>
          </DialogContent>
        </Dialog>
      </Fragment>
    );
  }
}

FulfillRequest.propTypes = {
  fulfillRequest: PropTypes.func.isRequired,
  classes: PropTypes.object.isRequired,
  requestId: PropTypes.string.isRequired,
  clearErrors: PropTypes.func.isRequired,
  UI: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  UI: state.UI
});

export default connect(
  mapStateToProps,
  { fulfillRequest, clearErrors }
)(withStyles(styles)(FulfillRequest));

Stripe Element:

class CheckoutForm extends Component {
  constructor(props) {
    super(props);
    this.state = { complete: false };
    this.submit = this.submit.bind(this);
  }

  async submit(ev) {
    let { token } = await this.props.stripe.createToken({ name: 'Name' });
    let response = await fetch(
      'https://us-east1-foodmigo-v01-101.cloudfunctions.net/api/charge',
      {
        method: 'POST',
        headers: { 'Content-Type': 'text/plain' },
        body: token.id
      }
    );
    if (response.ok) this.setState({ complete: true });
  }

  render() {
    if (this.state.complete) return <h1>Purchase Complete</h1>;
    return (
      <div className="checkout">
        <p>Would you like to be a hero?</p>
        <CardElement />

        <Button variant="contained" color="secondary" onClick={this.submit}>
          Send
        </Button>
      </div>
    );
  }
}

export default injectStripe(CheckoutForm);

Not sure how your Button component is composed, but couldn't you just pass it a prop of disabled={!this.state.complete} ?

EDIT: Misunderstood the structure of the components. That will still work though, however you'll need to raise the state indicating completion up out of your Form component into the FulfillRequest component so it can access it. Your state for FulfillRequest should look something like:

state = {
    open: false,
    body: '',
    errors: {},
    complete: false,
  };

Then you can create a function which sets the value in that parent and pass it to the child form:

  setComplete = (val) => {
    this.setState({
      complete: val
    })
  }

...

<CheckoutForm setComplete={this.setComplete} complete={this.state.complete}/>

Then call the passed function to set the complete state when the form resolves with success:

  async submit(ev) {
    let { token } = await this.props.stripe.createToken({ name: 'Name' });
    let response = await fetch(
      'https://us-east1-foodmigo-v01-101.cloudfunctions.net/api/charge',
      {
        method: 'POST',
        headers: { 'Content-Type': 'text/plain' },
        body: token.id
      }
    );
    if (response.ok) this.props.setComplete(true);
  }

Lastly, set the disabled prop on your button in the parent based on the result (and replace any other instances of this.state.complete with the complete prop in the CheckoutForm as well, if necessary):

<Button type="submit" color="primary" disabled={loading || !this.state.complete}>
                  Fulfill Request
                  {loading && <CircularProgress />}
                </Button>

Can't test this but it should work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM