diff --git a/packages/plugin/src/Resources/js/scripts/front-end/payments/paypal/buttons.js b/packages/plugin/src/Resources/js/scripts/front-end/payments/paypal/buttons.js index 964b4e084..3bfd40e72 100644 --- a/packages/plugin/src/Resources/js/scripts/front-end/payments/paypal/buttons.js +++ b/packages/plugin/src/Resources/js/scripts/front-end/payments/paypal/buttons.js @@ -1 +1 @@ -(()=>{function O(t,r){var n={};for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&r.indexOf(e)<0&&(n[e]=t[e]);if(t!=null&&typeof Object.getOwnPropertySymbols=="function")for(var a=0,e=Object.getOwnPropertySymbols(t);a"u")return r.resolve(null);var n=T(t),e=n.url,a=n.attributes,o=a["data-namespace"]||"paypal",i=g(o);return a["data-js-sdk-library"]||(a["data-js-sdk-library"]="paypal-js"),P(e,a)&&i?r.resolve(i):I({url:e,attributes:a},r).then(function(){var s=g(o);if(s)return s;throw new Error("The window.".concat(o," global variable is not available."))})}function I(t,r){r===void 0&&(r=Promise),h(t,r);var n=t.url,e=t.attributes;if(typeof n!="string"||n.length===0)throw new Error("Invalid url.");if(typeof e<"u"&&typeof e!="object")throw new Error("Expected attributes to be an object.");return new r(function(a,o){if(typeof document>"u")return a();C({url:n,attributes:e,onSuccess:function(){return a()},onError:function(){var i=new Error('The script "'.concat(n,'" failed to load. Check the HTTP status code and response body in DevTools to learn more.'));return o(i)}})})}function g(t){return window[t]}function h(t,r){if(typeof t!="object"||t===null)throw new Error("Expected an options object.");var n=t.environment;if(n&&n!=="production"&&n!=="sandbox")throw new Error('The `environment` option must be either "production" or "sandbox".');if(typeof r<"u"&&typeof r!="function")throw new Error("Expected PromisePonyfill to be a function.")}var p=new Map,v={root:"[data-freeform-paypal-buttons]",orderInput:"[data-freeform-paypal-order]"},E={orders:"/freeform/payments/paypal/orders"},N=window.FREEFORM_DEBUG===!0;function c(...t){N&&console.error("PayPal:",...t)}function f(t,r){let n=t.closest('[data-field-type="paypal"]')||t.parentElement;if(!n)return;let e=n.querySelector("[data-ff-paypal-notice]");e||(e=document.createElement("div"),e.setAttribute("data-ff-paypal-notice","true"),e.style.marginTop="8px",e.style.color="#0a7",e.style.fontSize="0.95em",n.appendChild(e)),e.textContent=r}function y(t){let r=t.getAttribute("data-config");if(!r)return null;try{return JSON.parse(r)}catch{return null}}async function F(t,r,n){try{let e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","FF-PAYPAL-INTEGRATION":r},credentials:"same-origin",body:JSON.stringify({values:n})});if(!e.ok){let o=await e.text();return c("Order creation failed",e.status,e.statusText,o),null}let a=await e.json();return a!=null&&a.id?{id:String(a.id),status:a.status,approve:a.approve}:null}catch(e){return c("Network error during order creation:",e),null}}async function k(t,r,n,e){try{let a=await fetch(`${t}/${n}/capture`,{method:"POST",headers:{"Content-Type":"application/json","FF-PAYPAL-INTEGRATION":r},credentials:"same-origin",body:JSON.stringify(e)});if(!a.ok){let i=await a.text();return c("Order capture failed",a.status,a.statusText,i),null}let o=await a.json();return o!=null&&o.status&&(o!=null&&o.id)?{status:String(o.status),id:String(o.id)}:null}catch(a){return c("Network error during order capture:",a),null}}async function S(t,r,n){let e=`${t}-${r?"sandbox":"live"}-${n}`;if(p.has(e))return p.get(e);let a=await b({clientId:t,currency:(n||"USD").toUpperCase(),components:"buttons",intent:"capture",enableFunding:"venmo,paylater"});if(!a)throw new Error("PayPal SDK failed to load");return p.set(e,a),window.paypal||(window.paypal=a),a}async function A(t){let r=y(t);if(!r){c("Could not parse config from element",t);return}if(!r.clientId){c("No client ID provided");return}let n=t.closest("form");if(!n){c("Could not find form element");return}let e=n.querySelector(v.orderInput);if(!e){c("Could not find hidden order input");return}try{(await S(r.clientId,r.sandbox,r.currency||"USD")).Buttons({style:{layout:"vertical",color:"blue",shape:"rect",label:"paypal"},createOrder:async()=>{try{let o=new FormData(n),i={};o.forEach((d,u)=>{i[u]=d});let s=await F(E.orders,r.integration,i);if(!(s!=null&&s.id))throw c("Failed to create order or no order ID returned",s),new Error("Failed to create PayPal order");return e.value=s.id,s.id}catch(o){throw c("Error in createOrder:",o),o}},onApprove:async o=>{e.value=String(o.orderID||"");try{let i=new FormData(n),s={};i.forEach((u,l)=>{s[l]=u});let d=await k(E.orders,r.integration,String(o.orderID||""),{values:s});d&&d.status==="COMPLETED"?f(t,"Payment completed with PayPal. Click Submit to finish."):(c("Payment capture failed",d),f(t,"PayPal capture failed. Please try again."))}catch(i){c("Error during payment capture:",i),f(t,"PayPal capture error. Please try again.")}},onError:o=>{c("Payment error:",o)},onCancel:o=>{}}).render(t)}catch(a){c("Failed to initialize buttons:",a)}}document.addEventListener("DOMContentLoaded",async()=>{let t=document.querySelectorAll(v.root);if(t.length===0)return;let r=new Map;if(t.forEach(n=>{let e=y(n);e!=null&&e.clientId&&(r.has(e.clientId)||r.set(e.clientId,[]),r.get(e.clientId).push(n))}),r.size===0){c("No valid configurations found");return}r.forEach(async(n,e)=>{try{let a=y(n[0]);if(!a)return;await S(e,a.sandbox,a.currency||"USD"),n.forEach(o=>{A(o)})}catch(a){c(`Failed to load SDK for clientId ${e}:`,a)}})});})(); +(()=>{function O(e,t){var a={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(a[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var n=0,r=Object.getOwnPropertySymbols(e);n"u")return t.resolve(null);var a=C(e),r=a.url,n=a.attributes,o=n["data-namespace"]||"paypal",i=g(o);return n["data-js-sdk-library"]||(n["data-js-sdk-library"]="paypal-js"),P(r,n)&&i?t.resolve(i):I({url:r,attributes:n},t).then(function(){var s=g(o);if(s)return s;throw new Error("The window.".concat(o," global variable is not available."))})}function I(e,t){t===void 0&&(t=Promise),h(e,t);var a=e.url,r=e.attributes;if(typeof a!="string"||a.length===0)throw new Error("Invalid url.");if(typeof r<"u"&&typeof r!="object")throw new Error("Expected attributes to be an object.");return new t(function(n,o){if(typeof document>"u")return n();T({url:a,attributes:r,onSuccess:function(){return n()},onError:function(){var i=new Error('The script "'.concat(a,'" failed to load. Check the HTTP status code and response body in DevTools to learn more.'));return o(i)}})})}function g(e){return window[e]}function h(e,t){if(typeof e!="object"||e===null)throw new Error("Expected an options object.");var a=e.environment;if(a&&a!=="production"&&a!=="sandbox")throw new Error('The `environment` option must be either "production" or "sandbox".');if(typeof t<"u"&&typeof t!="function")throw new Error("Expected PromisePonyfill to be a function.")}var p=new Map,v={root:"[data-freeform-paypal-buttons]",orderInput:"[data-freeform-paypal-order]"},E={orders:"/freeform/payments/paypal/orders"},F=window.FREEFORM_DEBUG===!0;function c(...e){F&&console.error("PayPal:",...e)}function f(e,t,a="success"){let r=e.closest('[data-field-type="paypal"]')||e.parentElement;if(!r)return;let n=r.querySelector("[data-ff-paypal-notice]");n||(n=document.createElement("div"),n.setAttribute("data-ff-paypal-notice","true"),n.style.marginTop="8px",n.style.fontSize="0.95em",r.appendChild(n));let o={muted:"#555",success:"#0a7",error:"#b00020"};n.style.color=o[a],n.textContent=t}function N(e){var t;return(t=e.freeform)!=null?t:null}function m(e){let t=e.getAttribute("data-config");if(!t)return null;try{return JSON.parse(t)}catch{return null}}async function A(e,t,a){try{let r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","FF-PAYPAL-INTEGRATION":t},credentials:"same-origin",body:JSON.stringify({values:a})});if(!r.ok){let o=await r.text();return c("Order creation failed",r.status,r.statusText,o),null}let n=await r.json();return n!=null&&n.id?{id:String(n.id),status:n.status,approve:n.approve}:null}catch(r){return c("Network error during order creation:",r),null}}async function k(e,t,a,r){try{let n=await fetch(`${e}/${a}/capture`,{method:"POST",headers:{"Content-Type":"application/json","FF-PAYPAL-INTEGRATION":t},credentials:"same-origin",body:JSON.stringify(r)});if(!n.ok){let i=await n.text();return c("Order capture failed",n.status,n.statusText,i),null}let o=await n.json();return o!=null&&o.status&&(o!=null&&o.id)?{status:String(o.status),id:String(o.id)}:null}catch(n){return c("Network error during order capture:",n),null}}async function S(e,t,a){let r=`${e}-${t?"sandbox":"live"}-${a}`;if(p.has(r))return p.get(r);let n=await w({clientId:e,currency:(a||"USD").toUpperCase(),components:"buttons",intent:"capture",enableFunding:"venmo,paylater"});if(!n)throw new Error("PayPal SDK failed to load");return p.set(r,n),window.paypal||(window.paypal=n),n}async function R(e){let t=m(e);if(!t){c("Could not parse config from element",e);return}if(!t.clientId){c("No client ID provided");return}let a=e.closest("form");if(!a){c("Could not find form element");return}let r=a.querySelector(v.orderInput);if(!r){c("Could not find hidden order input");return}try{(await S(t.clientId,t.sandbox,t.currency||"USD")).Buttons({style:{layout:"vertical",color:"blue",shape:"rect",label:"paypal"},createOrder:async()=>{try{let o=new FormData(a),i={};o.forEach((d,u)=>{i[u]=d});let s=await A(E.orders,t.integration,i);if(!(s!=null&&s.id))throw c("Failed to create order or no order ID returned",s),new Error("Failed to create PayPal order");return r.value=s.id,s.id}catch(o){throw c("Error in createOrder:",o),o}},onApprove:async o=>{r.value=String(o.orderID||""),f(e,"Processing your payment\u2026","muted");try{let i=new FormData(a),s={};i.forEach((u,l)=>{s[l]=u});let d=await k(E.orders,t.integration,String(o.orderID||""),{values:s});if(d&&d.status==="COMPLETED"){let u=N(a);u?(f(e,"Payment complete. Your form is being submitted automatically.","success"),u.triggerSubmit()):f(e,"Payment completed with PayPal. Click Submit to finish.","success")}else c("Payment capture failed",d),f(e,"PayPal capture failed. Please try again.","error")}catch(i){c("Error during payment capture:",i),f(e,"PayPal capture error. Please try again.","error")}},onError:o=>{c("Payment error:",o)},onCancel:o=>{}}).render(e)}catch(n){c("Failed to initialize buttons:",n)}}document.addEventListener("DOMContentLoaded",async()=>{let e=document.querySelectorAll(v.root);if(e.length===0)return;let t=new Map;if(e.forEach(a=>{let r=m(a);r!=null&&r.clientId&&(t.has(r.clientId)||t.set(r.clientId,[]),t.get(r.clientId).push(a))}),t.size===0){c("No valid configurations found");return}t.forEach(async(a,r)=>{try{let n=m(a[0]);if(!n)return;await S(r,n.sandbox,n.currency||"USD"),a.forEach(o=>{R(o)})}catch(n){c(`Failed to load SDK for clientId ${r}:`,n)}})});})(); diff --git a/packages/plugin/src/Resources/js/scripts/front-end/payments/stripe/elements.js b/packages/plugin/src/Resources/js/scripts/front-end/payments/stripe/elements.js index e3bb68398..78a7eeb0c 100644 --- a/packages/plugin/src/Resources/js/scripts/front-end/payments/stripe/elements.js +++ b/packages/plugin/src/Resources/js/scripts/front-end/payments/stripe/elements.js @@ -1,4 +1,4 @@ -(()=>{var Re=Object.create;var Y=Object.defineProperty;var Me=Object.getOwnPropertyDescriptor;var Fe=Object.getOwnPropertyNames;var He=Object.getPrototypeOf,Pe=Object.prototype.hasOwnProperty;var Z=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Ce=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Fe(e))!Pe.call(t,n)&&n!==r&&Y(t,n,{get:()=>e[n],enumerable:!(o=Me(e,n))||o.enumerable});return t};var Ae=(t,e,r)=>(r=t!=null?Re(He(t)):{},Ce(e||!t||!t.__esModule?Y(r,"default",{value:t,enumerable:!0}):r,t));var ce=Z(G=>{"use strict";Object.defineProperty(G,"__esModule",{value:!0});function H(t){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?H=function(e){return typeof e}:H=function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},H(t)}var se="dahlia",qe=function(e){return e===3?"v3":e},ae="https://js.stripe.com",ke="".concat(ae,"/").concat(se,"/stripe.js"),De=/^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/,Ne=/^https:\/\/js\.stripe\.com\/(v3|[a-z]+)\/stripe\.js(\?.*)?$/,re="loadStripe.setLoadParameters was called but an existing Stripe.js script already exists in the document; existing script parameters will be used",Ue=function(e){return De.test(e)||Ne.test(e)},_e=function(){for(var e=document.querySelectorAll('script[src^="'.concat(ae,'"]')),r=0;r element.");return n.appendChild(o),o},Xe=function(e,r){!e||!e._registerWrapper||e._registerWrapper({name:"stripe-js",version:"9.1.0",startTime:r})},F=null,C=null,A=null,Ge=function(e){return function(r){e(new Error("Failed to load Stripe.js",{cause:r}))}},We=function(e,r){return function(){window.Stripe?e(window.Stripe):r(new Error("Stripe.js not available"))}},Be=function(e){return F!==null?F:(F=new Promise(function(r,o){if(typeof window>"u"||typeof document>"u"){r(null);return}if(window.Stripe&&e&&console.warn(re),window.Stripe){r(window.Stripe);return}try{var n=_e();if(n&&e)console.warn(re);else if(!n)n=ne(e);else if(n&&A!==null&&C!==null){var a;n.removeEventListener("load",A),n.removeEventListener("error",C),(a=n.parentNode)===null||a===void 0||a.removeChild(n),n=ne(e)}A=We(r,o),C=Ge(o),n.addEventListener("load",A),n.addEventListener("error",C)}catch(s){o(s);return}}),F.catch(function(r){return F=null,Promise.reject(r)}))},Je=function(e,r,o){if(e===null)return null;var n=r[0];if(typeof n!="string")throw new Error("Expected publishable key to be of type string, got type ".concat(H(n)," instead."));var a=n.match(/^pk_test/),s=qe(e.version),l=se;a&&s!==l&&console.warn("Stripe.js@".concat(s," was loaded on the page, but @stripe/stripe-js@").concat("9.1.0"," expected Stripe.js@").concat(l,". This may result in unexpected behavior. For more information, see https://docs.stripe.com/sdks/stripejs-versioning"));var i=e.apply(void 0,r);return Xe(i,o),i},oe=function(e){var r=`invalid load parameters; expected object of shape +(()=>{var Re=Object.create;var Y=Object.defineProperty;var Me=Object.getOwnPropertyDescriptor;var Fe=Object.getOwnPropertyNames;var He=Object.getPrototypeOf,Pe=Object.prototype.hasOwnProperty;var Z=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Ce=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Fe(e))!Pe.call(t,n)&&n!==r&&Y(t,n,{get:()=>e[n],enumerable:!(o=Me(e,n))||o.enumerable});return t};var Ae=(t,e,r)=>(r=t!=null?Re(He(t)):{},Ce(e||!t||!t.__esModule?Y(r,"default",{value:t,enumerable:!0}):r,t));var ce=Z(G=>{"use strict";Object.defineProperty(G,"__esModule",{value:!0});function H(t){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?H=function(e){return typeof e}:H=function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},H(t)}var se="clover",qe=function(e){return e===3?"v3":e},ae="https://js.stripe.com",ke="".concat(ae,"/").concat(se,"/stripe.js"),De=/^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/,Ne=/^https:\/\/js\.stripe\.com\/(v3|[a-z]+)\/stripe\.js(\?.*)?$/,re="loadStripe.setLoadParameters was called but an existing Stripe.js script already exists in the document; existing script parameters will be used",Ue=function(e){return De.test(e)||Ne.test(e)},_e=function(){for(var e=document.querySelectorAll('script[src^="'.concat(ae,'"]')),r=0;r element.");return n.appendChild(o),o},Xe=function(e,r){!e||!e._registerWrapper||e._registerWrapper({name:"stripe-js",version:"8.11.0",startTime:r})},F=null,C=null,A=null,Ge=function(e){return function(r){e(new Error("Failed to load Stripe.js",{cause:r}))}},We=function(e,r){return function(){window.Stripe?e(window.Stripe):r(new Error("Stripe.js not available"))}},Be=function(e){return F!==null?F:(F=new Promise(function(r,o){if(typeof window>"u"||typeof document>"u"){r(null);return}if(window.Stripe&&e&&console.warn(re),window.Stripe){r(window.Stripe);return}try{var n=_e();if(n&&e)console.warn(re);else if(!n)n=ne(e);else if(n&&A!==null&&C!==null){var a;n.removeEventListener("load",A),n.removeEventListener("error",C),(a=n.parentNode)===null||a===void 0||a.removeChild(n),n=ne(e)}A=We(r,o),C=Ge(o),n.addEventListener("load",A),n.addEventListener("error",C)}catch(s){o(s);return}}),F.catch(function(r){return F=null,Promise.reject(r)}))},Je=function(e,r,o){if(e===null)return null;var n=r[0];if(typeof n!="string")throw new Error("Expected publishable key to be of type string, got type ".concat(H(n)," instead."));var a=n.match(/^pk_test/),s=qe(e.version),l=se;a&&s!==l&&console.warn("Stripe.js@".concat(s," was loaded on the page, but @stripe/stripe-js@").concat("8.11.0"," expected Stripe.js@").concat(l,". This may result in unexpected behavior. For more information, see https://docs.stripe.com/sdks/stripejs-versioning"));var i=e.apply(void 0,r);return Xe(i,o),i},oe=function(e){var r=`invalid load parameters; expected object of shape {advancedFraudSignals: boolean} diff --git a/packages/scripts/src/components/front-end/payments/paypal/buttons.ts b/packages/scripts/src/components/front-end/payments/paypal/buttons.ts index 9391451b0..6621dd06e 100644 --- a/packages/scripts/src/components/front-end/payments/paypal/buttons.ts +++ b/packages/scripts/src/components/front-end/payments/paypal/buttons.ts @@ -24,6 +24,10 @@ type Config = { finalizeOnSubmit?: boolean; }; +type FreeformRuntimeApi = { + triggerSubmit: () => void; +}; + const SELECTORS = { root: "[data-freeform-paypal-buttons]", orderInput: "[data-freeform-paypal-order]", @@ -41,7 +45,11 @@ function logError(...args: unknown[]) { } } -function showNotice(root: Element, text: string) { +function showNotice( + root: Element, + text: string, + tone: "muted" | "success" | "error" = "success", +) { const container = root.closest('[data-field-type="paypal"]') || root.parentElement; if (!container) return; @@ -52,13 +60,20 @@ function showNotice(root: Element, text: string) { notice = document.createElement("div"); notice.setAttribute("data-ff-paypal-notice", "true"); notice.style.marginTop = "8px"; - notice.style.color = "#0a7"; notice.style.fontSize = "0.95em"; container.appendChild(notice); } + const colors = { muted: "#555", success: "#0a7", error: "#b00020" }; + notice.style.color = colors[tone]; notice.textContent = text; } +function getFreeformRuntime(form: HTMLFormElement): FreeformRuntimeApi | null { + return ( + (form as unknown as { freeform?: FreeformRuntimeApi }).freeform ?? null + ); +} + function parseConfig(el: Element): Config | null { const attr = el.getAttribute("data-config"); if (!attr) return null; @@ -259,8 +274,9 @@ async function initializePayPalButtons(root: HTMLElement) { } }, onApprove: async (data: { orderID: string }) => { - // Order approved: immediately capture, but do NOT auto-submit + // Order approved: capture, then submit the form via Freeform (no extra click). hiddenInput.value = String(data.orderID || ""); + showNotice(root, "Processing your payment…", "muted"); try { const formData = new FormData(form); const values: Values = {}; @@ -276,17 +292,36 @@ async function initializePayPalButtons(root: HTMLElement) { }, ); if (captureResult && captureResult.status === "COMPLETED") { + const runtime = getFreeformRuntime(form); + if (runtime) { + showNotice( + root, + "Payment complete. Your form is being submitted automatically.", + "success", + ); + runtime.triggerSubmit(); + } else { + showNotice( + root, + "Payment completed with PayPal. Click Submit to finish.", + "success", + ); + } + } else { + logError("Payment capture failed", captureResult); showNotice( root, - "Payment completed with PayPal. Click Submit to finish.", + "PayPal capture failed. Please try again.", + "error", ); - } else { - logError("Payment capture failed", captureResult); - showNotice(root, "PayPal capture failed. Please try again."); } } catch (error) { logError("Error during payment capture:", error); - showNotice(root, "PayPal capture error. Please try again."); + showNotice( + root, + "PayPal capture error. Please try again.", + "error", + ); } }, onError: (err: unknown) => {