1/* global Stripe */ 2 3import { Controller } from '@hotwired/stimulus' 4import { post } from '@rails/request.js' 5import 'stripe' 6 7// https://docs.stripe.com/payments/save-and-reuse?platform=web&ui=elements 8// Connects to data-controller="billing" 9export default class extends Controller { 10 static values = { apiKey: String, intent: String, path: String, frame: String } 11 static targets = ['payment', 'error', 'submitBtn'] 12 13 connect () { 14 const stripe = Stripe(this.apiKeyValue) 15 16 // https://docs.stripe.com/elements/appearance-api#theme 17 const appearance = { 18 theme: 'night' 19 } 20 21 const clientSecret = this.intentValue 22 const elements = stripe.elements({ clientSecret, appearance }) 23 24 const paymentElement = elements.create('payment', { layout: 'accordion', business: { name: 'Fluency' } }) 25 paymentElement.mount(this.paymentTarget) 26 27 const submitBtn = this.submitBtnTarget 28 const errorTarget = this.errorTarget 29 const path = this.pathValue 30 const frame = this.frameValue 31 const originalValue = submitBtn.value 32 33 // Surface load failure (network/setup issues rendering the Element) 34 // https://docs.stripe.com/js/element/events/on_loaderror 35 paymentElement.on('loaderror', (event) => { 36 errorTarget.textContent = 'We couldnât load the payment form. Please check your connection and refresh, or try again in a minute.' 37 submitBtn.disabled = true 38 }) 39 40 this.element.addEventListener('submit', async (event) => { 41 event.preventDefault() 42 43 // Prevent multiple form submissions 44 if (submitBtn.disabled) { 45 return 46 } 47 48 // Disable form submission while loading 49 submitBtn.disabled = true 50 51 // Set the text while submitting since Turbo isn't directly in use here 52 if (submitBtn.dataset.turboSubmitsWith !== undefined) { 53 submitBtn.value = submitBtn.dataset.turboSubmitsWith 54 } 55 56 const { error, setupIntent } = await stripe.confirmSetup({ 57 elements, 58 redirect: 'if_required', 59 confirmParams: { 60 return_url: path 61 } 62 }) 63 64 if (error) { 65 if (error?.code === 'processing_error') { 66 // override generic 67 // An error occurred while processing your card. Try again. 68 errorTarget.textContent = 'A processing error occurred while contacting the payment network. Please check your connection and refresh, or try again in a minute, or use a different card.' 69 } else { 70 // use Stripe's message 71 errorTarget.textContent = error.message 72 } 73 submitBtn.disabled = false 74 submitBtn.value = originalValue 75 } else { 76 post(path, { 77 responseKind: 'turbo-stream', 78 headers: { 79 'Turbo-Frame': frame 80 }, 81 body: JSON.stringify({ 82 setup_intent: setupIntent.id 83 }) 84 }) 85 .catch(postError => { 86 errorTarget.textContent = postError.message + '. Please check your connection and refresh, or try again in a minute.' 87 }).finally(data => { 88 submitBtn.disabled = false 89 submitBtn.value = originalValue 90 }) 91 } 92 }) 93 } 94};
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.