diff --git a/ghost/members-api/static/auth/components/Modal.js b/ghost/members-api/static/auth/components/Modal.js index fc31f849a6..0da8bcc17a 100644 --- a/ghost/members-api/static/auth/components/Modal.js +++ b/ghost/members-api/static/auth/components/Modal.js @@ -11,6 +11,7 @@ import ResetPasswordPage from '../pages/ResetPasswordPage'; import StripeSubscribePage from '../pages/StripeSubscribePage'; import { IconClose } from '../components/icons'; import StripeUpgradePage from '../pages/StripeUpgradePage'; +import StripeSubscribePaymentPage from '../pages/StripeSubscribePaymentPage'; export default class Modal extends Component { constructor(props, context) { @@ -57,6 +58,7 @@ export default class Modal extends Component { window.location.hash = 'signup-complete'; }, (error) => { this.setState({ error: "Unable to confirm payment", showSpinner: false }); + window.location.hash = data.coupon ? `signup-payment?coupon=${data.coupon}` : "signup-payment"; }); }, (error) => { this.setState({ error: "Unable to signup", showSpinner: false }); @@ -70,6 +72,20 @@ export default class Modal extends Component { ); } + renderSignupPaymentPage({error, stripeConfig, members, signup, closeModal, siteConfig, showSpinner}) { + + const createSubscription = (data) => { + this.setState({showSpinner: true}); + return members.createSubscription(data).then((success) => { + this.setState({showSpinner: false}); + window.location.hash = 'signup-complete'; + }, (error) => { + this.setState({ error: "Unable to confirm payment", showSpinner: false }); + }); + }; + return + } + renderUpgradePage(props, state) { const { error, paymentConfig } = state; const { members } = this.context; @@ -79,7 +95,6 @@ export default class Modal extends Component { ); const stripeConfig = paymentConfig && paymentConfig.find(({adapter}) => adapter === 'stripe'); return - } render(props, state) { @@ -132,6 +147,7 @@ export default class Modal extends Component { {this.renderSignupPage({ error, stripeConfig, members, signup, closeModal, siteConfig, showSpinner})} + {this.renderSignupPaymentPage({ error, stripeConfig, members, signup, closeModal, siteConfig, showSpinner})} {this.renderUpgradePage(props, state)} diff --git a/ghost/members-api/static/auth/pages/StripeSubscribePaymentPage.js b/ghost/members-api/static/auth/pages/StripeSubscribePaymentPage.js new file mode 100644 index 0000000000..a602aa1dd8 --- /dev/null +++ b/ghost/members-api/static/auth/pages/StripeSubscribePaymentPage.js @@ -0,0 +1,122 @@ +import { Elements, StripeProvider, injectStripe } from 'react-stripe-elements'; +import { Component } from 'react'; +import FormHeader from '../components/FormHeader'; +import FormSubmit from '../components/FormSubmit'; +import CouponInput from '../components/CouponInput'; +import CheckoutForm from '../components/CheckoutForm'; +import Form from '../components/Form'; + +const getCouponData = frameLocation => { + const params = new URLSearchParams(frameLocation.query); + const coupon = params.get('coupon') || ''; + return { coupon }; +}; + +class PaymentForm extends Component { + + constructor(props) { + super(props); + } + + handleSubmit = ({ name, email, password, plan, coupon }) => { + // Within the context of `Elements`, this call to createToken knows which Element to + // tokenize, since there's only one in this group. + plan = this.props.selectedPlan ? this.props.selectedPlan.name : ""; + this.props.stripe.createToken({ name: name }).then(({ token }) => { + this.props.handleSubmit({ + adapter: 'stripe', + plan: plan, + stripeToken: token.id, + name, email, password, coupon + }); + }); + }; + + render({frameLocation}) { + let label = this.props.showSpinner ? "Confirming payment..." : "Confirm payment"; + const { coupon } = getCouponData(frameLocation); + return ( +
this.handleSubmit(data)}> + { coupon ? : '' } + + + + + ); + } +} + +const PaymentFormWrapped = injectStripe(PaymentForm); + +export default class StripeSubscriptionPage extends Component { + constructor(props) { + super(props); + this.plans = props.stripeConfig.config.plans || []; + this.state = { + selectedPlan: this.plans[0] ? this.plans[0] : "" + } + } + + renderPlan({ currency, amount, id, interval, name }) { + const selectedPlanId = this.state.selectedPlan ? this.state.selectedPlan.id : ""; + const dollarAmount = (amount / 100); + return ( + + ) + } + + changePlan(e) { + const plan = this.plans.find(plan => plan.id === e.target.value); + this.setState({ + selectedPlan: plan + }) + } + + renderPlans(plans) { + return ( +
this.changePlan(e)}> + { + plans.map((plan) => this.renderPlan(plan)) + } +
+ ); + } + + renderPlansSection() { + return ( +
+

Billing period

+ {this.renderPlans(this.plans)} +
+ ) + } + + render({ error, handleSubmit, stripeConfig, frameLocation, showSpinner }) { + const publicKey = stripeConfig.config.publicKey || ''; + return ( +
+
+ +
We were unable to process your payment, please try again or use different card details.
+
+ { this.renderPlansSection() } +
+

Card details

+
+ + + + + +
+
+
+ ) + } +};