PageSourceSearch

https://fluencypay.ai/assets/shared/billing/billing_controller-43c…15f39551df410c50d723e322601f0104e7c77d47.js

js fluencypay.ai collected 2026-10-05 15:33:45 UTC 3,191 bytes, 94 lines download raw bytes

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.