diff --git a/assets/js/cardpayments.js b/assets/js/cardpayments.js index be4f613b8..2e84a3f52 100644 --- a/assets/js/cardpayments.js +++ b/assets/js/cardpayments.js @@ -1,6 +1,7 @@ "use strict"; const STRIPE_PREPARE_PAYMENT_URL = API_BASE_URL + '/donations/stripe/payments/prepare'; +const STRIPE_SUBSCRIPTION_CHECKOUT_URL = API_BASE_URL + '/donations/stripe/subscriptions/checkout'; class OneTimePayment { @@ -135,32 +136,47 @@ class OneTimePayment { class RecurringPayment { /** - * Creates a new recurring payment object. - * @param {number} amount integer $$$ - * @param {string} currency EUR or USD - * @param {string} languageCode The IETF language tag of the locale to display Stripe placeholders and error strings in + * Initializes the recurring payment helper and stores a reference to the status object + * @param {Object} status + * @param {string} status.captcha The captcha (if captcha validation finished) or null + * @param {string} status.errorMessage An error message or null + * @param {boolean} status.inProgress Whether an async payment task is currently running + */ + constructor(status) { + this._status = status; + } + + /** + * Creates a Stripe Checkout Session and redirects to it + * @param {number} amount How many units of the given currency to pay per month + * @param {string} currency Which currency to pay in (EUR or USD) + * @param {string} languageCode The IETF language tag for Stripe Checkout UI locale */ checkout(amount, currency, languageCode) { - let plan = STRIPE_PLANS[currency]; + this._status.inProgress = true; + this._status.errorMessage = ''; + + const successUrl = window.location.href.split('#')[0] + 'thanks'; + const cancelUrl = window.location.href; + $.ajax({ - url: 'https://js.stripe.com/v3/', - cache: true, - dataType: 'script' + url: STRIPE_SUBSCRIPTION_CHECKOUT_URL, + type: 'POST', + data: { + amount: parseInt(amount), + currency: currency, + successUrl: successUrl, + cancelUrl: cancelUrl, + locale: languageCode, + captcha: this._status.captcha + } }).then(response => { - return window.Stripe(STRIPE_PK); - }).then(stripe => { - stripe.redirectToCheckout({ - items: [ - {plan: plan, quantity: parseInt(amount)} - ], - successUrl: window.location.href.split('#')[0] + 'thanks', - cancelUrl: window.location.href, - locale: languageCode - }).then(result => { - if (result.error) { - console.log(result.error.message); - } - }); + // Redirect to Stripe Checkout + window.location.href = response.url; + }).catch(error => { + console.error('Failed to create checkout session:', error); + this._status.errorMessage = error.responseJSON?.message || 'Failed to create checkout session'; + this._status.inProgress = false; }); } diff --git a/assets/js/const.template.js b/assets/js/const.template.js index 362b13799..4bbfe6169 100644 --- a/assets/js/const.template.js +++ b/assets/js/const.template.js @@ -13,4 +13,3 @@ const PADDLE_DISCOUNT_ID = '{{ .Site.Params.paddleDiscountId }}'; const PADDLE_DISCOUNT_CODE = '{{ .Site.Params.paddleDiscountCode }}'; const LEGACY_STORE_URL = '{{ .Site.Params.legacyStoreUrl }}'; const STRIPE_PK = '{{ .Site.Params.stripePk }}'; -const STRIPE_PLANS = {{ .Site.Params.stripePlans | jsonify }}; diff --git a/config/development/params.yaml b/config/development/params.yaml index 9c54e4d73..19d8629e3 100644 --- a/config/development/params.yaml +++ b/config/development/params.yaml @@ -22,6 +22,3 @@ paddleDiscountCode: WINTER2025 # STRIPE stripePk: pk_test_51RCM24IBZmkR4F9UiLBiSmsAnJvWqmHcDLxXR8ABKK1MNsZk3zCk2VJW7ZfaBlD81zpQxCX243sS3LEp9dABwiG800kJnGykDF -stripePlans: - EUR: plan_GgVY2JfD49bc02 - USD: plan_GgVZwj545E0uH3 diff --git a/config/production/params.yaml b/config/production/params.yaml index c90f47c66..a29b1377b 100644 --- a/config/production/params.yaml +++ b/config/production/params.yaml @@ -22,6 +22,3 @@ paddleDiscountCode: WINTER2025 # STRIPE stripePk: pk_live_eSasX216vGvC26GdbVwA011V -stripePlans: - EUR: plan_GgW4ovr7c6upzx - USD: plan_GejOEdJtfL3kdH diff --git a/config/staging/params.yaml b/config/staging/params.yaml index f9e514aec..37d3c9073 100644 --- a/config/staging/params.yaml +++ b/config/staging/params.yaml @@ -22,6 +22,3 @@ paddleDiscountCode: WINTER2025 # STRIPE stripePk: pk_test_51RCM24IBZmkR4F9UiLBiSmsAnJvWqmHcDLxXR8ABKK1MNsZk3zCk2VJW7ZfaBlD81zpQxCX243sS3LEp9dABwiG800kJnGykDF -stripePlans: - EUR: plan_GgVY2JfD49bc02 - USD: plan_GgVZwj545E0uH3 diff --git a/layouts/partials/captcha.html b/layouts/partials/captcha.html index 3616dad8d..0b50ae0b0 100644 --- a/layouts/partials/captcha.html +++ b/layouts/partials/captcha.html @@ -12,5 +12,5 @@ verifying: '{{ i18n "altcha_verifying" }}', waitAlert: '{{ i18n "altcha_waitAlert" }}' })" - x-ref="captcha" + x-ref="{{ with .ref }}{{ . }}{{ else }}captcha{{ end }}" > diff --git a/layouts/partials/donate-creditcard.html b/layouts/partials/donate-creditcard.html index 423345ee7..f1647c416 100644 --- a/layouts/partials/donate-creditcard.html +++ b/layouts/partials/donate-creditcard.html @@ -1,4 +1,4 @@ -
+
@@ -30,7 +30,7 @@
-
+
@@ -43,25 +43,30 @@

{{ partial "checkbox.html" (dict "context" . "alpineVariable" "acceptTerms" "label" (i18n "accept_privacy" | safeHTML)) }}

- - {{ $challengeUrl := printf "%s/donations/stripe/payments/challenge" .Site.Params.apiBaseUrl }} - {{ partial "captcha.html" (dict "challengeUrl" $challengeUrl "captchaPayload" "oneTimePaymentStatus.captcha" "captchaState" "captchaState") }} + {{ $oneTimeChallengeUrl := printf "%s/donations/stripe/payments/challenge" .Site.Params.apiBaseUrl }} + {{ partial "captcha.html" (dict "challengeUrl" $oneTimeChallengeUrl "captchaPayload" "oneTimePaymentStatus.captcha" "captchaState" "oneTimeCaptchaState" "ref" "oneTimeCaptcha") }}

-
+

{{ i18n "donate_creditcard_recurring_instruction" | safeHTML }}

{{ partial "checkbox.html" (dict "context" . "alpineVariable" "acceptTerms" "label" (i18n "accept_privacy" | safeHTML)) }}

- -
+ + {{ $recurringChallengeUrl := printf "%s/donations/stripe/subscriptions/challenge" .Site.Params.apiBaseUrl }} + {{ partial "captcha.html" (dict "challengeUrl" $recurringChallengeUrl "captchaPayload" "recurringPaymentStatus.captcha" "captchaState" "recurringCaptchaState" "ref" "recurringCaptcha") }} + +

+