Subscribe or follow on X for updates when new posts go live.
When online payment systems are built or integrated over long periods of time, a recurring tension typically appears between security, operational control, and user convenience.
Years ago, a shared payment system was required that needed to balance these constraints precisely.
The specification mandated the use of USA ePay as the payment solution provider and, critically, required that no PCI-sensitive authentication data (SAD) ever touch merchant-controlled servers or client-side applications.
The intention was not to reinvent existing payment flows. Instead, a straightforward but strict set of goals was defined:
In effect, the resulting form needed to appear native to the merchant experience while remaining fully isolated from the merchant’s infrastructure.
The approach resembles what modern platforms such as Stripe Elements or Polar Payments now abstract: embeddable components that tokenize card data before it ever enters the merchant environment.
At the time this system was created, the concept was less polished, but the objective was identical—ensure sensitive data remained outside merchant systems without undermining user experience.
This requirement aligned directly with the PCI DSS Fully Outsourced iFrame model.
Under this model, an <iframe> hosted entirely by the payment provider is embedded within the checkout page.
The iFrame displays the credit card form, collects the data, and communicates directly with the payment provider’s gateway—bypassing merchant infrastructure entirely.
To the end user, the process appears seamless.
From a compliance perspective, merchant exposure is reduced to near zero.
The following official PCI diagram illustrates the model:

Figure: Fully outsourced iFrame payment model. Source and further reading
Blue represents merchant infrastructure, green represents the payment solution provider, and grey represents the customer’s browser.
Notably, the merchant environment never interacts with raw cardholder data.
At the end of the transaction, only a tokenized result or status is returned to the merchant, not any cardholder information.
This resembles modern “payment intent” confirmation flows, though with tighter isolation boundaries and less developer flexibility.
Because the solution needed to support multiple business lines, the architecture was separated into clear bounded domains:
These domains interacted where necessary but did not share responsibilities.
In practice, the payment domain functioned as an independent microservice capable of evolving separately from the others.
The primary server-side logic was implemented in .NET Core.
The payment service exposed a small set of endpoints:
Each endpoint was structured to ensure that no sensitive card data was ever stored or processed.
Only opaque transaction identifiers returned from the provider were persisted.
This separation allowed the payment domain to operate strictly as a business-logic layer, while the infrastructure layer handled external API communication. State transitions were modeled in forms such as:
Initialized → Authorized → Captured → Refunded
MassTransit was used as the message bus to keep other domains synchronized.
Instead of using direct RPC interfaces, each payment transition emitted an event:
public record PaymentAuthorized(Guid PaymentId, string ProviderTransactionId); public record PaymentFailed(Guid PaymentId, string Reason);
The Ordering Domain subscribed to these events and updated order status accordingly—marking orders as paid, triggering invoice generation, or initiating retries after failures.
This pattern ensured the Payment Domain remained provider-agnostic. Payment providers could be replaced (e.g., migrating from USA ePay to Stripe, Authorize.net, or Polar) without requiring structural changes in other domains.
On the frontend, a consistent checkout experience was required despite the presence of an externally hosted iFrame.
React and Redux were used to manage application state and rendering, while the iFrame was embedded as a visually integrated component:
<iframe src="https://secure.paymentprovider.com/iframe/payment-form" sandbox="allow-scripts allow-same-origin" frameBorder="0" />
The customer completed the entire checkout experience—including cart, summary, confirmation, and finalization—within the main application interface, while all sensitive operations occurred inside the external domain.
CORS limitations at the time introduced significant challenges.
USA ePay did not provide granular CORS response headers, making direct cross-origin XHR impossible.
The viable workaround was JSONP (JSON-with-Padding).
JSONP injected a <script> element that referenced a provider endpoint and included a callback name:
function handlePaymentResponse(data) {
console.log('Payment result:', data);
}
const script = document.createElement('script');
script.src =
`https://secure.provider.com/payment?callback=handlePaymentResponse`;
document.body.appendChild(script);
Script tags are not restricted by CORS, enabling the provider to return a JavaScript response that executed the callback with transaction results.
While JSONP is not considered ideal today, it provided a functioning mechanism for securely passing non-sensitive data from the provider to the merchant page without introducing PCI risk.
Modern providers now rely on postMessage, tokenized SDKs, or standardized CORS configurations to support similar workflows safely.
From a PCI DSS standpoint, this architecture is highly advantageous.
Because the merchant never processes, stores, or transmits cardholder data, PCI scope is reduced to SAQ-A, the simplest compliance level.
Modern solutions such as Stripe Elements or Polar Components implement an evolved version of this approach, providing more refined APIs while maintaining similar security boundaries.
This implementation was successful due to several core architectural principles:
A system built today would likely use more modern abstractions such as Payment Intents, drop-in components, or tokenized JavaScript SDKs. These tools provide improved flexibility, better cross-origin communication, and more comprehensive developer ergonomics.
However, the foundational principles remain relevant:
The fully-outsourced iFrame pattern may appear dated compared to modern component-based payment SDKs, but it remains a reliable approach for scenarios where strict PCI isolation is required. It provides predictable behavior, minimal compliance burden, and a well-understood risk profile.
For organizations constrained by legacy providers or regulatory environments, this model continues to deliver a practical, low-risk payment integration strategy—one that offers stability even when newer tools are unavailable or unsuitable.