1/* globals Stripe */ 2 3import { Controller } from '@hotwired/stimulus' 4 5export default class StripeController extends Controller { 6 static values = { 7 key: String, 8 completePath: String, 9 clientSecret: String, 10 email: String 11 } 12 13 static targets = ['stripeForm', 'paymentElement', 'linkAuthenticationElement', 'submitButton', 'messagesElement'] 14 15 connect() { 16 this.stripefy() 17 } 18 19 stripefy() { 20 const { stripe, clientSecretValue: secret, paymentElementTarget: paymentTag} = this 21 this.elements = stripe.elements({ clientSecret: secret, appearance, fonts }) 22 23 const paymentElement = this.elements.create('payment', { 24 fields: { 25 billingDetails: { 26 email: "never", 27 }, 28 }, 29 }) 30 paymentElement.mount(paymentTag) 31 32 this.addPrefilledLinkElement() 33 } 34 35 addPrefilledLinkElement() { 36 const { linkAuthenticationElementTarget: linkAuthTag, emailValue: email} = this 37 38 if (!email) return false 39 40 const linkAuthenticationElement = this.elements.create('linkAuthentication', {defaultValues: {email: email}}) 41 linkAuthenticationElement.mount(linkAuthTag) 42 } 43 44 get stripe() { 45 this._stripe ||= Stripe(this.keyValue) 46 47 return this._stripe 48 } 49 50 async submit(e) { 51 const { elements, stripe, completePathValue } = this 52 53 e.preventDefault() 54 this.catchDuplicateSubmission() 55 56 try { 57 const result = await stripe.confirmPayment({ 58 elements, 59 confirmParams: { 60 return_url: completePathValue, 61 payment_method_data: { 62 billing_details: { 63 email: this.emailValue 64 }, 65 }, 66 } 67 }) 68 69 if(result.error) { throw new Error(result.error.message) } 70 71 } catch (error) { 72 this.addMessage(error) 73 74 this.reEnableSubmittedForm() 75 return 76 } 77 } 78 79 addMessage(message) { 80 const { messagesElementTarget: element } = this 81 82 element.style.display = 'block' 83 element.innerHTML = message 84 } 85 86 catchDuplicateSubmission() { 87 const { formSubmitted } = this 88 89 if(formSubmitted) { return } 90 this.formSubmitted = true 91 this.submitButtonTarget.disabled = true 92 } 93 94 reEnableSubmittedForm() { 95 this.formSubmitted = false 96 this.submitButtonTarget.disabled = false 97 } 98} 99 100const appearance = { 101 theme: 'stripe', 102 103 variables: { 104 fontFamily: 'ff-scala-sans-pro, Ideal Sans, system-ui, sans-serif', 105 borderRadius: '0', 106 tabIconColor: 'white', 107 tabIconHoverColor: 'white', 108 tabIconSelectedColor: 'white', 109 tabLogoColor: 'dark', 110 tabLogoSelectedColor: 'dark' 111 }, 112 rules: { 113 '.Label': { 114 color: 'white' 115 }, 116 '.Input': { 117 color: 'black', 118 backgroundColor: 'white', 119 marginBottom: '10px' 120 }, 121 '.Error': { 122 color: 'white', 123 backgroundColor: 'red', 124 border: '2px solid white', 125 paddingLeft: '0.2em', 126 paddingRight: '0.2em', 127 paddingTop: '0.2em', 128 paddingBottom: '0.2em' 129 }, 130 '.TabLabel': { 131 color: 'white', 132 }, 133 '.Tab': { 134 color: getComputedStyle(document.body).getPropertyValue('--button-primary-color'), 135 backgroundColor: getComputedStyle(document.body).getPropertyValue('--button-primary-background-color'), 136 border: 'none', 137 boxShadow: '2px 2px white' 138 }, 139 '.Tab--selected': { 140 border: 'none', 141 boxShadow: '2px 2px white', 142 backgroundColor: getComputedStyle(document.body).getPropertyValue('--button-primary-hover-background-color') 143 }, 144 '.Tab:hover': { 145 border: 'none', 146 color: 'white', 147 backgroundColor: getComputedStyle(document.body).getPropertyValue('--button-primary-hover-background-color') 148 }, 149 '.Tab:active': { 150 border: 'none', 151 boxShadow: '2px 2px white', 152 backgroundColor: getComputedStyle(document.body).getPropertyValue('--button-primary-hover-background-color') 153 }, 154 '.TabIcon': { 155 color: getComputedStyle(document.body).getPropertyValue('--button-primary-color'), 156 }, 157 '.TabIcon:hover': { 158 color: getComputedStyle(document.body).getPropertyValue('--button-primary-color'), 159 }, 160 '.Link': { 161 color: 'white', 162 textDecoration: 'underline', 163 }, 164 '.TermsText': { 165 color: 'white' 166 }, 167 '.CheckboxLabel': { 168 color: 'white' 169 } 170 } 171 } 172 173const fonts = [{ 174 family: 'ff-scala-sans-pro', 175 src: 'url(https://use.typekit.net/af/0f7856/00000000000000003b9b1f5e/27/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3)', 176 weight: '300', 177 }];
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.