// restore flow /* eslint-disable no-undef */ /* eslint-disable react/prop-types */ import React from 'react'; import Page from 'component/page'; import Card from 'component/common/card'; import { Lbryio } from 'lbryinc'; import { STRIPE_PUBLIC_KEY } from 'config'; import moment from 'moment'; let scriptLoading = false; // let scriptLoaded = false; // let scriptDidError = false; // these could probably be in state if managing locally let stripeEnvironment = 'test'; // if the key contains pk_live it's a live key // update the environment for the calls to the backend to indicate which environment to hit if (STRIPE_PUBLIC_KEY.indexOf('pk_live') > -1) { stripeEnvironment = 'live'; } // type Props = { // disabled: boolean, // label: ?string, // email: ?string, // scriptFailedToLoad: boolean, // }; // // type State = { // open: boolean, // currentFlowStage: string, // customerTransactions: Array, // pageTitle: string, // userCardDetails: any, // fill this out // scriptFailedToLoad: boolean, // }; class CardVerify extends React.Component { constructor(props) { // :Props super(props); this.state = { open: false, scriptFailedToLoad: false, currentFlowStage: 'loading', // loading, confirmingCard, cardConfirmed customerTransactions: [], pageTitle: 'Add Card', userCardDetails: {}, }; } componentDidMount() { var that = this; const script = document.createElement('script'); script.src = 'https://js.stripe.com/v3/'; script.async = true; // $FlowFixMe document.body.appendChild(script); // public key of the stripe account var publicKey = STRIPE_PUBLIC_KEY; // client secret of the SetupIntent (don't share with anyone but customer) var clientSecret = ''; // setting a timeout to let the client secret populate // TODO: fix this, should be a cleaner way setTimeout(function () { // check if customer has card setup already Lbryio.call( 'customer', 'status', { environment: stripeEnvironment, }, 'post' ) .then((customerStatusResponse) => { // user has a card saved if their defaultPaymentMethod has an id const defaultPaymentMethod = customerStatusResponse.Customer.invoice_settings.default_payment_method; var userHasAlreadySetupPayment = Boolean(defaultPaymentMethod && defaultPaymentMethod.id); // show different frontend if user already has card if (userHasAlreadySetupPayment) { var card = customerStatusResponse.PaymentMethods[0].card; var cardDetails = { brand: card.brand, expiryYear: card.exp_year, expiryMonth: card.exp_month, lastFour: card.last4, }; that.setState({ currentFlowStage: 'cardConfirmed', pageTitle: 'Tip History', userCardDetails: cardDetails, }); // get customer transactions Lbryio.call( 'customer', 'list', { environment: stripeEnvironment, }, 'post' ).then((customerTransactionsResponse) => { that.setState({ customerTransactions: customerTransactionsResponse, }); console.log(customerTransactionsResponse); }); // otherwise, prompt them to save a card } else { that.setState({ currentFlowStage: 'confirmingCard', }); // get a payment method secret for frontend Lbryio.call( 'customer', 'setup', { environment: stripeEnvironment, }, 'post' ).then((customerSetupResponse) => { console.log(customerSetupResponse); clientSecret = customerSetupResponse.client_secret; // instantiate stripe elements setupStripe(); }); } // if the status call fails, either an actual error or need to run setup first }) .catch(function (error) { console.log(error); // errorString passed from the API (with a 403 error) const errorString = 'user as customer is not setup yet'; // if it's beamer's error indicating the account is not linked yet if (error.message.indexOf(errorString) > -1) { // send them to save a card that.setState({ currentFlowStage: 'confirmingCard', }); // get a payment method secret for frontend Lbryio.call( 'customer', 'setup', { environment: stripeEnvironment, }, 'post' ).then((customerSetupResponse) => { console.log(customerSetupResponse); clientSecret = customerSetupResponse.client_secret; // instantiate stripe elements setupStripe(); }); } else { console.log('Unseen before error'); } }); }, 250); function setupStripe() { // TODO: have to fix this, using so that the script is available setTimeout(function () { var stripeElements = function (publicKey, setupIntent) { var stripe = Stripe(publicKey); var elements = stripe.elements(); // Element styles var style = { base: { fontSize: '16px', color: '#32325d', fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif', fontSmoothing: 'antialiased', '::placeholder': { color: 'rgba(0,0,0,0.4)', }, }, }; var card = elements.create('card', { style: style }); card.mount('#card-element'); // Element focus ring card.on('focus', function () { var el = document.getElementById('card-element'); el.classList.add('focused'); }); card.on('blur', function () { var el = document.getElementById('card-element'); el.classList.remove('focused'); }); card.on('ready', function () { card.focus(); }); var email = that.props.email; function submitForm(event) { event.preventDefault(); // if client secret wasn't loaded properly if (!clientSecret) { var displayErrorText = 'There was an error in generating your payment method. Please contact a developer'; var displayError = document.getElementById('card-errors'); displayError.textContent = displayErrorText; return; } changeLoadingState(true); stripe .confirmCardSetup(clientSecret, { payment_method: { card: card, billing_details: { email: email }, }, }) .then(function (result) { if (result.error) { console.log(result); changeLoadingState(false); var displayError = document.getElementById('card-errors'); displayError.textContent = result.error.message; } else { // The PaymentMethod was successfully set up // hide and show the proper divs orderComplete(stripe, clientSecret); } }); } // Handle payment submission when user clicks the pay button. var button = document.getElementById('submit'); button.addEventListener('click', function (event) { submitForm(event); }); // currently doesn't work because the iframe javascript context is different // would be nice though if it's even technically possible // window.addEventListener('keyup', function(event) { // if (event.keyCode === 13) { // submitForm(event); // } // }, false); }; // TODO: possible bug here where clientSecret isn't done stripeElements(publicKey, clientSecret); // Show a spinner on payment submission var changeLoadingState = function (isLoading) { if (isLoading) { // $FlowFixMe document.querySelector('button').disabled = true; // $FlowFixMe document.querySelector('#spinner').classList.remove('hidden'); // $FlowFixMe document.querySelector('#button-text').classList.add('hidden'); } else { // $FlowFixMe document.querySelector('button').disabled = false; // $FlowFixMe document.querySelector('#spinner').classList.add('hidden'); // $FlowFixMe document.querySelector('#button-text').classList.remove('hidden'); } }; // shows a success / error message when the payment is complete var orderComplete = function (stripe, clientSecret) { stripe.retrieveSetupIntent(clientSecret).then(function (result) { Lbryio.call( 'customer', 'status', { environment: stripeEnvironment, }, 'post' ).then((customerStatusResponse) => { var card = customerStatusResponse.PaymentMethods[0].card; var cardDetails = { brand: card.brand, expiryYear: card.exp_year, expiryMonth: card.exp_month, lastFour: card.last4, }; that.setState({ currentFlowStage: 'cardConfirmed', pageTitle: 'Tip History', userCardDetails: cardDetails, }); }); console.log(result); changeLoadingState(false); }); }; }, 0); } } componentDidUpdate() { if (!scriptLoading) { this.updateStripeHandler(); } } componentWillUnmount() { // pretty sure this doesn't exist // $FlowFixMe if (this.loadPromise) { // $FlowFixMe this.loadPromise.reject(); } // pretty sure this doesn't exist // $FlowFixMe if (CardVerify.stripeHandler && this.state.open) { // $FlowFixMe CardVerify.stripeHandler.close(); } } onScriptLoaded = () => { // if (!CardVerify.stripeHandler) { // CardVerify.stripeHandler = StripeCheckout.configure({ // key: 'pk_test_NoL1JWL7i1ipfhVId5KfDZgo', // }); // // if (this.hasPendingClick) { // this.showStripeDialog(); // } // } }; onScriptError = (...args) => { this.setState({ scriptFailedToLoad: true }); }; onClosed = () => { this.setState({ open: false }); }; updateStripeHandler() { // if (!CardVerify.stripeHandler) { // CardVerify.stripeHandler = StripeCheckout.configure({ // key: this.props.stripeKey, // }); // } } render() { const { scriptFailedToLoad } = this.props; const { currentFlowStage, customerTransactions, pageTitle, userCardDetails } = this.state; return (
{scriptFailedToLoad && (
There was an error connecting to Stripe. Please try again later.
)}
{currentFlowStage === 'loading' && (
)} {currentFlowStage === 'confirmingCard' && (
)} {currentFlowStage === 'cardConfirmed' && (

Brand: {userCardDetails.brand.toUpperCase()}   Last 4: {userCardDetails.lastFour}   Expires: {userCardDetails.expiryMonth}/{userCardDetails.expiryYear}  

} />
{(!customerTransactions || customerTransactions.length === 0) && ( )} {customerTransactions && customerTransactions.length > 0 && (
{customerTransactions && customerTransactions.map((transaction) => ( ))}
{__('Date')} {<>{__('Receiving Channel Name')}} {__('Amount (USD)')} {__('Anonymous')}
{moment(transaction.created_at).format('LLL')} {transaction.channel_name} ${transaction.tipped_amount / 100} {transaction.private_tip ? 'Yes' : 'No'}
} /> )}
)} ); } } export default CardVerify; /* eslint-enable no-undef */ /* eslint-enable react/prop-types */