ducky/web

Paddy 2015-05-29 Parent:b9d0efb44eaa Child:62e0c0df28bb

3:7ae5dd64c482 Go to Latest

ducky/web/src/pages/payment.jsx

Stop ignoring our build folder. Now that our HTML lives in our build folder, we should probably stop ignoring it. This means that an `hg clone` followed by an `npm install` followed by an `npm run deploy` will work. I hope.

History
paddy@2 1 import app from 'ampersand-app'
paddy@2 2 import React from 'react'
paddy@2 3 import ScriptLoaderMixin from 'react-script-loader'
paddy@2 4 import LaddaButton from 'react-ladda'
paddy@2 5 import LaddaCSS from '../../node_modules/ladda/dist/ladda.min.css'
paddy@2 6 import HeroUnit from '../components/hero'
paddy@2 7 import ValidationError from '../components/validation-error'
paddy@2 8 import onboardStyles from '../styles/onboarding.scss'
paddy@2 9 import config from '../config'
paddy@2 10
paddy@2 11 export default React.createClass({
paddy@2 12 displayName: 'PaymentMethodPage',
paddy@2 13 mixins: [ScriptLoaderMixin.ReactScriptLoaderMixin, React.addons.LinkedStateMixin],
paddy@2 14 getScriptURL () {
paddy@2 15 return 'https://js.stripe.com/v2/'
paddy@2 16 },
paddy@2 17
paddy@2 18 getInitialState () {
paddy@2 19 return {
paddy@2 20 stripeLoading: true,
paddy@2 21 stripeFailedToLoad: false,
paddy@2 22 active: false,
paddy@2 23 errors: [],
paddy@2 24 number: null,
paddy@2 25 name: null,
paddy@2 26 cvc: null,
paddy@2 27 expireMonth: null,
paddy@2 28 expireYear: null,
paddy@2 29 }
paddy@2 30 },
paddy@2 31
paddy@2 32 nameValidationOutputs: {},
paddy@2 33 numberValidationOutputs: {},
paddy@2 34 cvcValidationOutputs: {},
paddy@2 35 expirationValidationOutputs: {},
paddy@2 36
paddy@2 37 onScriptLoaded () {
paddy@2 38 Stripe.setPublishableKey(config.stripeKey)
paddy@2 39 this.setState({stripeLoading: false})
paddy@2 40 },
paddy@2 41
paddy@2 42 onScriptError () {
paddy@2 43 this.setState({stripeFailedToLoad: true})
paddy@2 44 },
paddy@2 45
paddy@2 46 getChargeDate () {
paddy@2 47 let created = new Date()
paddy@2 48 if (app.me && app.me.profile && app.me.profile.created && app.me.profile.created < created) {
paddy@2 49 console.log("using register date...")
paddy@2 50 created = app.me.profile.created
paddy@2 51 }
paddy@2 52 let month = created.getMonth()
paddy@2 53 let day = created.getDate()
paddy@2 54 month = month + 1
paddy@2 55 if (day > 1) {
paddy@2 56 day = 1
paddy@2 57 month = month + 1
paddy@2 58 }
paddy@2 59 let result = new Date(created.toString())
paddy@2 60 result.setDate(day)
paddy@2 61 result.setMonth(month)
paddy@2 62 return result
paddy@2 63 },
paddy@2 64
paddy@2 65 addCard (e) {
paddy@2 66 e.preventDefault()
paddy@2 67 if (this.state.stripeLoading || this.state.stripeFailedToLoad) {
paddy@2 68 return
paddy@2 69 }
paddy@2 70 this.setState({active: true})
paddy@2 71 const t = this
paddy@2 72 const errors = []
paddy@2 73 Stripe.card.createToken({
paddy@2 74 number: this.state.number,
paddy@2 75 cvc: this.state.cvc,
paddy@2 76 exp_month: this.state.expireMonth,
paddy@2 77 exp_year: this.state.expireYear,
paddy@2 78 name: this.state.name,
paddy@2 79 }, function(status, response) {
paddy@2 80 console.log(status)
paddy@2 81 console.log(response)
paddy@2 82 if (response.error) {
paddy@2 83 if (response.error.type == 'card_error') {
paddy@2 84 switch (response.error.code) {
paddy@2 85 case 'incorrect_number':
paddy@2 86 errors.push({'error': 'invalid_value', 'field': '/number'})
paddy@2 87 break
paddy@2 88 case 'invalid_number':
paddy@2 89 errors.push({'error': 'invalid_format', 'field': '/number'})
paddy@2 90 break
paddy@2 91 case 'invalid_expiry_month':
paddy@2 92 errors.push({'error': 'invalid_format', 'field': '/expireMonth'})
paddy@2 93 break
paddy@2 94 case 'invalid_expiry_year':
paddy@2 95 errors.push({'error': 'invalid_format', 'field': '/expireYear'})
paddy@2 96 break
paddy@2 97 case 'invalid_cvc':
paddy@2 98 errors.push({'error': 'invalid_format', 'field': '/cvc'})
paddy@2 99 break
paddy@2 100 case 'expired_card':
paddy@2 101 errors.push({'error': 'invalid_value', 'field': '/expiration'})
paddy@2 102 break
paddy@2 103 case 'incorrect_cvc':
paddy@2 104 errors.push({'error': 'invalid_value', 'field': '/cvc'})
paddy@2 105 break
paddy@2 106 case 'incorrect_zip':
paddy@2 107 errors.push({'error': 'invalid_value', 'field': '/zip'})
paddy@2 108 break
paddy@2 109 case 'card_declined':
paddy@2 110 errors.push({'error': 'insufficient', 'field': '/balance'})
paddy@2 111 break
paddy@2 112 case 'missing':
paddy@2 113 errors.push({'error': 'missing', 'field': '/customer/card'})
paddy@2 114 break
paddy@2 115 case 'processing_error':
paddy@2 116 errors.push({'error': 'act_of_god', 'field': '/'})
paddy@2 117 break
paddy@2 118 case 'rate_limit':
paddy@2 119 errors.push({'error': 'access_denied', 'field': '/rate'})
paddy@2 120 break
paddy@2 121 default:
paddy@2 122 errors.push({'error': 'act_of_god', 'field': '/'})
paddy@2 123 break
paddy@2 124 }
paddy@2 125 } else {
paddy@2 126 console.log('Error:', response.error.message)
paddy@2 127 }
paddy@2 128 } else {
paddy@2 129 console.log('Sending '+response.id+' to server to create customer')
paddy@2 130 }
paddy@2 131 t.setState({active: false, errors: errors})
paddy@2 132 })
paddy@2 133 },
paddy@2 134
paddy@2 135 render () {
paddy@2 136 return (
paddy@2 137 <div className='container'>
paddy@2 138 <HeroUnit title='Add a Payment Method'>Gotta keep our servers online and food on the table.</HeroUnit>
paddy@2 139 <article className='onboarding payment'>
paddy@2 140 <p>Ducky costs $2 a month to use. We’ll charge the card you enter below on the first of every month until your account is disabled. You won’t be charged before {this.getChargeDate().toLocaleDateString(navigator.languages, {month: 'long', year: 'numeric', day: 'numeric'})}.</p>
paddy@2 141 <form onSubmit={this.addCard}>
paddy@2 142 <div>
paddy@2 143 <label htmlFor='name'>Cardholder Name</label>
paddy@2 144 <input id='name' type='text' placeholder='This is the name on your card' valueLink={this.linkState('name')} />
paddy@2 145 <ValidationError errors={this.state.errors} field='/name' outputs={this.nameValidationOutputs} />
paddy@2 146
paddy@2 147 <label htmlFor='cardNumber'>Card Number</label>
paddy@2 148 <input id='cardNumber' type='text' placeholder='4242 4242 4242 4242' valueLink={this.linkState('number')} />
paddy@2 149 <ValidationError errors={this.state.errors} field='/number' outputs={this.numberValidationOutputs} />
paddy@2 150
paddy@2 151 <label htmlFor='cvc'>Security Code / CVC</label>
paddy@2 152 <input id='cvc' className='cvc' type='password' placeholder='123' valueLink={this.linkState('cvc')} />
paddy@2 153
paddy@2 154 <label htmlFor='expireMonth' className='expiration'>Expires</label>
paddy@2 155 <input id='expireMonth' className='expiration month' type='text' placeholder='01' valueLink={this.linkState('expireMonth')} />
paddy@2 156 <input id='expireYear' className='expiration year' type='text' placeholder='15' valueLink={this.linkState('expireYear')} />
paddy@2 157 <ValidationError errors={this.state.errors} field='/cvc' outputs={this.cvcValidationOutputs} />
paddy@2 158 <ValidationError errors={this.state.errors} field='/expiration' outputs={this.expirationValidationOutputs} />
paddy@2 159
paddy@2 160 <div className='actionbuttons'>
paddy@2 161 <button type='button' onClick={this.skip} className='ladda-button' disabled={this.state.active}>Not Now</button>
paddy@2 162 <LaddaButton style='expand-right' active={this.state.active}>
paddy@2 163 <button type='submit' className='primary' disabled={this.state.stripeLoading || this.state.stripeFailedToLoad || this.state.active || this.state.errors.length}>Add Card</button>
paddy@2 164 </LaddaButton>
paddy@2 165 </div>
paddy@2 166 </div>
paddy@2 167 </form>
paddy@2 168 </article>
paddy@2 169 </div>
paddy@2 170 )
paddy@2 171 }
paddy@2 172 })