Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/billing/add-payment-method.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Button, ButtonToolbar, Row, Col, Well } from "../antd-bootstrap";6import { Component, Rendered, redux } from "../app-framework";7import { ErrorDisplay, Loading } from "../components";8import { HelpEmailLink } from "../customize";9import { powered_by_stripe } from "./util";10import { loadStripe, StripeCard } from "./stripe";1112interface Props {13on_close?: Function; // optionally called when this should be closed14hide_cancel_button?: boolean;15}1617const CARD_STYLE = {18margin: "15px",19border: "1px solid grey",20padding: "30px",21background: "white",22borderRadius: "5px",23};2425interface State {26submitting: boolean;27error: string;28loading: boolean;29}3031export class AddPaymentMethod extends Component<Props, State> {32private mounted: boolean = false;33private card?: StripeCard;3435constructor(props, state) {36super(props, state);37this.state = {38submitting: false,39error: "",40loading: true,41};42}4344public async componentDidMount(): Promise<void> {45this.mounted = true;46const stripe = await loadStripe();47if (!this.mounted) return;48this.setState({ loading: false });49const elements = stripe.elements();50this.card = elements.create("card");51if (this.card == null) throw Error("bug -- card cannot be null");52this.card.mount("#card-element");53}5455public componentWillUnmount(): void {56this.mounted = false;57}5859private async submit_payment_method(): Promise<void> {60this.setState({ error: "", submitting: true });61const actions = redux.getActions("billing");62const store = redux.getStore("billing");63if (store.get("customer") == null) {64actions.setState({ continue_first_purchase: true });65}66const stripe = await loadStripe();67let result: {68error?: { message: string };69token?: { id: string };70} = {};71try {72result = await stripe.createToken(this.card);73if (!this.mounted) return;74if (result.error != null) {75this.setState({ error: result.error.message });76return;77} else if (result.token != null) {78await actions.submit_payment_method(result.token.id);79if (!this.mounted) return;80}81} catch (err) {82if (this.mounted) {83result.error = { message: err.toString() }; // used in finally84this.setState({ error: err.toString() });85}86} finally {87if (this.mounted) {88this.setState({ submitting: false });89if (this.props.on_close != null && result.error == null)90this.props.on_close();91}92}93}9495private render_cancel_button(): Rendered {96if (this.props.hide_cancel_button) return;97return (98<Button99onClick={() =>100this.props.on_close != null ? this.props.on_close() : undefined101}102>103Cancel104</Button>105);106}107108private render_add_button(): Rendered {109return (110<Button111onClick={() => this.submit_payment_method()}112bsStyle="primary"113disabled={this.state.submitting}114>115{this.state.submitting ? <Loading /> : "Add Credit Card"}116</Button>117);118}119120private render_payment_method_buttons(): Rendered {121return (122<div>123<Row>124<Col sm={4}>{powered_by_stripe()}</Col>125<Col sm={8}>126<ButtonToolbar className="pull-right" style={{ marginTop: "10px" }}>127{this.render_add_button()}128{this.render_cancel_button()}129</ButtonToolbar>130</Col>131</Row>132<div style={{ color: "#666", marginTop: "15px" }}>133(Wire transfers for non-recurring purchases above $100 are possible.134Please email <HelpEmailLink />135.)136</div>137</div>138);139}140141private render_error(): Rendered {142if (this.state.error) {143return (144<ErrorDisplay145error={this.state.error}146onClose={() => this.setState({ error: "" })}147/>148);149}150}151152private render_card(): Rendered {153return (154<div style={CARD_STYLE}>155{this.state.loading ? <Loading theme="medium" /> : undefined}156<div id="card-element">157{/* a Stripe Element will be inserted here. */}158</div>159</div>160);161}162163public render(): Rendered {164return (165<Well style={{ boxShadow: "5px 5px 5px lightgray", zIndex: 2 }}>166{this.render_card()}167{this.render_error()}168{this.render_payment_method_buttons()}169</Well>170);171}172}173174175