Documentation
Feedback
Guides
API Reference

Guides
Guides

Payment App

Learn how to build a Payment App on VTEX IO to create custom payment experiences at checkout without redirecting customers.

Watch the Payment App demo on VTEX Office Hours: English | Portuguese.

A Payment App is a special type of app built in VTEX IO that allows developers to create custom payment experiences on the checkout page without redirecting customers to an external website.

Technically, a Payment App is a TypeScript class that extends React's Component. You configure a payment method to use a specific Payment App, and the Checkout UI instantiates the app when the customer selects that payment method.

Build a Payment App instead of a standard Payments Integration to:

  • Implement a payment method using the Payment Provider Protocol without redirecting users to an external website.
  • Add a security layer to your store’s online payments using the 3D Secure 2 (3DS2) standard.
  • Measure conversions using analytics tools to customize flows.

The Payment App is displayed as a modal window when the customer clicks the Buy now button:

{"base64":"  ","img":{"width":1454,"height":1135,"type":"gif","mime":"image/gif","wUnits":"px","hUnits":"px","length":1028404,"url":"https://cdn.jsdelivr.net/gh/vtexdocs/dev-portal-content@main/images/payments-integration-payment-app-0.gif"}}
This guide covers how to develop, test, and deploy a Payment App:

IO apps do not work in headless environments. If you want to process payments in this type of scenario, you must use a checkout webview or implement a fully headless integration directly between your system and the acquirer/connector, sending the information via custom payments or notes payable.

Understanding the Payment App flow

The Payment App model supports a wide variety of payment methods through its interaction with the Checkout API. The following sequence diagram illustrates the complete flow:

{"base64":"  ","img":{"width":1082,"height":759,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":73948,"url":"https://cdn.jsdelivr.net/gh/vtexdocs/dev-portal-content@main/images/payments-integration-payment-app-1.png"}}

The steps below correspond to the diagram. Steps 1–4 are common to every payment flow. Steps 5–7 are specific to the Payment App flow.

  1. Checkout UI sends a Start Transaction request to the Checkout API.
  2. Checkout API sends a Start Transaction request to the Gateway API. The Gateway creates a new transaction, generates a unique transactionId, and returns it to the Checkout UI.
  3. Checkout UI sends a Send Payments request directly to the Gateway API. The Gateway creates a payment entity for each payment inside the transaction. A single transaction can contain multiple payments (all sent in this request). Each payment entity receives a unique paymentId. This step transmits the actual payment information (for example, credit card data).
  4. Checkout UI sends an Authorization request to the Checkout API.
  5. Checkout API sends two requests to the Gateway API:
    • Send Additional Data: Sends order information (client profile, shipping address, cart items) to the Gateway. This data is stored securely in the Gateway database and used by anti-fraud providers or some payment providers during authorization.
    • Authorization request: Triggers the Payment Authorization workflow inside the Gateway. The workflow proceeds as follows:
      1. The Gateway calls Create Payment on the connector configured in the store's payment conditions.
      2. The connector executes its payment-method logic. For example, a Pix (Brazilian payment method) connector requests the payment provider to generate a QR code.
      3. The connector responds with a paymentAppData field containing:
        • appName: The Payment App identifier in the format "{vendor}.{appName}".
        • payload: The data required to complete the transaction (for example, the Pix QR code content). The Gateway forwards this response through the Checkout API back to the Checkout UI.
  6. The Checkout UI instantiates the Payment App identified by paymentAppData.appName and injects paymentAppData.payload into the app's appPayload property. The app then executes its logic, for example, rendering a 3DS2 authentication challenge, displaying a Pix QR code, or simulating a redirect within the Payment App frame. At this point, the transaction enters the Authorizing state. Learn more in Transaction flow in Payments.
  7. When the user completes the interaction, the Payment App closes and triggers transaction validation. The Checkout UI queries the transaction status from the Gateway API through the Checkout API:
    • Authorized or Undefined: The Checkout UI displays the Order Placed screen.
    • Any other status: The Checkout UI displays a warning and returns the user to the payment method selection.

Implementing a Payment App

This section covers setting up the development environment, cloning the boilerplate, testing, and deploying. All steps use the VTEX IO development platform.

If you're implementing a new payment method in your Payment App, contact the VTEX Support Team to add it to the VTEX backend.

Step 1: Setting up the development environment

  1. Install the VTEX IO CLI in your terminal.
  2. Create a development workspace.

Step 2: Developing your Payment App

Make the following changes to the boilerplate to create your own app. You also need to add the business logic of your payment system.

Before you start editing the Payment App, you must have Git installed on your computer.

  1. Using your terminal, clone the repository that contains a Payment App model to your local files by running:

_10
git clone https://github.com/vtex-apps/example-payment-authorization-app.git

  1. Open the cloned project in a code editor.

  2. Open manifest.json and update the fields for your scenario. Key requirements:

    • name: Must match the appName property of paymentAppData in the connector's Create Payment response body.
    • billingOptions: Set type to free. See Billing Options.

    For details on all required fields, see the Manifest documentation.

  3. Open pages/pages.json and replace example-payment-auth-app with the name value from manifest.json. This file creates the routes that allow VTEX to find and display the app at checkout. The "component": "index" entry indicates that react/index.js contains the component instantiated at checkout.

Replace only the last part of the path in pages.json with your app name (the example-payment-auth-app part). Don't change the checkout/transactions/ prefix, which the checkout uses to identify payment applications.

If you're using the Test Connector for internal tests, keep the original app name from the repository in pages.json. The Test Connector uses this name to open the Payment App in the checkout.

  1. Using your terminal, go to the app directory and run the following command:

_10
vtex link

Once you link the app, local file changes are automatically synced to the VTEX cloud development environment and reflected in your workspace.

Step 3: Testing a Payment App flow

Follow the steps below to display the Payment App on the checkout screen and test the general flow.

Proceed with the following steps only when you want to test a Payment App flow; otherwise, skip to Step 4: Deploying the Payment App.

  1. Make sure your connector is installed and configured with the Payment App and payment method you want to test. The connector must return the paymentAppData field in the Create Payment response body with the following properties:
  2. Select the payment condition associated with your connector in the checkout and complete the purchase to test the flow.

Step 4: Deploying your Payment App

Once you've made the changes, see Making your new app version publicly available for instructions on running your app on the Master workspace.

Operational mode

After deploying the Payment App, use the following features to customize its behavior:

Handling the order payload

The Payment App receives appPayload as a prop, a serialized JSON string containing the order payload. This prop isn't declared in the Payment App class; the Checkout UI injects it after instantiation. Access it with the following code:


_10
const { appPayload } = this.props // This appPayload is a serialized JSON (as string).

The JSON object contains the data the Payment App needs to approve or deny the transaction. The connector developer defines which fields are displayed in appPayload.

Example payload:


_10
{
_10
"timeToCancel": 300,
_10
"transactionApproveLink": "https://..."
_10
}

In this example:

  • "timeToCancel": Time in seconds before the Payment App automatically cancels the transaction.
  • "transactionApproveLink": URL to request transaction approval when the Payment App conditions are met.

Understanding the response to the Checkout UI

When the Payment App finishes its operation, it must notify the Checkout UI so the UI can close the app and verify the transaction status. If the status is approved or undefined, the Checkout UI redirects the customer to the Order Placed page. Otherwise, it displays a warning and returns the customer to the payment method selection.

The Payment App notifies the Checkout UI by dispatching the transactionValidation.vtex event using the browser’s native event handling engine. The Checkout UI listens for this event, closes the Payment App, and queries the transaction status from the Gateway. Use the following method to trigger the event:


_10
respondTransaction = () => {
_10
$(window).trigger('transactionValidation.vtex')
_10
}

The $(window).trigger() syntax above uses jQuery, which is available in the VTEX Checkout environment. Alternatively, you can use the native browser API: window.dispatchEvent(new Event('transactionValidation.vtex')).

If the transactionValidation.vtex event isn't triggered, the order confirmation email won't be sent to the user. Create a retry flow to handle cases where this event isn't triggered or the order confirmation email isn't sent.

Injecting external scripts

External script injection adds custom content and behavior to your Payment App. Inject the script into the checkout <head> element using DOM manipulation:


_10
const head = document.getElementsByTagName('head')[0]
_10
_10
const js = document.createElement('script')
_10
js.id = '{{script-id}}' // Replace with your script's unique identifier
_10
js.src = '{{script-src}}' // Replace with the external script URL
_10
js.async = true
_10
js.defer = true
_10
js.onload = {{callback-onload}} // Replace with a callback function to run after the script loads
_10
_10
head.appendChild(js)

See the script injection example that adds Google reCAPTCHA to a Payment App.

If the external script handles DOM manipulation, use React's Ref to create a div container and pass it to the library. See the example.

Payment App scenarios

The following scenarios illustrate common Payment App use cases.

Scenario 1: An integration via Payment Provider Protocol that requires a new payment method

A Payment App is the recommended approach for new payment methods because customers complete the transaction without leaving the checkout page. It also enables adding a security layer to online payments using the 3D Secure 2 standard.

Scenario 2: Payment App and 3D Secure 2

3D Secure 2 (3DS2) is a protocol that enables checkout processes to comply with Strong Customer Authentication (SCA) requirements through risk-based authentication for online card transactions. It was created to comply with European payment regulations under the Revised Payment Services Directive 2 (PSD2).

3DS2 is an evolution of 3D Secure 1 (3DS1), introducing the frictionless authentication flow and improving the purchase experience. See the 3D Secure 2 FAQ for a comparison.

On VTEX, 3D Secure 2 can only be implemented via the Payment App, as the platform doesn't support alternative methods, such as redirect URLs, for this protocol.

3DS2 flow

To apply 3DS2 at checkout, the acquirer must create a Payment App that handles authentication challenges for each issuing bank that requires Strong Authentication.

When the customer enters card information and clicks the Checkout button, the acquirer calls the issuing bank through the 3DS2 flow.

The card issuer performs Risk-based Authentication, analyzing the fraud risk of the purchase. The risk score is based on elements such as transaction value, purchase history, and device information.

  • High fraud risk: The issuer triggers the 3DS2 challenge, requiring the customer to complete Strong Authentication (for example, approving the payment in the bank app).
  • Low fraud risk: The issuer doesn't trigger the challenge (frictionless flow).

If the acquirer requires Strong Authentication for a payment, it must return status as undefined and include the Payment App information in the paymentAppData field of the Create Payment response body.

A status of undefined indicates to the VTEX Gateway that the payment isn't yet authorized or denied. Strong Authentication is one possible reason for this. The paymentAppData field in the response determines which Payment App the Checkout UI instantiates.

The Gateway stores the app data on the payment and notifies the Checkout that the payment isn't yet authorized or denied. The Checkout UI then instantiates the Payment App specified in the paymentAppData field.

The Payment App displays the Strong Authentication challenge on the checkout screen, requiring the customer to approve the transaction through the issuing bank (for example, via the bank app).

After the customer completes the challenge, the Payment App closes and the Checkout UI checks the transaction status:

  • approved or undefined — The Checkout UI displays the Order Placed screen.
  • denied or canceled — The Checkout UI displays a warning and returns the customer to payment method selection.

The following image shows how the 3DS2 is displayed to customers:

{"base64":"  ","img":{"width":1999,"height":1245,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":288965,"url":"https://cdn.jsdelivr.net/gh/vtexdocs/dev-portal-content@main/images/payments-integration-payment-app-2.png"}}

Contributors
4
Photo of the contributor
Photo of the contributor
Photo of the contributor
Photo of the contributor
Was this helpful?
Yes
No
Suggest Edits (GitHub)
Contributors
4
Photo of the contributor
Photo of the contributor
Photo of the contributor
Photo of the contributor
Was this helpful?
Suggest edits (GitHub)
On this page