@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Clean up rendering of credit card form

Summary:
General cleanup and separation into generic vs Stripe blocks of code.

- There was an old CC form view for Stripe stuff that I never cleaned up; clean that up.
- Move non-Stripe CC form rendering into a base class (Balanced can reuse it).
- Move non-Stripe CC form JS into a shareable class.
- Simplify JS a bit (JX.Workflow can add extra parameters to a request, so we don't need hidden inputs).
- Genericize CSS.
- Depend on Stripe JS directly, if they're down we're not going to be able to add cards anyway.

Ref T2787.

Test Plan: Hit all Stripe errors and added new cards.

Reviewers: btrahan, chad

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2787

Differential Revision: https://secure.phabricator.com/D5758

+204 -270
+22 -19
src/__celerity_resource_map__.php
··· 2258 2258 ), 2259 2259 'javelin-behavior-stripe-payment-form' => 2260 2260 array( 2261 - 'uri' => '/res/e4149d37/rsrc/js/application/phortune/behavior-stripe-payment-form.js', 2261 + 'uri' => '/res/62dc91b4/rsrc/js/application/phortune/behavior-stripe-payment-form.js', 2262 2262 'type' => 'js', 2263 2263 'requires' => 2264 2264 array( ··· 2266 2266 1 => 'javelin-dom', 2267 2267 2 => 'javelin-json', 2268 2268 3 => 'javelin-workflow', 2269 + 4 => 'phortune-credit-card-form', 2269 2270 ), 2270 2271 'disk' => '/rsrc/js/application/phortune/behavior-stripe-payment-form.js', 2271 2272 ), ··· 3588 3589 ), 3589 3590 'disk' => '/rsrc/css/application/pholio/pholio-inline-comments.css', 3590 3591 ), 3592 + 'phortune-credit-card-form' => 3593 + array( 3594 + 'uri' => '/res/7be5799a/rsrc/js/application/phortune/phortune-credit-card-form.js', 3595 + 'type' => 'js', 3596 + 'requires' => 3597 + array( 3598 + 0 => 'javelin-install', 3599 + 1 => 'javelin-dom', 3600 + ), 3601 + 'disk' => '/rsrc/js/application/phortune/phortune-credit-card-form.js', 3602 + ), 3603 + 'phortune-credit-card-form-css' => 3604 + array( 3605 + 'uri' => '/res/563c8c6d/rsrc/css/application/phortune/phortune-credit-card-form.css', 3606 + 'type' => 'css', 3607 + 'requires' => 3608 + array( 3609 + ), 3610 + 'disk' => '/rsrc/css/application/phortune/phortune-credit-card-form.css', 3611 + ), 3591 3612 'phrequent-css' => 3592 3613 array( 3593 3614 'uri' => '/res/9d6f3eb7/rsrc/css/application/phrequent/phrequent.css', ··· 3920 3941 array( 3921 3942 ), 3922 3943 'disk' => '/rsrc/css/sprite-tokens.css', 3923 - ), 3924 - 'stripe-core' => 3925 - array( 3926 - 'uri' => '/res/fc74303d/rsrc/externals/stripe-js/stripe_core.js', 3927 - 'type' => 'js', 3928 - 'requires' => 3929 - array( 3930 - ), 3931 - 'disk' => '/rsrc/externals/stripe-js/stripe_core.js', 3932 - ), 3933 - 'stripe-payment-form-css' => 3934 - array( 3935 - 'uri' => '/res/634a6371/rsrc/css/application/phortune/stripe-payment-form.css', 3936 - 'type' => 'css', 3937 - 'requires' => 3938 - array( 3939 - ), 3940 - 'disk' => '/rsrc/css/application/phortune/stripe-payment-form.css', 3941 3944 ), 3942 3945 'syntax-highlighting-css' => 3943 3946 array(
+3 -2
src/__phutil_library_map__.php
··· 1583 1583 'PhortuneCart' => 'applications/phortune/storage/PhortuneCart.php', 1584 1584 'PhortuneCharge' => 'applications/phortune/storage/PhortuneCharge.php', 1585 1585 'PhortuneController' => 'applications/phortune/controller/PhortuneController.php', 1586 + 'PhortuneCreditCardForm' => 'applications/phortune/view/PhortuneCreditCardForm.php', 1586 1587 'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php', 1587 1588 'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php', 1588 1589 'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php', 1589 1590 'PhortuneMultiplePaymentProvidersException' => 'applications/phortune/exception/PhortuneMultiplePaymentProvidersException.php', 1590 1591 'PhortuneNoPaymentProviderException' => 'applications/phortune/exception/PhortuneNoPaymentProviderException.php', 1592 + 'PhortuneNotImplementedException' => 'applications/phortune/exception/PhortuneNotImplementedException.php', 1591 1593 'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php', 1592 1594 'PhortunePaymentMethodEditController' => 'applications/phortune/controller/PhortunePaymentMethodEditController.php', 1593 1595 'PhortunePaymentMethodListController' => 'applications/phortune/controller/PhortunePaymentMethodListController.php', ··· 1604 1606 'PhortuneProductTransactionQuery' => 'applications/phortune/query/PhortuneProductTransactionQuery.php', 1605 1607 'PhortuneProductViewController' => 'applications/phortune/controller/PhortuneProductViewController.php', 1606 1608 'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.php', 1607 - 'PhortuneStripePaymentFormView' => 'applications/phortune/view/PhortuneStripePaymentFormView.php', 1608 1609 'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php', 1609 1610 'PhortuneTestExtraPaymentProvider' => 'applications/phortune/provider/__tests__/PhortuneTestExtraPaymentProvider.php', 1610 1611 'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php', ··· 3309 3310 'PhortuneMonthYearExpiryControl' => 'AphrontFormControl', 3310 3311 'PhortuneMultiplePaymentProvidersException' => 'Exception', 3311 3312 'PhortuneNoPaymentProviderException' => 'Exception', 3313 + 'PhortuneNotImplementedException' => 'Exception', 3312 3314 'PhortunePaymentMethod' => 3313 3315 array( 3314 3316 0 => 'PhortuneDAO', ··· 3332 3334 'PhortuneProductTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 3333 3335 'PhortuneProductViewController' => 'PhortuneController', 3334 3336 'PhortunePurchase' => 'PhortuneDAO', 3335 - 'PhortuneStripePaymentFormView' => 'AphrontView', 3336 3337 'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider', 3337 3338 'PhortuneTestExtraPaymentProvider' => 'PhortunePaymentProvider', 3338 3339 'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
+1 -1
src/applications/phortune/provider/PhortunePaymentProvider.php
··· 81 81 PhortuneCharge $charge); 82 82 83 83 84 - 85 84 /* -( Adding Payment Methods )--------------------------------------------- */ 86 85 87 86 ··· 111 110 array $errors) { 112 111 throw new PhortuneNotImplementedException($this); 113 112 } 113 + 114 114 115 115 }
+11 -65
src/applications/phortune/provider/PhortuneStripePaymentProvider.php
··· 85 85 86 86 $card_errors = $request->getStr('cardErrors'); 87 87 $stripe_token = $request->getStr('stripeToken'); 88 + 89 + $errors = array(); 88 90 if ($card_errors) { 89 91 $raw_errors = json_decode($card_errors); 90 92 $errors = $this->parseRawCreatePaymentMethodErrors($raw_errors); ··· 140 142 AphrontRequest $request, 141 143 array $errors) { 142 144 143 - $e_card_number = isset($errors['number']) ? pht('Invalid') : true; 144 - $e_card_cvc = isset($errors['cvc']) ? pht('Invalid') : true; 145 - $e_card_exp = isset($errors['exp']) ? pht('Invalid') : null; 146 - 147 - $user = $request->getUser(); 148 - 149 - $form_id = celerity_generate_unique_node_id(); 150 - require_celerity_resource('stripe-payment-form-css'); 151 - require_celerity_resource('aphront-tooltip-css'); 152 - Javelin::initBehavior('phabricator-tooltips'); 153 - 154 - $form = id(new AphrontFormView()) 155 - ->setID($form_id) 156 - ->appendChild( 157 - id(new AphrontFormMarkupControl()) 158 - ->setLabel('') 159 - ->setValue( 160 - javelin_tag( 161 - 'div', 162 - array( 163 - 'class' => 'credit-card-logos', 164 - 'sigil' => 'has-tooltip', 165 - 'meta' => array( 166 - 'tip' => 'We support Visa, Mastercard, American Express, '. 167 - 'Discover, JCB, and Diners Club.', 168 - 'size' => 440, 169 - ) 170 - )))) 171 - ->appendChild( 172 - id(new AphrontFormTextControl()) 173 - ->setLabel('Card Number') 174 - ->setDisableAutocomplete(true) 175 - ->setSigil('number-input') 176 - ->setError($e_card_number)) 177 - ->appendChild( 178 - id(new AphrontFormTextControl()) 179 - ->setLabel('CVC') 180 - ->setDisableAutocomplete(true) 181 - ->setSigil('cvc-input') 182 - ->setError($e_card_cvc)) 183 - ->appendChild( 184 - id(new PhortuneMonthYearExpiryControl()) 185 - ->setLabel('Expiration') 186 - ->setUser($user) 187 - ->setError($e_card_exp)) 188 - ->appendChild( 189 - javelin_tag( 190 - 'input', 191 - array( 192 - 'hidden' => true, 193 - 'name' => 'stripeToken', 194 - 'sigil' => 'stripe-token-input', 195 - ))) 196 - ->appendChild( 197 - javelin_tag( 198 - 'input', 199 - array( 200 - 'hidden' => true, 201 - 'name' => 'cardErrors', 202 - 'sigil' => 'card-errors-input' 203 - ))); 145 + $ccform = id(new PhortuneCreditCardForm()) 146 + ->setUser($request->getUser()) 147 + ->setCardNumberError(isset($errors['number']) ? pht('Invalid') : true) 148 + ->setCardCVCError(isset($errors['cvc']) ? pht('Invalid') : true) 149 + ->setCardExpirationError(isset($errors['exp']) ? pht('Invalid') : null) 150 + ->addScript('https://js.stripe.com/v2/'); 204 151 205 - require_celerity_resource('stripe-core'); 206 152 Javelin::initBehavior( 207 153 'stripe-payment-form', 208 154 array( 209 - 'stripePublishKey' => $this->getPublishableKey(), 210 - 'root' => $form_id, 155 + 'stripePublishableKey' => $this->getPublishableKey(), 156 + 'formID' => $ccform->getFormID(), 211 157 )); 212 158 213 - return $form; 159 + return $ccform->buildForm(); 214 160 } 215 161 216 162
+103
src/applications/phortune/view/PhortuneCreditCardForm.php
··· 1 + <?php 2 + 3 + final class PhortuneCreditCardForm { 4 + 5 + private $formID; 6 + private $scripts = array(); 7 + private $user; 8 + 9 + private $cardNumberError; 10 + private $cardCVCError; 11 + private $cardExpirationError; 12 + 13 + public function setUser(PhabricatorUser $user) { 14 + $this->user = $user; 15 + return $this; 16 + } 17 + 18 + public function setCardExpirationError($card_expiration_error) { 19 + $this->cardExpirationError = $card_expiration_error; 20 + return $this; 21 + } 22 + 23 + public function setCardCVCError($card_cvc_error) { 24 + $this->cardCVCError = $card_cvc_error; 25 + return $this; 26 + } 27 + 28 + public function setCardNumberError($card_number_error) { 29 + $this->cardNumberError = $card_number_error; 30 + return $this; 31 + } 32 + 33 + public function addScript($script_uri) { 34 + $this->scripts[] = $script_uri; 35 + return $this; 36 + } 37 + 38 + public function getFormID() { 39 + if (!$this->formID) { 40 + $this->formID = celerity_generate_unique_node_id(); 41 + } 42 + return $this->formID; 43 + } 44 + 45 + public function buildForm() { 46 + $form_id = $this->getFormID(); 47 + 48 + require_celerity_resource('phortune-credit-card-form-css'); 49 + require_celerity_resource('phortune-credit-card-form'); 50 + 51 + require_celerity_resource('aphront-tooltip-css'); 52 + Javelin::initBehavior('phabricator-tooltips'); 53 + 54 + $form = new AphrontFormView(); 55 + 56 + foreach ($this->scripts as $script) { 57 + $form->appendChild( 58 + phutil_tag( 59 + 'script', 60 + array( 61 + 'type' => 'text/javascript', 62 + 'src' => $script, 63 + ))); 64 + } 65 + 66 + $form 67 + ->setID($form_id) 68 + ->appendChild( 69 + id(new AphrontFormMarkupControl()) 70 + ->setLabel('') 71 + ->setValue( 72 + javelin_tag( 73 + 'div', 74 + array( 75 + 'class' => 'credit-card-logos', 76 + 'sigil' => 'has-tooltip', 77 + 'meta' => array( 78 + 'tip' => 'We support Visa, Mastercard, American Express, '. 79 + 'Discover, JCB, and Diners Club.', 80 + 'size' => 440, 81 + ) 82 + )))) 83 + ->appendChild( 84 + id(new AphrontFormTextControl()) 85 + ->setLabel('Card Number') 86 + ->setDisableAutocomplete(true) 87 + ->setSigil('number-input') 88 + ->setError($this->cardNumberError)) 89 + ->appendChild( 90 + id(new AphrontFormTextControl()) 91 + ->setLabel('CVC') 92 + ->setDisableAutocomplete(true) 93 + ->setSigil('cvc-input') 94 + ->setError($this->cardCVCError)) 95 + ->appendChild( 96 + id(new PhortuneMonthYearExpiryControl()) 97 + ->setLabel('Expiration') 98 + ->setUser($this->user) 99 + ->setError($this->cardExpirationError)); 100 + 101 + return $form; 102 + } 103 + }
-119
src/applications/phortune/view/PhortuneStripePaymentFormView.php
··· 1 - <?php 2 - 3 - final class PhortuneStripePaymentFormView extends AphrontView { 4 - private $stripeKey; 5 - private $cardNumberError; 6 - private $cardCVCError; 7 - private $cardExpirationError; 8 - 9 - public function setStripeKey($key) { 10 - $this->stripeKey = $key; 11 - return $this; 12 - } 13 - private function getStripeKey() { 14 - return $this->stripeKey; 15 - } 16 - 17 - public function setCardNumberError($error) { 18 - $this->cardNumberError = $error; 19 - return $this; 20 - } 21 - private function getCardNumberError() { 22 - return $this->cardNumberError; 23 - } 24 - 25 - public function setCardCVCError($error) { 26 - $this->cardCVCError = $error; 27 - return $this; 28 - } 29 - private function getCardCVCError() { 30 - return $this->cardCVCError; 31 - } 32 - 33 - public function setCardExpirationError($error) { 34 - $this->cardExpirationError = $error; 35 - return $this; 36 - } 37 - private function getCardExpirationError() { 38 - return $this->cardExpirationError; 39 - } 40 - 41 - public function render() { 42 - $form_id = celerity_generate_unique_node_id(); 43 - require_celerity_resource('stripe-payment-form-css'); 44 - require_celerity_resource('aphront-tooltip-css'); 45 - Javelin::initBehavior('phabricator-tooltips'); 46 - 47 - $form = id(new AphrontFormView()) 48 - ->setID($form_id) 49 - ->setUser($this->getUser()) 50 - ->appendChild( 51 - id(new AphrontFormMarkupControl()) 52 - ->setLabel('') 53 - ->setValue( 54 - javelin_tag( 55 - 'div', 56 - array( 57 - 'class' => 'credit-card-logos', 58 - 'sigil' => 'has-tooltip', 59 - 'meta' => array( 60 - 'tip' => 'We support Visa, Mastercard, American Express, '. 61 - 'Discover, JCB, and Diners Club.', 62 - 'size' => 440, 63 - ) 64 - )))) 65 - ->appendChild( 66 - id(new AphrontFormTextControl()) 67 - ->setLabel('Card Number') 68 - ->setDisableAutocomplete(true) 69 - ->setSigil('number-input') 70 - ->setError($this->getCardNumberError())) 71 - ->appendChild( 72 - id(new AphrontFormTextControl()) 73 - ->setLabel('CVC') 74 - ->setDisableAutocomplete(true) 75 - ->setSigil('cvc-input') 76 - ->setError($this->getCardCVCError())) 77 - ->appendChild( 78 - id(new PhortuneMonthYearExpiryControl()) 79 - ->setLabel('Expiration') 80 - ->setUser($this->getUser()) 81 - ->setError($this->getCardExpirationError())) 82 - ->appendChild( 83 - javelin_tag( 84 - 'input', 85 - array( 86 - 'hidden' => true, 87 - 'name' => 'stripeToken', 88 - 'sigil' => 'stripe-token-input', 89 - ))) 90 - ->appendChild( 91 - javelin_tag( 92 - 'input', 93 - array( 94 - 'hidden' => true, 95 - 'name' => 'cardErrors', 96 - 'sigil' => 'card-errors-input' 97 - ))) 98 - ->appendChild( 99 - phutil_tag( 100 - 'input', 101 - array( 102 - 'hidden' => true, 103 - 'name' => 'stripeKey', 104 - 'value' => $this->getStripeKey(), 105 - ))) 106 - ->appendChild( 107 - id(new AphrontFormSubmitControl()) 108 - ->setValue('Add Payment Method')); 109 - 110 - Javelin::initBehavior( 111 - 'stripe-payment-form', 112 - array( 113 - 'stripePublishKey' => $this->getStripeKey(), 114 - 'root' => $form_id, 115 - )); 116 - 117 - return $form->render(); 118 - } 119 - }
+1 -1
webroot/rsrc/css/application/phortune/stripe-payment-form.css webroot/rsrc/css/application/phortune/phortune-credit-card-form.css
··· 1 1 /** 2 - * @provides stripe-payment-form-css 2 + * @provides phortune-credit-card-form-css 3 3 */ 4 4 5 5 .credit-card-logos {
-17
webroot/rsrc/externals/stripe-js/stripe_core.js
··· 1 - /** 2 - * @provides stripe-core 3 - * @do-not-minify 4 - */ 5 - (function(c){function k(a){return a.replace(/^\s+|\s+$/g,"")}function n(){if(!c.publishableKey)throw"No Publishable API Key: Call Stripe.setPublishableKey to provide your key.";}var d=null,l={};typeof window!=="undefined"&&!window.JSON&&(window.JSON={});(function(){if(typeof JSON.parse!=="function")JSON.parse=function(a,b){function d(a,e){var c,h,f=a[e];if(f&&typeof f==="object")for(c in f)Object.hasOwnProperty.call(f,c)&&(h=d(f,c),h!==void 0?f[c]=h:delete f[c]);return b.call(a,e,f)}var e=RegExp("[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]", 6 - "g"),a=String(a);e.lastIndex=0;e.test(a)&&(a=a.replace(e,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return e=eval("("+a+")"),typeof b==="function"?d({"":e},""):e;throw new SyntaxError("JSON.parse");}})();var v=function(a){function b(){d=null;var a=document.getElementsByTagName("body")[0], 7 - b=document.createElement("iframe");m="stripeFrame"+(new Date).getTime();q=j.apiURL+"/js/v1/apitunnel.html";var i=q+"#"+encodeURIComponent(window.location.href);b.setAttribute("src",i);b.setAttribute("name",m);b.setAttribute("id",m);b.setAttribute("frameborder","0");b.setAttribute("scrolling","no");b.setAttribute("allowtransparency","true");b.setAttribute("width",0);b.setAttribute("height",0);b.setAttribute("style","position:absolute;top:0;left:0;width:0;height:0");i=function(){d=window.frames[m]; 8 - c()};b.attachEvent?b.attachEvent("onload",i):b.onload=i;a.appendChild(b)}function c(){if(d){var b=o.length;if(b>0){for(var e=0;e<b;++e){var i=o[e].message,r=i.id;h[r]=o[e].callback;a.postMessage(i,j.apiURL,q,d);f[r]=window.setTimeout(function(a){h[a](504,{error:{message:"There was an error processing your card"}});delete h[a];delete f[a]},6E4,r)}o=[]}}}if(typeof a==="undefined"){var a={},e=function(a){if(typeof a==="undefined"){var a={},b=function(){var a={};a.serialize=function(b,e){var d=[],c;for(c in b)if(b.hasOwnProperty(c)){var u= 9 - e?e+"["+c+"]":c,f=b[c];d.push(typeof f=="object"?a.serialize(f,u):encodeURIComponent(u)+"="+encodeURIComponent(f))}return d.join("&")};a.deserialize=function(a){for(var b={},a=a.split("&"),e=a.length,c=null,d=null,i=0;i<e;++i){d=a[i].split("=");d[0]=decodeURIComponent(d[0]);d[1]=decodeURIComponent(d[1]);for(var f=d[0],c=[],g=-1;(g=f.indexOf("["))!==-1;)c.push(f.substr(g,f.indexOf("]")-g+1)),f=f.substr(f.indexOf("]")+1);if(c.length===0)b[d[0]]=d[1];else{g=d[0].substr(0,d[0].indexOf("["));typeof b[g]=== 10 - "undefined"&&(b[g]={});for(var f=b[g],t=c.length,p=0;p<t-1;++p)g=c[p].substr(1,c[p].length-2),typeof f[g]==="undefined"&&(f[g]={}),f=f[g];c=c[t-1];g=c.substr(1,c.length-2);f[g]=d[1]}}return b};return a};typeof a!=="undefined"?a=b():exports.createSerializer=b}return{postMessage:function(b,c,d,e){if(typeof window!=="undefined")b=a.serialize(b),typeof window.postMessage==="undefined"?e.location.href=d+"#"+ +new Date+Math.floor(Math.random()*1E3)+"&"+b:e.postMessage(b,c)},receiveMessage:function(b,c){if(typeof window!== 11 - "undefined")if(window.postMessage)attachedCallback=function(d){if(d.origin.toLowerCase()!==c.toLowerCase())return!1;b(a.deserialize(d.data))},window.addEventListener?window.addEventListener("message",attachedCallback,!1):window.attachEvent("onmessage",attachedCallback);else{var d=window.location.hash;setInterval(function(){var c=window.location.hash,e=/^#?\d+&/;if(c!==d&&e.test(c))d=c,window.location.hash="",b(a.deserialize(c.replace(e,"")))},100)}}}};typeof a!=="undefined"?a=e():exports.createXD= 12 - e}var d=null,o=[],l=0,h={},f={},k=!1,m,q,j={apiURL:"https://api.stripe.com",onMessage:function(a){var b=a.id,c=null,c=a.response===null||a.response===""?{error:{message:"There was an error processing your card"}}:JSON.parse(a.response);h[b](parseInt(a.status),c);window.clearTimeout(f[b]);delete h[b];delete f[b]}},n=!1,s=function(){b();n||(a.receiveMessage(j.onMessage,j.apiURL),n=!0)};j.init=function(){if(!m||!document.getElementById(m))typeof document!=="undefined"&&document&&document.body?s():typeof window!== 13 - "undefined"&&window&&!k&&(window.addEventListener?window.addEventListener("load",s,!1):window.attachEvent&&window.attachEvent("onload",s)),k=!0};j.callAPI=function(a,b,d,e,f){if(a!=="POST"&&a!=="GET"&&a!=="DELETE")throw"You can only call the API with POST, GET or DELETE";j.init();var h=(l++).toString();o.push({message:{id:h,method:a,url:"/v1/"+b,params:d,key:e},callback:f});c()};return j};typeof l!=="undefined"?l=v():exports.createTransport=v;c.transport=l;c.validateCardNumber=function(a){var a=a.replace(/\s+|-/g, 14 - ""),b;if(b=a.length>=10)if(b=a.length<=16)if(a.match(/^[0-9]+$/)===null)b=!1;else{var a=a.split("").reverse().join(""),c=0,d;for(b=0;b<a.length;++b)d=parseInt(a.charAt(b),10),b%2!=0&&(d*=2),c+=d<10?d:d-9;b=c!=0&&c%10==0}return b};c.cardType=function(a){if(!d){d={};for(var b=40;b<=49;++b)d[b]="Visa";for(b=50;b<=59;++b)d[b]="MasterCard";d[34]=d[37]="American Express";d[60]=d[62]=d[64]=d[65]="Discover";d[35]="JCB";d[30]=d[36]=d[38]=d[39]="Diners Club"}a=d[a.substr(0,2)];return typeof a==="undefined"? 15 - "Unknown":a};c.validateCVC=function(a){a=k(a);return a.match(/^[0-9]+$/)!==null&&a.length>=3&&a.length<=4};c.validateExpiry=function(a,b){var a=k(a),b=k(b),c=new Date;return a.match(/^[0-9]+$/)!==null&&b.match(/^[0-9]+$/)!==null&&b>c.getFullYear()||b==c.getFullYear()&&a>=c.getMonth()+1};c.createToken=function(a,b,d){typeof b==="function"&&(d=b,b=null);n();var e={expMonth:"exp_month",expYear:"exp_year",addressLine1:"address_line_1",addressLine2:"address_line_2",addressZip:"address_zip",addressState:"address_state", 16 - addressCountry:"address_country"};for(convertibleParam in e)e.hasOwnProperty(convertibleParam)&&a.hasOwnProperty(convertibleParam)&&(a[e[convertibleParam]]=a[convertibleParam],delete a[convertibleParam]);params={card:a};b!==null&&(params.amount=b);c.transport.callAPI("POST","tokens",params,c.publishableKey,d)};c.getToken=function(a,b){n();c.transport.callAPI("GET","tokens/"+a,{},c.publishableKey,b)};c.setPublishableKey=function(a){c.publishableKey=a};l.init()})(typeof exports!=="undefined"&&exports!== 17 - null?exports:window.Stripe={});
+25 -46
webroot/rsrc/js/application/phortune/behavior-stripe-payment-form.js
··· 4 4 * javelin-dom 5 5 * javelin-json 6 6 * javelin-workflow 7 + * phortune-credit-card-form 7 8 */ 8 9 9 10 JX.behavior('stripe-payment-form', function(config) { 10 - Stripe.setPublishableKey(config.stripePublishKey); 11 + Stripe.setPublishableKey(config.stripePublishableKey); 11 12 12 - var root = JX.$(config.root); 13 - var cardErrors = JX.DOM.find(root, 'input', 'card-errors-input'); 14 - var stripeToken = JX.DOM.find(root, 'input', 'stripe-token-input'); 15 - 16 - var getCardData = function() { 17 - return { 18 - number : JX.DOM.find(root, 'input', 'number-input').value, 19 - cvc : JX.DOM.find(root, 'input', 'cvc-input' ).value, 20 - month : JX.DOM.find(root, 'select', 'month-input' ).value, 21 - year : JX.DOM.find(root, 'select', 'year-input' ).value 22 - }; 23 - } 13 + var root = JX.$(config.formID); 14 + var ccform = new JX.PhortuneCreditCardForm(root); 24 15 25 16 var onsubmit = function(e) { 26 17 e.kill(); 27 18 28 19 // validate the card data with Stripe client API and submit the form 29 20 // with any detected errors 30 - var cardData = getCardData(); 31 - var errors = []; 21 + var cardData = ccform.getCardData(); 22 + var errors = []; 23 + 32 24 if (!Stripe.validateCardNumber(cardData.number)) { 33 25 errors.push('number'); 34 26 } 27 + 35 28 if (!Stripe.validateCVC(cardData.cvc)) { 36 29 errors.push('cvc'); 37 30 } 31 + 38 32 if (!Stripe.validateExpiry(cardData.month, cardData.year)) { 39 33 errors.push('expiry'); 40 34 } 41 35 42 36 if (errors.length) { 43 - cardErrors.value = JX.JSON.stringify(errors); 44 - JX.Workflow.newFromForm(root) 37 + JX.Workflow 38 + .newFromForm(root, {cardErrors: JX.JSON.stringify(errors)}) 45 39 .start(); 46 40 return; 47 41 } 48 42 49 - // no errors detected so contact Stripe asynchronously 50 - var submitData = { 51 - number : cardData.number, 52 - cvc : cardData.cvc, 53 - exp_month : cardData.month, 54 - exp_year : cardData.year 43 + var data = { 44 + number: cardData.number, 45 + cvc: cardData.cvc, 46 + exp_month: cardData.month, 47 + exp_year: cardData.year 55 48 }; 56 - Stripe.createToken(submitData, stripeResponseHandler); 57 - return false; 49 + 50 + Stripe.createToken(data, onresponse); 58 51 } 59 52 60 - var stripeResponseHandler = function(status, response) { 53 + var onresponse = function(status, response) { 54 + var errors = []; 55 + var token = null; 61 56 if (response.error) { 62 - var errors = []; 63 - switch (response.error.type) { 64 - case 'card_error': 65 - errors.push(response.error.code); 66 - break; 67 - case 'invalid_request_error': 68 - errors.push('invalid_request'); 69 - break; 70 - case 'api_error': 71 - default: 72 - errors.push('stripe'); 73 - break; 74 - } 75 - cardErrors.value = JX.JSON.stringify(errors); 57 + errors = [response.error.type]; 76 58 } else { 77 - // success - we can use the token to create a customer object with 78 - // Stripe and let the billing commence! 79 - var token = response['id']; 80 - cardErrors.value = '[]'; 81 - stripeToken.value = token; 59 + token = response.id; 82 60 } 83 61 84 - JX.Workflow.newFromForm(root) 62 + JX.Workflow 63 + .newFromForm(root, {cardErrors: errors, stripeToken: token}) 85 64 .start(); 86 65 } 87 66
+38
webroot/rsrc/js/application/phortune/phortune-credit-card-form.js
··· 1 + /** 2 + * @provides phortune-credit-card-form 3 + * @requires javelin-install 4 + * javelin-dom 5 + */ 6 + 7 + /** 8 + * Simple wrapper for credit card forms generated by `PhortuneCreditCardForm`. 9 + * 10 + * To construct an object for a form: 11 + * 12 + * new JX.PhortuneCreditCardForm(form_root_node); 13 + * 14 + * To read card data from a form: 15 + * 16 + * var data = ccform.getCardData(); 17 + */ 18 + JX.install('PhortuneCreditCardForm', { 19 + construct : function(root) { 20 + this._root = root; 21 + }, 22 + 23 + members : { 24 + _root : null, 25 + 26 + getCardData : function() { 27 + var root = this._root; 28 + 29 + return { 30 + number : JX.DOM.find(root, 'input', 'number-input').value, 31 + cvc : JX.DOM.find(root, 'input', 'cvc-input' ).value, 32 + month : JX.DOM.find(root, 'select', 'month-input' ).value, 33 + year : JX.DOM.find(root, 'select', 'year-input' ).value 34 + }; 35 + } 36 + } 37 + 38 + });