Loading...
Loading...
Apply when implementing a VTEX Payment Provider Protocol (PPP) connector or working with payment/connector endpoint files. Covers all nine required endpoints: Manifest, Create Payment, Cancel, Capture/Settle, Refund, Inbound Request, Create Auth Token, Provider Auth Redirect, and Get Credentials. Use for building or debugging any payment connector that integrates with the VTEX Payment Gateway.
npx skill4agent add vtexdocs/ai-skills payment-provider-protocolpayment-idempotencypayment-async-flowpayment-pci-securitycallbackUrl/manifest/payments/payments/{paymentId}/cancellations/payments/{paymentId}/settlements/payments/{paymentId}/refunds/payments/{paymentId}/inbound-request/{action}import { Router } from "express";
const router = Router();
// All 6 payment-flow endpoints implemented
router.get("/manifest", manifestHandler);
router.post("/payments", createPaymentHandler);
router.post("/payments/:paymentId/cancellations", cancelPaymentHandler);
router.post("/payments/:paymentId/settlements", capturePaymentHandler);
router.post("/payments/:paymentId/refunds", refundPaymentHandler);
router.post("/payments/:paymentId/inbound-request/:action", inboundRequestHandler);
export default router;import { Router } from "express";
const router = Router();
// Missing manifest, inbound-request, and refund endpoints
// This will fail homologation and break runtime operations
router.post("/payments", createPaymentHandler);
router.post("/payments/:paymentId/cancellations", cancelPaymentHandler);
router.post("/payments/:paymentId/settlements", capturePaymentHandler);
export default router;paymentIdstatusauthorizationIdtidnsuacquirercodemessagedelayToAutoSettledelayToAutoSettleAfterAntifrauddelayToCancelpaymentIdcancellationIdcodemessagerequestIdpaymentIdsettleIdvaluecodemessagerequestIdpaymentIdrefundIdvaluecodemessagerequestIddelayToAutoSettleinterface CreatePaymentResponse {
paymentId: string;
status: "approved" | "denied" | "undefined";
authorizationId: string | null;
nsu: string | null;
tid: string | null;
acquirer: string | null;
code: string | null;
message: string | null;
delayToAutoSettle: number;
delayToAutoSettleAfterAntifraud: number;
delayToCancel: number;
paymentUrl?: string;
}
async function createPaymentHandler(req: Request, res: Response): Promise<void> {
const { paymentId, value, currency, paymentMethod, card, callbackUrl } = req.body;
const result = await processPaymentWithAcquirer(req.body);
const response: CreatePaymentResponse = {
paymentId,
status: result.status,
authorizationId: result.authorizationId ?? null,
nsu: result.nsu ?? null,
tid: result.tid ?? null,
acquirer: "MyAcquirer",
code: result.code ?? null,
message: result.message ?? null,
delayToAutoSettle: 21600, // 6 hours in seconds
delayToAutoSettleAfterAntifraud: 1800, // 30 minutes in seconds
delayToCancel: 21600, // 6 hours in seconds
};
res.status(200).json(response);
}// Missing required fields — Gateway will reject this response
async function createPaymentHandler(req: Request, res: Response): Promise<void> {
const result = await processPaymentWithAcquirer(req.body);
// Missing: authorizationId, nsu, tid, acquirer, code, message,
// delayToAutoSettle, delayToAutoSettleAfterAntifraud, delayToCancel
res.status(200).json({
paymentId: req.body.paymentId,
status: result.status,
});
}/manifestpaymentMethodsnameallowsSplitallowsSplitpaymentMethodsasync function manifestHandler(_req: Request, res: Response): Promise<void> {
const manifest = {
paymentMethods: [
{ name: "Visa", allowsSplit: "onCapture" },
{ name: "Mastercard", allowsSplit: "onCapture" },
{ name: "American Express", allowsSplit: "onCapture" },
{ name: "BankInvoice", allowsSplit: "onAuthorize" },
{ name: "Pix", allowsSplit: "disabled" },
],
};
res.status(200).json(manifest);
}// Empty manifest — no payment methods will appear in the Admin
async function manifestHandler(_req: Request, res: Response): Promise<void> {
res.status(200).json({ paymentMethods: [] });
}Shopper → VTEX Checkout → VTEX Payment Gateway → [Your Connector Middleware] → Acquirer/PSP
↕
Configuration Flow (Admin)// --- Manifest ---
interface ManifestResponse {
paymentMethods: Array<{
name: string;
allowsSplit: "onCapture" | "onAuthorize" | "disabled";
}>;
}
// --- Create Payment ---
interface CreatePaymentRequest {
reference: string;
orderId: string;
transactionId: string;
paymentId: string;
paymentMethod: string;
value: number;
currency: string;
installments: number;
card?: {
holder: string;
number: string;
csc: string;
expiration: { month: string; year: string };
};
miniCart: Record<string, unknown>;
callbackUrl: string;
returnUrl?: string;
}
interface CreatePaymentResponse {
paymentId: string;
status: "approved" | "denied" | "undefined";
authorizationId: string | null;
nsu: string | null;
tid: string | null;
acquirer: string | null;
code: string | null;
message: string | null;
delayToAutoSettle: number;
delayToAutoSettleAfterAntifraud: number;
delayToCancel: number;
paymentUrl?: string;
}
// --- Cancel Payment ---
interface CancelPaymentResponse {
paymentId: string;
cancellationId: string | null;
code: string | null;
message: string | null;
requestId: string;
}
// --- Capture/Settle Payment ---
interface CapturePaymentResponse {
paymentId: string;
settleId: string | null;
value: number;
code: string | null;
message: string | null;
requestId: string;
}
// --- Refund Payment ---
interface RefundPaymentResponse {
paymentId: string;
refundId: string | null;
value: number;
code: string | null;
message: string | null;
requestId: string;
}
// --- Inbound Request ---
interface InboundResponse {
requestId: string;
paymentId: string;
responseData: {
statusCode: number;
contentType: string;
content: string;
};
}import { Router, Request, Response } from "express";
const router = Router();
router.get("/manifest", async (_req: Request, res: Response) => {
res.status(200).json({
paymentMethods: [
{ name: "Visa", allowsSplit: "onCapture" },
{ name: "Mastercard", allowsSplit: "onCapture" },
{ name: "Pix", allowsSplit: "disabled" },
],
});
});
router.post("/payments", async (req: Request, res: Response) => {
const body: CreatePaymentRequest = req.body;
const result = await processWithAcquirer(body);
const response: CreatePaymentResponse = {
paymentId: body.paymentId,
status: result.status,
authorizationId: result.authorizationId ?? null,
nsu: result.nsu ?? null,
tid: result.tid ?? null,
acquirer: "MyProvider",
code: result.code ?? null,
message: result.message ?? null,
delayToAutoSettle: 21600,
delayToAutoSettleAfterAntifraud: 1800,
delayToCancel: 21600,
};
res.status(200).json(response);
});
router.post("/payments/:paymentId/cancellations", async (req: Request, res: Response) => {
const { paymentId } = req.params;
const { requestId } = req.body;
const result = await cancelWithAcquirer(paymentId);
res.status(200).json({
paymentId,
cancellationId: result.cancellationId ?? null,
code: result.code ?? null,
message: result.message ?? "Successfully cancelled",
requestId,
});
});
router.post("/payments/:paymentId/settlements", async (req: Request, res: Response) => {
const body = req.body;
const result = await captureWithAcquirer(body.paymentId, body.value);
res.status(200).json({
paymentId: body.paymentId,
settleId: result.settleId ?? null,
value: result.capturedValue ?? body.value,
code: result.code ?? null,
message: result.message ?? null,
requestId: body.requestId,
});
});
router.post("/payments/:paymentId/refunds", async (req: Request, res: Response) => {
const body = req.body;
const result = await refundWithAcquirer(body.paymentId, body.value);
res.status(200).json({
paymentId: body.paymentId,
refundId: result.refundId ?? null,
value: result.refundedValue ?? body.value,
code: result.code ?? null,
message: result.message ?? null,
requestId: body.requestId,
});
});
router.post("/payments/:paymentId/inbound-request/:action", async (req: Request, res: Response) => {
const body = req.body;
const result = await handleInbound(body);
res.status(200).json({
requestId: body.requestId,
paymentId: body.paymentId,
responseData: {
statusCode: 200,
contentType: "application/json",
content: JSON.stringify(result),
},
});
});
export default router;import { Router, Request, Response } from "express";
const configRouter = Router();
// 1. POST /authorization/token
configRouter.post("/authorization/token", async (req: Request, res: Response) => {
const { applicationId, returnUrl } = req.body;
const token = await generateAuthorizationToken(applicationId, returnUrl);
res.status(200).json({ applicationId, token });
});
// 2. GET /authorization/redirect
configRouter.get("/authorization/redirect", async (req: Request, res: Response) => {
const { token } = req.query;
const providerLoginUrl = buildProviderLoginUrl(token as string);
res.redirect(302, providerLoginUrl);
});
// 3. GET /authorization/credentials
configRouter.get("/authorization/credentials", async (req: Request, res: Response) => {
const { authorizationCode } = req.query;
const credentials = await exchangeCodeForCredentials(authorizationCode as string);
res.status(200).json({
applicationId: "vtex",
appKey: credentials.appKey,
appToken: credentials.appToken,
});
});
export default configRouter;delayToAutoSettledelayToAutoSettleAfterAntifrauddelayToCancelpaymentIdstatusauthorizationIdtidnsuacquirerdelayToAutoSettledelayToAutoSettleAfterAntifrauddelayToCancelpayment-idempotencypaymentIdrequestIdpayment-async-flowcallbackUrlpayment-pci-security