lbry-desktop/ui/page/settingsStripeCard/view.jsx

418 lines
13 KiB
React
Raw Normal View History

updated code about to test something generate programatically beginning of the frontend stripe integration page seems to be working add user put functionality behind conditional tag connect frontend working well adding environment variables to save success and failure url bugfix bugfix final clean up adding credit card page seems to be coming along calls successfully coming from the frontend fixing up frontend cleaning up frontend coming along client secret working basic frontend in place adding tip page adding more to the tip frontend frontend almost done tabs coming along one last thing to do for frontend adding explainer text as custom function putting finishing touches on tabs support tabs working well disable fiat toggle when card not connected fix frontend gui bug bugfix and pull out label function fix symbol for tip gui modal when card is not yet saved fix fiat disabled bug knowing whether card is added programatically sending tip with frontend tip functionality working show unpaid balance add frontend for card add section update frontend update frontend bugfix change to use react instead of css update how stripe is instantiated fix bug use customer setup coming along working but needs optimization persist if card is saved adding anonymous tip functionality fix nan bug build stripe endpoints programatically show for all users for time being allow the stripe key to automatically switch to live environment bugfix bugfix fix jslint fix channel page support button better docs show customer transactions on frontend basic table in place various page updates per jeremys notes showing card details nicer tip history table add better prompt to add card on file viewer page some linting time put connect account behind fiat enabled no persist fiat mode wallet calls tip stuff
2021-07-03 19:19:23 +02:00
// @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;
2021-07-05 04:28:14 +02:00
// let scriptLoaded = false;
// let scriptDidError = false; // these could probably be in state if managing locally
updated code about to test something generate programatically beginning of the frontend stripe integration page seems to be working add user put functionality behind conditional tag connect frontend working well adding environment variables to save success and failure url bugfix bugfix final clean up adding credit card page seems to be coming along calls successfully coming from the frontend fixing up frontend cleaning up frontend coming along client secret working basic frontend in place adding tip page adding more to the tip frontend frontend almost done tabs coming along one last thing to do for frontend adding explainer text as custom function putting finishing touches on tabs support tabs working well disable fiat toggle when card not connected fix frontend gui bug bugfix and pull out label function fix symbol for tip gui modal when card is not yet saved fix fiat disabled bug knowing whether card is added programatically sending tip with frontend tip functionality working show unpaid balance add frontend for card add section update frontend update frontend bugfix change to use react instead of css update how stripe is instantiated fix bug use customer setup coming along working but needs optimization persist if card is saved adding anonymous tip functionality fix nan bug build stripe endpoints programatically show for all users for time being allow the stripe key to automatically switch to live environment bugfix bugfix fix jslint fix channel page support button better docs show customer transactions on frontend basic table in place various page updates per jeremys notes showing card details nicer tip history table add better prompt to add card on file viewer page some linting time put connect account behind fiat enabled no persist fiat mode wallet calls tip stuff
2021-07-03 19:19:23 +02:00
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,
}
class CardVerify extends React.Component<Props, State> {
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,
2021-07-05 04:28:14 +02:00
lastFour: card.last4,
updated code about to test something generate programatically beginning of the frontend stripe integration page seems to be working add user put functionality behind conditional tag connect frontend working well adding environment variables to save success and failure url bugfix bugfix final clean up adding credit card page seems to be coming along calls successfully coming from the frontend fixing up frontend cleaning up frontend coming along client secret working basic frontend in place adding tip page adding more to the tip frontend frontend almost done tabs coming along one last thing to do for frontend adding explainer text as custom function putting finishing touches on tabs support tabs working well disable fiat toggle when card not connected fix frontend gui bug bugfix and pull out label function fix symbol for tip gui modal when card is not yet saved fix fiat disabled bug knowing whether card is added programatically sending tip with frontend tip functionality working show unpaid balance add frontend for card add section update frontend update frontend bugfix change to use react instead of css update how stripe is instantiated fix bug use customer setup coming along working but needs optimization persist if card is saved adding anonymous tip functionality fix nan bug build stripe endpoints programatically show for all users for time being allow the stripe key to automatically switch to live environment bugfix bugfix fix jslint fix channel page support button better docs show customer transactions on frontend basic table in place various page updates per jeremys notes showing card details nicer tip history table add better prompt to add card on file viewer page some linting time put connect account behind fiat enabled no persist fiat mode wallet calls tip stuff
2021-07-03 19:19:23 +02:00
};
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,
2021-07-05 04:28:14 +02:00
});
updated code about to test something generate programatically beginning of the frontend stripe integration page seems to be working add user put functionality behind conditional tag connect frontend working well adding environment variables to save success and failure url bugfix bugfix final clean up adding credit card page seems to be coming along calls successfully coming from the frontend fixing up frontend cleaning up frontend coming along client secret working basic frontend in place adding tip page adding more to the tip frontend frontend almost done tabs coming along one last thing to do for frontend adding explainer text as custom function putting finishing touches on tabs support tabs working well disable fiat toggle when card not connected fix frontend gui bug bugfix and pull out label function fix symbol for tip gui modal when card is not yet saved fix fiat disabled bug knowing whether card is added programatically sending tip with frontend tip functionality working show unpaid balance add frontend for card add section update frontend update frontend bugfix change to use react instead of css update how stripe is instantiated fix bug use customer setup coming along working but needs optimization persist if card is saved adding anonymous tip functionality fix nan bug build stripe endpoints programatically show for all users for time being allow the stripe key to automatically switch to live environment bugfix bugfix fix jslint fix channel page support button better docs show customer transactions on frontend basic table in place various page updates per jeremys notes showing card details nicer tip history table add better prompt to add card on file viewer page some linting time put connect account behind fiat enabled no persist fiat mode wallet calls tip stuff
2021-07-03 19:19:23 +02:00
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) {
document.querySelector('button').disabled = true;
document.querySelector('#spinner').classList.remove('hidden');
document.querySelector('#button-text').classList.add('hidden');
} else {
document.querySelector('button').disabled = false;
document.querySelector('#spinner').classList.add('hidden');
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() {
if (this.loadPromise) {
this.loadPromise.reject();
}
if (CardVerify.stripeHandler && this.state.open) {
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 (
<Page backout={{ title: pageTitle, backLabel: __('Done') }} noFooter noSideNavigation>
<div>
{scriptFailedToLoad && (
<div className="error__text">There was an error connecting to Stripe. Please try again later.</div>
)}
</div>
{currentFlowStage === 'loading' && <div className="headerCard toConfirmCard">
<Card
title={__('Connect your card with Odysee')}
subtitle={__('Getting your card connection status...')}
/>
</div>}
{currentFlowStage === 'confirmingCard' && <div className="sr-root">
<div className="sr-main">
<div className="sr-payment-form card cardInput">
<div className="sr-form-row">
<label className="payment-details">
Card Details
</label>
<div className="sr-input sr-element sr-card-element" id="card-element" />
</div>
<div className="sr-field-error" id="card-errors" role="alert" />
<button className="linkButton" id="submit">
<div className="spinner hidden" id="spinner" />
<span id="button-text">Add Card</span>
</button>
</div>
</div>
</div>}
{currentFlowStage === 'cardConfirmed' && <div className="successCard">
<Card
title={__('Card Details')}
body={<>
<h4 className="grey-text">Brand: {userCardDetails.brand.toUpperCase()} &nbsp;
Last 4: {userCardDetails.lastFour} &nbsp;
Expires: {userCardDetails.expiryMonth}/{userCardDetails.expiryYear} &nbsp;
</h4>
</>}
/>
<br />
{(!customerTransactions || customerTransactions.length === 0) && <Card
title={__('Tip History')}
subtitle={__('You have not sent any tips yet. When you do they will appear here. ')}
/>}
{customerTransactions && customerTransactions.length > 0 && <Card
title={__('Tip History')}
body={<><div className="table__wrapper">
<table className="table table--transactions">
<thead>
<tr>
<th className="date-header">{__('Date')}</th>
<th>{<>{__('Receiving Channel Name')}</>}</th>
<th>{__('Amount (USD)')} </th>
<th>{__('Anonymous')}</th>
</tr>
</thead>
<tbody>
{customerTransactions &&
customerTransactions.map((transaction) => (
<tr key={transaction.name + transaction.created_at}>
<td>{moment(transaction.created_at).format('LLL')}</td>
<td>{transaction.channel_name}</td>
<td>${transaction.tipped_amount / 100}</td>
<td>{transaction.private_tip ? 'Yes' : 'No'}</td>
</tr>
))}
</tbody>
</table>
</div></>}
/>}
</div>}
</Page>
);
}
}
export default CardVerify;
/* eslint-enable no-undef */
/* eslint-enable react/prop-types */