context = window rest = context.JK.Rest() logger = context.JK.logger LocationActions = context.LocationActions AppStore = context.AppStore UserStore = context.UserStore SubscriptionActions = context.SubscriptionActions profileUtils = context.JK.ProfileUtils @AccountPaymentHistoryScreen = React.createClass({ mixins: [ ICheckMixin, Reflux.listenTo(AppStore, "onAppInit"), Reflux.listenTo(UserStore, "onUserChanged"), Reflux.listenTo(@LocationStore, "onLocationsChanged"), Reflux.listenTo(SubscriptionStore, "onSubscriptionChanged") ] shownOnce: false screenVisible: false LIMIT: 20 TILE_PAYMENTS_TO_YOU: 'payments to you' TILE_PAYMENTS_TO_JAMKAZAM: 'payments to jamkazam' TILE_PAYMENT_METHOD: 'payment method' STUDENT_TILES: ['payment method', 'payments to jamkazam'] TEACHER_TILES: ['payments to jamkazam', 'payments to you'] onAppInit: (@app) -> @app.bindScreen('account/paymentHistory', {beforeShow: @beforeShow, afterShow: @afterShow, beforeHide: @beforeHide}) onUserChanged: (userState) -> if !@shouldShowNameSet @shouldShowNameSet = true if userState?.user? username = userState.user.name first_name = userState.user.first_name last_name = userState.user.last_name shouldShowName = !username? || username.trim() == '' || username.toLowerCase().indexOf('anonymous') > -1 else shouldShowName = @state.shouldShowName if userState?.user?.teacher? @setState({selected: 'payments to you'}) @setState({user: userState?.user, shouldShowName: shouldShowName}) componentDidMount: () -> #@checkboxes = [{selector: 'input.billing-address-in-us', stateKey: 'billingInUS'}] @root = $(@getDOMNode()) @endOfList = @root.find('.end-of-payments-list') @contentBodyScroller = @root #@iCheckify() @preparePaymentMethod() componentDidUpdate: (prevProps, prevState) -> #@iCheckify() #$expiration = @root.find('input.expiration') #if !$expiration.data('payment-applied') # $expiration.payment('formatCardExpiry').data('payment-applied', true) #$cardNumber = @root.find("input.card-number") #if !$cardNumber.data('payment-applied') # $cardNumber.payment('formatCardNumber').data('payment-applied', true) #$cvv = @root.find("input.cvv") #if !$cvv.data('payment-applied') # $cvv.payment('formatCardCVC').data('payment-applied', true) if @currentNext() == null @contentBodyScroller.off('scroll') if @state[@getCurrentPageName()] == 1 and @getCurrentList().length == 0 @endOfList.show() logger.debug("PaymentHistoryScreen: empty search") else if @state[@getCurrentPageName()] > 0 logger.debug("end of search") @endOfList.show() else @registerInfiniteScroll(@contentBodyScroller) if @activeTile(prevState.selected) != @activeTile() && @getCurrentList().length == 0 @refresh() @configureElements() registerInfiniteScroll:() -> $scroller = @contentBodyScroller logger.debug("registering infinite scroll") $scroller.off('scroll') $scroller.on('scroll', () => # be sure to not fire off many refreshes when user hits the bottom return if @refreshing if $scroller.scrollTop() + $scroller.innerHeight() + 100 >= $scroller[0].scrollHeight #$scroller.append('
... Loading more Payments ...
') @setState({searching: true}) logger.debug("refreshing more payments for infinite scroll") @nextPage() ) nextPage: () -> #nextPage = @state.salesCurrentPage + 1 @incrementCurrentPage() @refresh() checkboxChanged: (e) -> checked = $(e.target).is(':checked') value = $(e.target).val() @setState({userSchedulingComm: value}) beforeHide: (e) -> @screenVisible = false @resetErrors() beforeShow: (e) -> afterShow: (e) -> @clearResults() @screenVisible = true @refresh() #@getUncollectables() resetErrors: () -> @setState({ccError: null, cvvError: null, expiryError: null, billingInUSError: null, zipCodeError: null, nameError: null, firstNameError: null, lastNameError: null, cityError: null, stateError: null, address1Error: null, countryError: null, address2Error:null}) onBack: (e) -> e.preventDefault() window.history.go(-1); checkboxChanged: (e) -> checked = $(e.target).is(':checked') @setState({billingInUS: checked}) refresh: () -> @buildQuery() if @activeTile() == @TILE_PAYMENTS_TO_YOU @refreshTeacherDistributions() else if @activeTile() == @TILE_PAYMENTS_TO_JAMKAZAM @refreshSales() else if @activeTile() == @TILE_PAYMENT_METHOD @refreshPayment() else logger.debug("dropping refresh because no tile match", @activeTile) refreshPayment:() -> SubscriptionActions.updateSubscription() refreshSales: () -> @refreshing = true #rest.getSalesHistory(@currentQuery) #.done(@salesHistoryDone) #.fail(@salesHistoryFail) rest.listInvoices() .done(@paymentHistoryDone) .fail(@paymentHistoryFail) refreshTeacherDistributions: () -> @refreshing = true rest.listTeacherDistributions(@currentQuery) .done(@teacherDistributionsDone) .fail(@teacherDistributionsFail) getUncollectables: () -> rest.getUncollectables({}) .done(@uncollectablesDone) .fail(@uncollectablesFail) salesHistoryDone:(response) -> @refreshing = false this.setState({salesNext: response.next, sales: this.state.sales.concat(response.entries)}) salesHistoryFail:(jqXHR) -> @refreshing = false @app.notifyServerError jqXHR, 'Payments to JamKazam Unavailable' paymentHistoryDone:(response) -> @refreshing = false this.setState({salesNext: null, sales: this.state.sales.concat(response.entries), pastDue: response.past_due}) paymentHistoryFail:(jqXHR) -> @refreshing = false @app.notifyServerError jqXHR, 'Payments to JamKazam Unavailable' teacherDistributionsDone:(response) -> @refreshing = false this.setState({distributionsNext: response.next, distributions: this.state.distributions.concat(response.entries)}) teacherDistributionsFail:(jqXHR) -> @refreshing = false @app.notifyServerError jqXHR, 'Payments to You Unavailable' uncollectablesDone: (response) -> this.setState({uncollectables: response}) uncollectablesFail: (jqXHR) -> @app.notifyServerError jqXHR, 'Unable to fetch uncollectable info' clearResults:() -> this.setState({salesCurrentPage: 0, sales: [], distributionsCurrentPage: 0, distributions: [], salesNext: null, distributionsNext: null, updating: false, pastDue: false}) buildQuery:(page = @getCurrentPage()) -> @currentQuery = this.defaultQuery(page) defaultQuery:(page = @getCurrentPage()) -> query = per_page: @LIMIT page: page + 1 if @currentNext() query.page = @currentNext() query getCurrentPage: () -> page = this.state[@getCurrentPageName()] if !page? page = 1 page incrementCurrentPage: () -> newState = {} newState[@getCurrentPageName] = @state[@getCurrentPageName()] + 1 this.setState(newState) getCurrentPageName: () -> if @activeTile() == @TILE_PAYMENTS_TO_JAMKAZAM 'salesCurrentPage' else if @activeTile() == @TILE_PAYMENTS_TO_YOU 'distributionsCurrentPage' else 1 getCurrentList: () -> if @activeTile() == @TILE_PAYMENTS_TO_JAMKAZAM @state['sales'] else if @activeTile() == @TILE_PAYMENTS_TO_YOU @state['distributions'] else @state['sales'] currentNext: () -> if @activeTile() == @TILE_PAYMENTS_TO_JAMKAZAM @state.salesNext else if @activeTile() == @TILE_PAYMENTS_TO_YOU @state.distributionsNext else null onClick: (e) -> e.preventDefault() context.location.href = '/client#/account' getInitialState: () -> { user: null, nextPager: null, salesCurrentPage: 0, distributionsCurrentPage: 0 salesNext: null, distributionsNext: null sales: [], distributions: [] selected: @TILE_PAYMENT_METHOD, updating: false, billingInUS: true, userWantsUpdateCC: false, selectedCountry: null, paypal_updating: false, uncollectables: [] } onCancel: (e) -> e.preventDefault() context.location.href = '/client#/account' mainContent: () -> if !@state.user? `
Loading...
` else if @state.selected == @TILE_PAYMENTS_TO_YOU @paymentsToYou() else if @state.selected == @TILE_PAYMENTS_TO_JAMKAZAM @paymentsToJamKazam() else if @state.selected == @TILE_PAYMENT_METHOD @paymentMethod() else @paymentsToJamKazam() paymentsToYou: () -> rows = [] for paymentHistory in @getCurrentList() paymentMethod = 'Stripe' if paymentHistory.distributed date = paymentHistory.teacher_payment.teacher_payment_charge.last_billing_attempt_at status = 'Paid' else date = paymentHistory.created_at if paymentHistory.not_collectable status = 'Uncollectible' else if !paymentHistory.teacher?.teacher?.stripe_account_id? status = 'No Stripe Acct' else status = 'Collecting' date = context.JK.formatDate(date, true) description = paymentHistory.description if paymentHistory.teacher_payment? amt = paymentHistory.teacher_payment.real_distribution_in_cents else amt = paymentHistory.real_distribution_in_cents displayAmount = ' $' + (amt/100).toFixed(2) amountClasses = {status: status} row = ` {date} {paymentMethod} {description} {status} {displayAmount} ` rows.push(row) `
{rows}
DATE METHOD DESCRIPTION STATUS AMOUNT
Next
No more payment history
BACK

` onSubscriptionChanged: (subscription) -> @setState({pastDue: subscription.past_due}) onLocationsChanged: (countries) -> console.log("countries in ", countries) @setState({countries: countries}) onCountryChanged: (e) -> val = $(e.target).val() @setState({selectedCountry: val}) currentCountry: () -> this.state.selectedCountry || this.props.selectedCountry || '' openBrowser: () -> context.JK.popExternalLink("https://www.jamkazam.com/client#/subscription") onRecurlyToken: (err, token_data) -> if err console.log("error", err) handled = false if err.code == "validation" && err.fields? handled = true for field in err.fields console.log("problem field", field) # handle error using err.code and err.fields if field == "first_name" @setState({firstNameError: true}) if field == "last_name" @setState({lastNameError: true}) if err.code == "invalid-parameter" if err.fields.indexOf("year") > -1 || err.fields.indexOf("month") > -1 || err.fields.indexOf("number") > -1 || err.fields.indexOf("cvv") > -1 @setState({ccError: true}) handled = true @app.layout.notify({title: 'Please double-check ' + err.fields[0], text: err.message}) if !handled @app.layout.notify({title: "Error Updating Payment Info", text: JSON.stringify(err)}) @setState({updating: false}) else # recurly.js has filled in the 'token' field, so now we can submit the # form to your server console.log("eintercepted", token_data) rest.updatePayment({recurly_token: token_data.id}).done((response) => @updatePaymentDone(response)).fail((jqXHR) => @updatePaymentFailure(jqXHR)) updatePaymentDone: (response) -> @setState({updating: false, "paypal_updating": false}) logger.debug("recurly submitted: " + JSON.stringify(response)) @setState({userWantsUpdateCC: false}) #if @state.shouldShowName window.UserActions.refresh() if response.uncollectables context.JK.Banner.showAlert('Credit Card Updated', 'Than you. Your credit card info has been updated.

We will try to bill any unpaid lessons within the next hour, and an email will be sent at that time.') else @app.layout.notify({title: 'Payment Updated', text: 'Your payment info has been updated.'}) updatePaymentFailure: (jqXHR) -> @setState({updating: false, "paypal_updating": false}) handled = false if jqXHR.status == 404 errors = JSON.parse(jqXHR.responseText)?.message @app.layout.notify({title: "Error Updating Payment Info", text: errors}) else @app.notifyServerError(jqXHR, 'Payment Not Updated') onFormSubmit: (event) -> form = event.target console.log("ok work this", form) event.preventDefault() @setState({updating: true}) recurly.token(@elements, form, @onRecurlyToken) configureRecurly: () -> if !window.configuredRecurly console.log("configuring recurly...") window.recurly.configure(gon.global.recurly_public_api_key) window.configuredRecurly = true @setState({"configuredRecurly": true}) @elements = window.recurly.Elements() # then load paypal: @paypal = window.recurly.PayPal({ braintree: { clientAuthorization: gon.global.braintree_token} }) @paypal.on('error', this.onPayPalError) @paypal.on('token', this.onPayPalToken) delayedConfigure: () -> if gon.isNativeClient return if !window.recurly? console.log("relaunch delayed recurly configure") setTimeout(() => @delayedConfigure() , 1000) return @configureRecurly() @configureElements() configureElements: () -> node = $('#subscription-elements') if node.length > 0 if window.recurly && @elements? && !node.data('recurlied') commonStyles = { inputType: 'mobileSelect', style: { fontSize:'1em' color:'black', placeholder: { color:'black', } invalid: { fontColor: 'red' } } } #cardNumberStyle = $.extend(true, {}, commonStyles, {style:{placeholder:{content: 'Card Number'}}}) #cardMonthStyle = $.extend(true, {}, commonStyles, {style:{placeholder:{content: 'MM'}}}) #cardYearStyle = $.extend(true, {}, commonStyles, {style:{placeholder:{content: 'YYYY'}}}) #cardCvcStyle = $.extend(true, {}, commonStyles, {style:{placeholder:{content: 'CVC'}}}) #cardNumberElement = @elements.CardNumberElement( cardNumberStyle ) #cardMonthElement = @elements.CardMonthElement(cardMonthStyle) #cardYearElement = @elements.CardYearElement(cardYearStyle) #cardCvvElement = @elements.CardCvvElement(cardCvcStyle) #cardNumberElement.attach('#subscription-elements-number') #cardMonthElement.attach('#subscription-elements-month') #cardYearElement.attach('#subscription-elements-year') #cardCvvElement.attach('#subscription-elements-cvv') cardElement = @elements.CardElement(commonStyles) cardElement.attach("#subscription-elements") document.querySelector('#user-payment-submit').addEventListener('submit', @onSubmit.bind(this)) node.data('recurlied', true) preparePaymentMethod: () -> LocationActions.load() setTimeout(() => @delayedConfigure() , 200) defaultText: () -> 'Select Country' onStartPaypal: (e) -> e.preventDefault() if @state.updating return @setState({"paypal_updating": true}) @paypal.start() onPayPalToken:(token) -> console.log("OnPayPalToken", token) @setState({updating: true}) rest.updatePayment({recurly_token: token.id}).done((response) => @updatePaymentDone(response)).fail((jqXHR) => @updatePaymentFailure(jqXHR)) onPayPalError: (err) -> console.error("OnPayPalError", err) @setState({"paypal_updating": false}) openBrowserToPayment: () -> context.JK.popExternalLink("/client#/account/paymentHistory", true) paymentMethod: () -> if context.jamClient.IsNativeClient() return `

Updating payment is only supported in a web browser. Please click the button below to open this page in your system web browser.

UPDATE PAYMENT METHOD


` disabled = @state.updating || @reuseStoredCard() submitClassNames = {'button-orange': true, 'purchase-btn': true, disabled: disabled && @state.updating} updateCardClassNames = {'button-grey': true, 'update-btn': true, disabled: disabled && @state.updating} backClassNames = {'button-grey': true, disabled: disabled && @state.updating} cardNumberFieldClasses = {field: true, "card-number": true, error: @state.ccError} expirationFieldClasses = {field: true, "expiration": true, error: @state.expiryError} cvvFieldClasses = {field: true, "card-number": true, error: @state.cvvError} inUSClasses = {field: true, "billing-in-us": true, error: @state.billingInUSError} zipCodeClasses = {field: true, "zip-code": true, error: @state.zipCodeError} nameClasses= {field: true, "name": true, error: @state.nameError} firstNameClasses= {field: true, "first-name": true, error: @state.firstNameError} lastNameClasses= {field: true, "last-name": true, error: @state.lastNameError} address1Classes= {field: true, "address-1": true, error: @state.address1Error} address2Classes= {field: true, "address-2": true, error: @state.address2Error} cityClasses= {field: true, "city": true, error: @state.cityError} stateClasses= {field: true, "state": true, error: @state.stateError} countryClasses = {field: true, "country": true, error: @state.countryError} formClasses= {stored: @reuseStoredCard()} leftColumnClasses = {column: true, 'column-left': true, stored: @reuseStoredCard()} rightColumnClasses = {column: true, 'column-right': true, stored: @reuseStoredCard()} if @state.countries? countries = [``] for countryId, countryInfo of @state.countries countries.push(``) country = @state.countries[this.currentCountry()] else countries = [] countryJsx = ` ` if gon.global.paypal_admin_only if @state.user?.admin paypal_visibility = 'block' else paypal_visibility = 'none' else paypal_visibility = 'block' paypalButtonClasses = "paypal-button " if !@state.configuredRecurly paypalButtonText = `Loading PayPal ...` paypalButtonClasses = paypalButtonClasses + "paypal-updating" else if @state.paypal_updating && @state.updating paypalButtonClasses = paypalButtonClasses + "paypal-updating" paypalButtonText = `Updating your account ...` else paypalButtonText = `PayPal` if @state.uncollectables.length > 0 uncollectable = @state.uncollectables[0] uncollectableMessage = `
A charge for your music lesson with {uncollectable.teacher.name} failed. Please update your credit card information immediately so that we can pay the instructor. If you have called your credit card provider and believe there should be no problem with your card, please email us at support@jamkazam.com so that we can figure out what's gone wrong. Thank you!
` if @state.pastDue uncollectableMessage = `
A charge for your subscription has failed. Please update your credit card information immediately so that you can restore your plan.
` if @state.user?['has_stored_credit_card?'] && @state.uncollectables.length == 0 if @state.userWantsUpdateCC header = 'Please update your billing address and payment information below.' updateCardAction = `NEVERMIND` actions = `
BACK {updateCardAction} SUBMIT CARD INFORMATION
` else header = 'You have a payment method on file already.' updateCardAction = `I'D LIKE TO UPDATE MY PAYMENT INFO` managedSubscriptionAction = `MANAGE MY SUBSCRIPTION` actions = `
BACK {updateCardAction} {managedSubscriptionAction}
` else header = `
Please enter your billing address and payment information below.

This information is sent to Recurly, which is compliant with the PCI-DSS security standard. JamKazam does not receive or store your credit card.
` actions = `
BACKSUBMIT CARD INFORMATION
` firstNameField = `
` lastNameField = `
` `
{uncollectableMessage}
{header}
{firstNameField} {lastNameField}
{countryJsx}
Or Use PayPal:
{paypalButtonText}
{actions}

` paymentsToJamKazam: () -> rows = [] uncollectables = [] for uncollectable in @state.uncollectables date = context.JK.formatDate(uncollectable.last_billed_at_date, true) paymentMethod = 'Credit Card' amt = uncollectable.expected_price_in_cents displayAmount = ' $' + (amt/100).toFixed(2) if uncollectable['is_card_declined?'] reason = 'card declined' else if uncollectable['is_card_expired?'] reason = 'card expired' else reason = 'charge fail' row = ` {date} {paymentMethod} {uncollectable.description} {reason} {displayAmount} ` uncollectables.push(row) if uncollectables.length > 0 uncollectableTable = `
You have unpaid lessons, which are listed immediately below. Click here to update your credit card info.
Unpaid Lessons
{uncollectables}
CHARGED AT DESCRIPTION REASON AMOUNT
Payments
` for paymentHistory in @getCurrentList() paymentMethod = 'Credit Card' if paymentHistory.sale? sale = paymentHistory.sale amt = sale.recurly_total_in_cents status = 'paid' displayAmount = ' $' + (amt/100).toFixed(2) date = context.JK.formatDate(sale.created_at, true) items = [] for line_item in sale.line_items items.push(line_item.product_info?.name) description = items.join(', ') else # this is a recurly webhook #transaction = paymentHistory.transaction #amt = transaction.amount_in_cents #status = transaction.transaction_type #displayAmount = '($' + (amt/100).toFixed(2) + ')' #date = context.JK.formatDate(transaction.transaction_at, true) #description = transaction.admin_description invoice = paymentHistory amt = invoice.total_in_cents status = invoice.state displayAmount = '($' + (amt/100).toFixed(2) + ')' date = context.JK.formatDate(invoice.created_at, true) description = invoice.description amountClasses = {status: status} row = ` {date} {description} {status} {displayAmount} ` rows.push(row) `
{uncollectableTable} {rows}
DATE DESCRIPTION STATUS AMOUNT
Next
No more payment history
BACK

` selectionMade: (selection, e) -> e.preventDefault() #@getUncollectables() @setState({selected: selection}) activeTile: (selected = this.state.selected) -> if selected? selected else @tiles()[-1] createTileLink: (i, tile) -> if this.state.selected? active = this.state.selected == tile else active = i == 0 tileClasses = {activeTile: active, 'profile-tile': true} tileClasses[@myRole()] = true tileClasses = classNames(tileClasses) classes = classNames({last: i == @tiles().length - 1}) return `
{tile}
` tiles: () -> #if @viewerStudent() # tiles = @STUDENT_TILES #else # tiles = @TEACHER_TILES #tiles @STUDENT_TILES myRole: () -> if @viewerStudent() 'student' else 'teacher' viewerStudent: () -> #!@viewerTeacher() true viewerTeacher: () -> this.state.user?.is_a_teacher onCustomBack: (customBack, e) -> e.preventDefault() context.location = customBack render: () -> mainContent = @mainContent() profileSelections = [] for tile, i in @tiles() profileSelections.push(@createTileLink(i, tile, profileSelections)) profileNavClasses = {"profile-nav": true} profileNavClasses[@myRole()] = true profileNavClasses = classNames(profileNavClasses) profileNav = `
{profileSelections}
` `
payment
management:
{profileNav}
{mainContent}
` onUpdate: (e) -> e.preventDefault() if this.state.updating return name = @root.find('input[name="name"]').val() if @isSchoolManaged() scheduling_communication = 'school' else scheduling_communication = 'teacher' correspondence_email = @root.find('input[name="correspondence_email"]').val() @setState({updating: true}) rest.updateSchool({ id: this.state.school.id, name: name, scheduling_communication: scheduling_communication, correspondence_email: correspondence_email }).done((response) => @onUpdateDone(response)).fail((jqXHR) => @onUpdateFail(jqXHR)) onUpdateDone: (response) -> @setState({school: response, userSchedulingComm: null, schoolName: null, updateErrors: null, updating: false}) @app.layout.notify({title: "update success", text: "Your school information has been successfully updated"}) onSubmitForm: (e) -> form = document.querySelector('#user-payment-submit') e.preventDefault() @onSubmit(form) onSubmit: (form) -> @resetErrors() #e.preventDefault() console.log("onSubmit") if !window.recurly? @app.layout.notify({ title: 'Payment System Not Loaded', text: "Please refresh this page and try to enter your info again. Sorry for the inconvenience!" }) else errored = false # do a client-side sweep 1st if !@root.find('input.first_name').val() errored = true @setState({firstNameError: true}) if !@root.find('input.last_name').val() errored = true @setState({lastNameError: true}) if !@root.find('input.address-1').val() errored = true @setState({address1Error: true}) if !@root.find('input.city').val() errored = true @setState({cityError: true}) if !@root.find('input.state').val() errored = true @setState({stateError: true}) if !@root.find('select.country').val() errored = true @setState({countryError: true}) if !@root.find('input.zip').val() errored = true @setState({zipCodeError: true}) if errored return #form = event.target @setState({updating: true}) window.recurly.token(@elements, form, @onRecurlyToken) onUpdateFail: (jqXHR) -> handled = false @setState({updating: false}) if jqXHR.status == 422 errors = JSON.parse(jqXHR.responseText) handled = true @setState({updateErrors: errors}) else console.log("error path not taken", jqXHR) onSubmitOld: (e) -> @resetErrors() e.preventDefault() if !window.Stripe? @app.layout.notify({ title: 'Payment System Not Loaded', text: "Please refresh this page and try to enter your info again. Sorry for the inconvenience!" }) else ccNumber = @root.find('input.card-number').val() expiration = @root.find('input.expiration').val() cvv = @root.find('input.cvv').val() inUS = @root.find('input.billing-address-in-us').is(':checked') zip = @root.find('input.zip').val() error = false if @state.shouldShowName name = @root.find('#set-user-on-card').val() if name.indexOf('Anonymous') > -1 @setState({nameError: true}) error = true if !$.payment.validateCardNumber(ccNumber) @setState({ccError: true}) error = true bits = expiration.split('/') if bits.length == 2 month = bits[0].trim() year = bits[1].trim() month = new Number(month) year = new Number(year) if year < 2000 year += 2000 if !$.payment.validateCardExpiry(month, year) @setState({expiryError: true}) error = true else @setState({expiryError: true}) error = true cardType = $.payment.cardType(ccNumber) if !$.payment.validateCardCVC(cvv, cardType) @setState({cvvError: true}) error = true if inUS && (!zip? || zip == '') @setState({zipCodeError: true}) if error return data = { number: ccNumber, cvc: cvv, exp_month: month, exp_year: year, } @setState({updating: true}) window.Stripe.card.createToken(data, (status, response) => (@stripeResponseHandler(status, response))); stripeResponseHandler: (status, response) -> console.log("stripe response", JSON.stringify(response)) if response.error @setState({updating: false}) if response.error.code == "invalid_number" @setState({ccError: true, cvvError: null, expiryError: null}) else if response.error.code == "invalid_cvc" @setState({ccError: null, cvvError: true, expiryError: null}) else if response.error.code == "invalid_expiry_year" || response.error.code == "invalid_expiry_month" @setState({ccError: null, cvvError: null, expiryError: true}) #@setState({userWantsUpdateCC: false}) #window.UserActions.refresh() @storeCC(response.id) storeCC: (token) -> if this.state.billingInUS zip = @root.find('input.zip').val() data = { token: token, zip: zip, test_drive: false, normal: false } if @state.shouldShowName data.name = @root.find('#set-user-on-card').val() @setState({updating: true}) rest.submitStripe(data).done((response) => @stripeSubmitted(response)).fail((jqXHR) => @stripeSubmitFailure(jqXHR)) stripeSubmitted: (response) -> @setState({updating: false}) logger.debug("stripe submitted: " + JSON.stringify(response)) @setState({userWantsUpdateCC: false}) #if @state.shouldShowName window.UserActions.refresh() if response.past_due context.JK.Banner.showAlert('Credit Card Updated', 'Thank you. Your credit card info has been updated.

We will try to bill any unpaid lessons within the next hour, and an email will be sent at that time.') else @app.layout.notify({title: 'Credit Card Updated', text: 'Your credit card info has been updated.'}) stripeSubmitFailure: (jqXHR) -> @setState({updating: false}) handled = false if jqXHR.status == 422 errors = JSON.parse(jqXHR.responseText) if errors.errors.name? @setState({name: errors.errors.name[0]}) handled = true else if errors.errors.user? @app.layout.notify({title: "Can't Update Credit Card", text: "You " + errors.errors.user[0] + '.' }) handled = true if !handled @app.notifyServerError(jqXHR, 'Credit Card Not Stored') onUnlockPaymentInfo: (e) -> e.preventDefault() @setState({userWantsUpdateCC: true}) onLockPaymentInfo: (e) -> e.preventDefault() @setState({userWantsUpdateCC: false}) reuseStoredCard: () -> !@state.userWantsUpdateCC && @state.user?['has_stored_credit_card?'] && @state.uncollectables.length == 0 })