PayFields: Integrate with Single-Page Applications
Reactivity powers modern single-page applications (SPAs) built on frameworks like React, Vue, and Angular. Reactivity enables real-time user interface (UI) updates in response to user actions and data changes. Integrate PayFields to bring the same seamless experience into your customer payment flow, ensuring a frictionless and responsive experience within your SPA.
SPA Reactivity
SPA reactivity automatically updates the UI when data changes occur. SPAs load a single HTML page and use JavaScript frameworks like React, Vue, or Angular to dynamically modify and update the Document Object Model (DOM).
Key concepts of SPA reactivity include:
Virtual DOM: For example, React uses a virtual DOM to represent the UI. When data changes, React compares the new and previous Virtual DOMs to calculate the smallest set of updates for the actual DOM.
Components: In React and Vue, components structure the UI. Each component manages its own state and props (properties) and re-renders the UI when either changes.
Data Binding: Angular uses two-way data binding, automatically updating the view when the model changes and the model when the view changes.
Asynchronous Operations: SPAs also rely on asynchronous operations like API requests, which trigger updates in the UI when responses are received.
Integrate PayFields Into Your SPA
PayFields is an embedded payments solution that securely handles sensitive payment information like credit card numbers within your application. PayFields operates asynchronously, which might create reactivity challenges. Integrate PayFields into an SPA to ensure a smooth and secure user experience.
Here are some key considerations for integrating PayFields into your single-page application (SPA):
Dynamic Script Loading: PayFields dynamic script and resource loading might delay access to the PayFields library in your SPA. The query parameter
?spa=1
identifies your SPA integration to prevent immediate loading and authentication validation failures in the console log:HTML<script src="<https://test-api.payrix.com/payFieldsScript?spa=1>"></script>
Iframe Integration: PayFields uses iframes to isolate and secure payment-related data. Your SPA must add these iframes to the DOM after the PayFields library loads.
Important!
Do not use HTML <input>
elements for sensitive, personally identifiable information (PII), like card numbers or emails, in your SPA. Capture all sensitive data using PCI-compliant <div>
elements from the PayFields Developer Guide. These <div>
elements are replaced by secure, iframed input fields hosted by Payrix Pro for PCI compliance and data isolation.
PayFields Reactive Integration Example
The following example shows a modern, Promise-based approach to integrating PayFields into a React-based SPA. The PayFields script loads asynchronously, initializes the PayFields configuration, and mounts the payment input fields into designated DOM elements.
In this React component example:
useLayoutEffect
ensures that PayFields is initialized and ready before any user interaction occurs.Promise
ensures new fields are mounted only after the PayFields library is ready to avoid timing issues, acting as a placeholder.PayFields.unmountAll()
is called to remove existing iframes before reinitialization.PayFields.ready()
is called to re-mount the new payment fields.
import { useLayoutEffect, useRef } from "react"
export default function Example() {
const payFields = useRef(null)
async function loadScripts() {
const scripts = [
{
id: "payrixScript",
src: "https://test-api.payrix.com/payFieldsScript?spa=1",
onLoad: () => {
payFields.current = window.PayFields
console.log("PayFields script loaded")
},
},
{
id: "payFieldGooglePayScript",
src: "https://pay.google.com/gp/p/js/pay.js",
onLoad: () => {
console.log("Google Pay script loaded")
},
},
]
const load = ({ id, src, onLoad }) =>
new Promise((resolve, reject) => {
if (document.getElementById(id)) return resolve()
const script = document.createElement("script")
script.src = src
script.id = id
script.async = true
script.onload = () => {
onLoad?.()
resolve()
}
script.onerror = () => reject(new Error(`Failed to load script: ${id}`))
document.body.appendChild(script)
})
for (const script of scripts) {
await load(script)
}
}
function removePayFields() {
const script = document.getElementById("payrixScript")
if (script) {
if (payFields.current && typeof payFields.current.unmountAll === "function") {
payFields.current.unmountAll()
}
script.remove()
}
}
async function handleClick() {
payFields.current.submit()
}
useLayoutEffect(() => {
const initScript = async () => {
await loadScripts()
if (!payFields) {
console.log("Error Loading PayFields")
return
}
const merchant = ""
// From Secure Storage
const apiKey = ""
payFields.current.config.merchant = merchant
payFields.current.config.apiKey = apiKey
payFields.current.config.googlePay.environment = "TEST"
payFields.current.config.googlePay.enabled = true
payFields.current.config.mode = "txn"
payFields.current.config.txnType = "sale"
// payFields.current.config.customer = customer_id
// payFields.current.config.token = token;
payFields.current.config.amount = "100"
payFields.current.config.billingAddress = {}
payFields.current.fields = [
{ type: "number", element: "#number" },
{ type: "cvv", element: "#cvv" },
{ type: "name", element: "#name" },
{ type: "expiration", element: "#expiration" },
{ type: "address", element: "#address" },
// { type: "customer_id", element: "#customer_id"}
]
payFields.current.onSuccess = (response) => {
console.log(response)
}
payFields.current.onFailure = (response) => {
console.log("onFailure", response)
}
payFields.current.onValidationFailure = () => {
alert("something went wrong with input fields")
console.log("onvalidationFailure")
}
console.log({ payfields: payFields.current })
payFields.current.ready()
}
initScript()
return () => {
removePayFields()
}
}, [])
return (
<>
<div className="main">
<div className="payfields">
<h1>PayFields Example</h1>
<form>
<div id="form-container">
<label htmlFor="number">Number:</label>
<div id="number" className="form-row"></div>
<label htmlFor="expiration">Expiration:</label>
<div id="expiration" className="form-row"></div>
<label htmlFor="cvv">CVV:</label>
<div id="cvv" className="form-row"></div>
<label htmlFor="name">Customer Name:</label>
<div id="name" className="form-row"></div>
<div id="customer_id" className="customer-row"></div>
</div>
<div id="add">
<div id="address" className="address-row"></div>
</div>
</form>
<div className="button-container">
<button type="button" id="button" name="Submit" onClick={handleClick}>
Submit
</button>
</div>
<div id="googlePayButton"></div>
</div>
</div>
</>
)
}