payment-provider-framework
Apply when designing or implementing a Payment Connector in VTEX IO. Covers PPF implementation, TypeScript 3.9.7 builder-hub constraints and safe dependency resolutions, configuration.json schema validation, PaymentProviderService clients wiring, Secure Proxy scope (authorize-only), ExternalClient v
By vtex · 742 installs
npx skills add vtex/skills --skill payment-provider-framework
Source repository · Upstream listing
Payment Provider Framework (VTEX IO)
When this skill applies
Use this skill when:
Creating or maintaining a payment connector implemented as a VTEX IO app (not a standalone HTTP service you host yourself)
Wiring @vtex/payment provider , PaymentProvider , and PaymentProviderService in node/index.ts
Configuring the paymentProvider builder, configuration.json (payment methods, customFields , feature flags)
Implementing this.retry(request) for Gateway retry semantics on IO
Extending SecureExternalClient and passing secureProxy on requests for card flows on IO
Testing via payment affiliation, workspaces, beta/stable releases, the VTEX App Store, and VTEX homologation
Do not use this skill for:
PPP HTTP contracts, response field by field requirements, and the nine endpoints in the abstract — use [ payment provider protocol ](../payment provider protocol/SKILL.md)
Idempotency and duplicate paymentId handling — use [ payment idempotency ](../payment idempotency/SKILL.md)
Async undefined status, callbackUrl notification vs retry (IO vs non IO) — use [ payment async flow ](../payment async flow/SKILL.md)
PCI rules, logging, and token semantics beyond IO wiring — use [ payment pci security ](../payment pci security/SKILL.md)
Decision rules
PPF on IO : "Payment Provider Framework is the VTEX IO–based way to build payment connectors." The app uses IO infrastructure; API routes, request/response types, and Secure Proxy are integrated per VTEX guides. Start from the example app described in the official Payment Provider Framework documentation.
Prerequisites : Follow implementation prerequisites in the Payment Provider Protocol article and the guide on integrating a new payment provider on VTEX.
Dependencies : In the app node folder, add @vtex/payment provider (for example 1.x in package.json ). Keep @vtex/api in devDependencies (for example 6.x ); linking may bump it beyond 6.x , which is acceptable. If types break, delete node modules and yarn.lock in the project root and in node , then run yarn install f in both.
paymentProvider builder : In manifest.json , include "paymentProvider": "1.x" next to node so policies for Payment Gateway callbacks and PPP routes apply.
configuration.json : Declare paymentMethods so the builder can implement them without re declaring everything on /manifest . Use names matching the List Payment Provider Manifest API reference; only invent a new name when the method is genuinely new. New methods in Admin may require a support ticket.
PaymentProvider : One class method per PPP route; TypeScript enforces shapes — see Payment Flow endpoints in the API reference.
PaymentProviderService : Registers default routes /manifest , /payments , /settlements , /refunds , /cancellations , /inbound ; pass extra routes / clients when needed.
Overriding /manifest : Only with an approved use case — open a ticket. See the Preferred pattern section for an example route override shape.
Configurable options : Use configuration.json / builder options for flags such as implementsOAuth , implementsSplit , usesProviderHeadersName , usesBankInvoiceEnglishName , usesSecureProxy , requiresDocument , acceptSplitPartialRefund , usesAutoSettleOptions . Set name and rely on auto generated serviceUrl on IO unless documented otherwise. Do not invent fields — unknown keys (such as usesTestSuite ) cause builder validation errors. See the "configuration.json schema" constraint below for the canonical list and customFields format.
Gateway retry : In PPF, call this.retry(request) where the protocol requires retry — see the Payment authorization section in the PPP article.
Card data on IO : "Prefer SecureExternalClient with secureProxy: secureProxyUrl from Create Payment; destination must be allowlisted." Supported Content Type values for Secure Proxy: application/json and application/x www form urlencoded only. Important: only the Create Payment (authorize) request carries secureProxyUrl . Post authorization operations (cancel, capture, refund) do not transport card data and must call the PSP API directly via ExternalClient with credentials and outbound access policies.
Checkout testing : Account must be allowed for IO connectors (ticket with app name and account). Publish beta, install on master , wait ~1 hour, open affiliation URL, enable test mode and workspace, configure payment condition (~10 minutes), place test order; then stable + homologation.
Publication : Configure billingOptions per the Billing Options guide; submit via Submitting your app. Prepare homologation artifacts (connector app name, partner contact, production endpoint, allowed accounts, new methods/flows) per the Integrating a new payment provider on VTEX guide (SLA often ~30 days).
Updates : Ship changes in a new beta, re test affiliations, then stable; re homologate if required.
Hard constraints
Constraint: Builder Hub uses TypeScript 3.9.7 — code and dependencies MUST be compatible
The vtex.builder hub compiles IO apps with TypeScript 3.9.7 . It also ignores skipLibCheck: true in tsconfig.json — every .d.ts file in node modules is type checked. This means that even if your own code is valid, a transitive dependency shipping modern .d.ts syntax will break the build with hundreds of errors unrelated to your code.
Why this matters
Agents and developers regularly produce code with TS 4.x+ syntax or install the latest @types/ packages. The build fails with cryptic errors in files the developer never touched, causing many wasted iterations.
Prohibited syntax (incompatible with TS 3.9.7)
Syntax Minimum TS version Example
Template literal types 4.1 type X = ${string}/${string}
Typed catch clause 4.0 catch (error: any)
override keyword 4.3 override method() in classes
import type ... = require() 4.5 import type X = require("pkg")
satisfies operator 4.9 obj satisfies Type
Correct catch block pattern
Unused variables are errors, not warnings. The builder hub treats declared but unused variables as compilation errors. Avoid destructuring fields you do not use:
Safe dependency versions (compatible with TS 3.9.7)
Use resolutions in node/package.json to pin transitive dependencies to versions that do not ship modern .d.ts syntax. The /<package pattern pins nested copies too.
Package Safe version First broken version Reason
@types/node 12.20.55 13.x+ (some APIs) Modern syntax in .d.ts
@types/express serve static core 4.17.2 4.17.13+ Template literal types
@types/express 4.17.10 4.17.11+ Depends on @types/express serve static core@^4.17.18
@types/koa 2.15.0 3.x import type ... = require()
@opentelemetry/api 1.0.4 Newer versions TS 4.x syntax
@types/serve static 1.15.0 Newer versions Transitive dependency issues
Diagnosing new broken packages: if the build fails with errors in .d.ts files from node modules , identify the package from the error path, test older versions until you find one without modern syntax, and add it to both devDependencies and resolutions (with /<package pattern).
Detection
If the generated code uses any syntax from the table above, or if package.json lacks resolutions for type packages, STOP and fix before attempting vtex link .
Constraint: configuration.json must use only valid schema fields and correct customFields format
The paymentProvider builder validates configuration.json against a strict schema. Unknown fields cause build errors. The customFields[].options array for select type fields must use text and value keys — never label .
Why this matters
Invalid fields like usesTestSuite or useAntifraud (if not in the current schema) cause immediate vtex link failure. Using label instead of text in select options silently breaks the Admin UI or fails validation.
Canonical fields (verify against current VTEX documentation):
name (required), serviceUrl (auto on IO), implementsOAuth , implementsSplit , usesProviderHeadersName , usesBankInvoiceEnglishName , usesSecureProxy , requiresDocument , acceptSplitPartialRefund , usesAutoSettleOptions , paymentMethods , customFields .
Fields known to break the build: usesTestSuite (does not exist in the schema).
Correct customFields with select
Wrong customFields
Detection
If configuration.json contains keys not in the canonical list, or uses label instead of text in select options, STOP and fix before build.
Constraint: Declare the paymentProvider builder and a real connector identity in configuration.json
IO connectors MUST include the paymentProvider builder in manifest.json and a paymentProvider/configuration.json with a non placeholder name and accurate paymentMethods . Do not ship the literal placeholder "MyConnector" (or equivalent) as production configuration.
Why this matters
Without the builder, PPP routes and Gateway policies are not wired. A placeholder name breaks Admin, affiliations, and homologation.
Detection
If manifest.json lacks paymentProvider , or configuration.json still uses example placeholder names, stop and fix before publishing.
Correct
Wrong
Constraint: Register PPP routes only through PaymentProviderService with a PaymentProvider implementation
The service MUST wrap a class extending PaymentProvider from @vtex/payment provider so standard PPP paths are registered. Do not hand roll the same route surface without the package unless VTEX explicitly prescribes an alternative.
Why this matters
Missed or mismatched routes break Gateway calls and homologation; the package keeps handlers aligned with the protocol.
Detection
If node/index.ts exposes PPP paths manually and does not instantiate PaymentProviderService with the connector class, reconcile with the documented pattern.
Correct
Wrong
Constraint: PaymentProviderService clients field requires { implementation, options } — not the class directly
When passing custom IOClients to PaymentProviderService , the clients field expects an object with implementation (the class) and options (retry/timeout config), following the ServiceConfig interface from @vtex/api . Passing the class directly causes a runtime error.
Why this matters
This is a common mistake that produces a confusing runtime error instead of a clear type error, since the PPF types may not enforce this strictly.
Correct
Wrong
Constraint: Use this.retry(request) for Gateway retry on IO
Where the PPP flow requires retry semantics on IO, handlers MUST invoke this.retry(request) as specified in the protocol — not a custom retry helper that bypasses the framework.
Why this matters
"The Gateway expects framework driven retry behavior; omitting it causes inconsistent authorization and settlement behavior."
Detection
Search payment handlers for protocol retry cases; if retries are implemented without this.retry , fix before release.
Correct
Wrong
Constraint: Forward card authorization calls through Secure Proxy on IO with allowlisted destinations
For card flows on IO with usesSecureProxy behavior, proxied HTTP calls MUST go through SecureExternalClient (or equivalent VTEX pattern), MUST pass secureProxy set to the secureProxyUrl from the payment request, and MUST target a VTEX allowlisted PCI endpoint. Only application/json or application/x www form urlencoded bodies are supported. If usesSecureProxy is false, the provider must be PCI certified and supply AOC for serviceUrl per VTEX.
Why this matters
"Skipping Secure Proxy or wrong content types breaks PCI scope, proxy validati