payment-provider-protocol

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

PPP Endpoint Implementation

PPP端点实现

When this skill applies

适用场景

Use this skill when:
  • Building a new payment connector middleware that integrates a PSP with the VTEX Payment Gateway
  • Implementing, debugging, or extending any of the 9 PPP endpoints
  • Preparing a connector for VTEX Payment Provider Test Suite homologation
Do not use this skill for:
  • Idempotency and duplicate prevention logic — use
    payment-idempotency
  • Async payment flows and callback URLs — use
    payment-async-flow
  • PCI compliance and Secure Proxy card handling — use
    payment-pci-security
本内容适用于以下场景:
  • 构建将支付服务提供商(PSP)与VTEX支付网关集成的全新支付连接器中间件
  • 实现、调试或扩展9个PPP端点中的任意一个
  • 为VTEX支付提供商测试套件认证准备连接器
以下场景请勿使用本内容:
  • 幂等性与重复请求预防逻辑 — 请使用
    payment-idempotency
  • 异步支付流程与回调URL — 请使用
    payment-async-flow
  • PCI合规与安全代理卡片处理 — 请使用
    payment-pci-security

Decision rules

决策规则

  • The connector MUST implement all 6 payment-flow endpoints: Manifest, Create Payment, Cancel, Capture/Settle, Refund, Inbound Request.
  • The configuration flow (3 endpoints: Create Auth Token, Provider Auth Redirect, Get Credentials) is optional but recommended for merchant onboarding.
  • All endpoints must be served over HTTPS on port 443 with TLS 1.2.
  • The connector must respond in under 5 seconds during homologation tests and under 20 seconds in production.
  • The provider must be PCI-DSS certified or use Secure Proxy for card payments.
  • The Gateway initiates all calls. The middleware never calls the Gateway except via
    callbackUrl
    (async notifications) and Secure Proxy (card data forwarding).
  • 连接器必须实现全部6个支付流程端点:Manifest、创建支付、取消支付、捕获/结算、退款、入站请求。
  • 配置流程的3个端点(创建授权令牌、提供商授权重定向、获取凭证)为可选,但建议用于商家入驻流程。
  • 所有端点必须通过HTTPS在443端口提供服务,且使用TLS 1.2协议。
  • 在认证测试期间,连接器的响应时间必须在5秒以内;生产环境下响应时间需在20秒以内。
  • 支付提供商必须获得PCI-DSS认证,或使用安全代理处理卡片支付。
  • 所有调用均由网关发起。中间件仅可通过
    callbackUrl
    (异步通知)和安全代理(卡片数据转发)调用网关。

Hard constraints

硬性约束

Constraint: Implement all required payment flow endpoints

约束:实现所有必填支付流程端点

The connector MUST implement all six payment-flow endpoints: GET
/manifest
, POST
/payments
, POST
/payments/{paymentId}/cancellations
, POST
/payments/{paymentId}/settlements
, POST
/payments/{paymentId}/refunds
, and POST
/payments/{paymentId}/inbound-request/{action}
.
Why this matters The VTEX Payment Provider Test Suite validates every endpoint during homologation. Missing endpoints cause test failures and the connector will not be approved. At runtime, the Gateway expects all endpoints — a missing cancel endpoint means payments cannot be voided.
Detection If the connector router/handler file does not define handlers for all 6 payment-flow paths, STOP and add the missing endpoints before proceeding.
Correct
typescript
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;
Wrong
typescript
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;
连接器必须实现全部6个支付流程端点:GET
/manifest
、POST
/payments
、POST
/payments/{paymentId}/cancellations
、POST
/payments/{paymentId}/settlements
、POST
/payments/{paymentId}/refunds
以及 POST
/payments/{paymentId}/inbound-request/{action}
重要性 VTEX支付提供商测试套件在认证过程中会验证每个端点。缺失端点会导致测试失败,连接器将无法通过审批。在运行时,网关会期望所有端点可用——若缺失取消端点,将无法作废支付订单。
检测方式 如果连接器的路由/处理文件未为所有6个支付流程路径定义处理函数,请立即停止并添加缺失的端点后再继续。
正确示例
typescript
import { Router } from "express";

const router = Router();

// 实现了全部6个支付流程端点
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;
错误示例
typescript
import { Router } from "express";

const router = Router();

// 缺失Manifest、入站请求和退款端点
// 这将导致认证失败并破坏运行时操作
router.post("/payments", createPaymentHandler);
router.post("/payments/:paymentId/cancellations", cancelPaymentHandler);
router.post("/payments/:paymentId/settlements", capturePaymentHandler);

export default router;

Constraint: Return correct HTTP status codes and response shapes

约束:返回正确的HTTP状态码和响应格式

Each endpoint MUST return the exact response shape documented in the PPP API. Create Payment MUST return
paymentId
,
status
,
authorizationId
,
tid
,
nsu
,
acquirer
,
code
,
message
,
delayToAutoSettle
,
delayToAutoSettleAfterAntifraud
, and
delayToCancel
. Cancel MUST return
paymentId
,
cancellationId
,
code
,
message
,
requestId
. Capture MUST return
paymentId
,
settleId
,
value
,
code
,
message
,
requestId
. Refund MUST return
paymentId
,
refundId
,
value
,
code
,
message
,
requestId
.
Why this matters The Gateway parses these fields programmatically. Missing fields cause deserialization errors and the Gateway treats the payment as failed. Incorrect
delayToAutoSettle
values cause payments to auto-cancel or auto-capture at wrong times.
Detection If a response object is missing any of the required fields for its endpoint, STOP and add the missing fields.
Correct
typescript
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;
}

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);
}
Wrong
typescript
// 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,
  });
}
每个端点必须返回PPP API文档中规定的精确响应格式。创建支付端点必须返回
paymentId
status
authorizationId
tid
nsu
acquirer
code
message
delayToAutoSettle
delayToAutoSettleAfterAntifraud
delayToCancel
。取消支付端点必须返回
paymentId
cancellationId
code
message
requestId
。捕获支付端点必须返回
paymentId
settleId
value
code
message
requestId
。退款支付端点必须返回
paymentId
refundId
value
code
message
requestId
重要性 网关会以编程方式解析这些字段。缺失字段会导致反序列化错误,网关会将该支付标记为失败。
delayToAutoSettle
值不正确会导致支付订单在错误的时间自动取消或自动捕获。
检测方式 如果响应对象缺失对应端点的任何必填字段,请立即停止并添加缺失的字段。
正确示例
typescript
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;
}

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小时(秒)
    delayToAutoSettleAfterAntifraud: 1800, // 30分钟(秒)
    delayToCancel: 21600,         // 6小时(秒)
  };

  res.status(200).json(response);
}
错误示例
typescript
// 缺失必填字段 — 网关会拒绝该响应
async function createPaymentHandler(req: Request, res: Response): Promise<void> {
  const result = await processPaymentWithAcquirer(req.body);

  // 缺失:authorizationId、nsu、tid、acquirer、code、message、
  // delayToAutoSettle、delayToAutoSettleAfterAntifraud、delayToCancel
  res.status(200).json({
    paymentId: req.body.paymentId,
    status: result.status,
  });
}

Constraint: Manifest must declare all supported payment methods

约束:Manifest必须声明所有支持的支付方式

The GET
/manifest
endpoint MUST return a
paymentMethods
array listing every payment method the connector supports, with the correct
name
and
allowsSplit
configuration for each.
Why this matters The Gateway reads the manifest to determine which payment methods are available. If a method is missing, merchants cannot configure it in the VTEX Admin. An incorrect
allowsSplit
value causes split payment failures.
Detection If the manifest handler returns an empty
paymentMethods
array or hardcodes methods the provider does not actually support, STOP and fix the manifest.
Correct
typescript
async 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);
}
Wrong
typescript
// Empty manifest — no payment methods will appear in the Admin
async function manifestHandler(_req: Request, res: Response): Promise<void> {
  res.status(200).json({ paymentMethods: [] });
}
GET
/manifest
端点必须返回
paymentMethods
数组,列出连接器支持的所有支付方式,且每个支付方式需包含正确的
name
allowsSplit
配置。
重要性 网关会读取Manifest来确定可用的支付方式。若缺失某一支付方式,商家将无法在VTEX管理后台配置该方式。
allowsSplit
值不正确会导致分账支付失败。
检测方式 如果Manifest处理函数返回空的
paymentMethods
数组,或硬编码了提供商实际不支持的支付方式,请立即停止并修复Manifest。
正确示例
typescript
async 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);
}
错误示例
typescript
// 空Manifest — 管理后台不会显示任何支付方式
async function manifestHandler(_req: Request, res: Response): Promise<void> {
  res.status(200).json({ paymentMethods: [] });
}

Preferred pattern

推荐模式

Architecture overview:
text
Shopper → VTEX Checkout → VTEX Payment Gateway → [Your Connector Middleware] → Acquirer/PSP
                    Configuration Flow (Admin)
Recommended TypeScript interfaces for all endpoint contracts:
typescript
// --- 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;
  };
}
Complete payment flow router with all 6 endpoints:
typescript
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;
Configuration flow endpoints (optional, for merchant onboarding):
typescript
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;
架构概述:
text
购物者 → VTEX结账系统 → VTEX支付网关 → [你的连接器中间件] → 收单机构/支付服务提供商(PSP)
                    配置流程(管理后台)
所有端点契约推荐使用TypeScript接口:
typescript
// --- Manifest ---
interface ManifestResponse {
  paymentMethods: Array<{
    name: string;
    allowsSplit: "onCapture" | "onAuthorize" | "disabled";
  }>;
}

// --- 创建支付 ---
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;
}

// --- 取消支付 ---
interface CancelPaymentResponse {
  paymentId: string;
  cancellationId: string | null;
  code: string | null;
  message: string | null;
  requestId: string;
}

// --- 捕获/结算支付 ---
interface CapturePaymentResponse {
  paymentId: string;
  settleId: string | null;
  value: number;
  code: string | null;
  message: string | null;
  requestId: string;
}

// --- 退款支付 ---
interface RefundPaymentResponse {
  paymentId: string;
  refundId: string | null;
  value: number;
  code: string | null;
  message: string | null;
  requestId: string;
}

// --- 入站请求 ---
interface InboundResponse {
  requestId: string;
  paymentId: string;
  responseData: {
    statusCode: number;
    contentType: string;
    content: string;
  };
}
包含全部6个端点的完整支付流程路由:
typescript
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 ?? "取消成功",
    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;
配置流程端点(可选,用于商家入驻):
typescript
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;

Common failure modes

常见失败模式

  • Partial endpoint implementation — Implementing only Create Payment and Capture while skipping Manifest, Cancel, Refund, and Inbound Request. The Test Suite tests all endpoints and will fail homologation. At runtime, the Gateway cannot cancel or refund payments.
  • Incorrect HTTP methods — Using POST for the Manifest endpoint or GET for Create Payment. The Gateway sends specific HTTP methods; mismatched handlers return 404 or 405.
  • Missing or zero delay values — Omitting
    delayToAutoSettle
    ,
    delayToAutoSettleAfterAntifraud
    , or
    delayToCancel
    from the Create Payment response, or setting them to zero. This causes immediate auto-capture or auto-cancel, leading to premature settlement or lost payments.
  • Incomplete response shapes — Returning only
    paymentId
    and
    status
    without
    authorizationId
    ,
    tid
    ,
    nsu
    ,
    acquirer
    , etc. The Gateway deserializes all fields and treats missing ones as failures.
  • 端点部分实现 — 仅实现创建支付和捕获端点,跳过Manifest、取消、退款和入站请求端点。测试套件会测试所有端点,这种情况会导致认证失败。在运行时,网关无法取消或退款支付订单。
  • HTTP方法错误 — 对Manifest端点使用POST方法,或对创建支付端点使用GET方法。网关会发送特定的HTTP方法;方法不匹配会返回404或405错误。
  • 延迟值缺失或为零 — 在创建支付响应中省略
    delayToAutoSettle
    delayToAutoSettleAfterAntifraud
    delayToCancel
    ,或将其设置为零。这会导致支付订单立即自动捕获或自动取消,从而导致提前结算或订单丢失。
  • 响应格式不完整 — 仅返回
    paymentId
    status
    ,缺失
    authorizationId
    tid
    nsu
    acquirer
    等字段。网关会反序列化所有字段,缺失字段会被视为失败。

Review checklist

审核清单

  • Are all 6 payment-flow endpoints implemented (Manifest, Create Payment, Cancel, Capture, Refund, Inbound Request)?
  • Does each endpoint return the complete response shape with all required fields?
  • Does the Manifest declare all payment methods the provider actually supports?
  • Are the correct HTTP methods used (GET for Manifest, POST for everything else)?
  • Are
    delayToAutoSettle
    ,
    delayToAutoSettleAfterAntifraud
    , and
    delayToCancel
    set to sensible non-zero values?
  • Is the connector served over HTTPS on port 443 with TLS 1.2?
  • Does the connector respond within 5 seconds for test suite and 20 seconds in production?
  • Are configuration flow endpoints implemented if merchant self-onboarding is needed?
  • 是否实现了全部6个支付流程端点(Manifest、创建支付、取消支付、捕获、退款、入站请求)?
  • 每个端点是否返回包含所有必填字段的完整响应格式?
  • Manifest是否声明了提供商实际支持的所有支付方式?
  • 是否使用了正确的HTTP方法(Manifest用GET,其他用POST)?
  • delayToAutoSettle
    delayToAutoSettleAfterAntifraud
    delayToCancel
    是否设置了合理的非零值?
  • 连接器是否通过HTTPS在443端口提供服务,且使用TLS 1.2协议?
  • 连接器在测试套件中的响应时间是否在5秒以内,生产环境是否在20秒以内?
  • 若需要商家自助入驻,是否实现了配置流程端点?

Related skills

相关技能

  • payment-idempotency
    — Idempotency keys (
    paymentId
    ,
    requestId
    ) and state machine for duplicate prevention
  • payment-async-flow
    — Async payment methods,
    callbackUrl
    , and the 7-day retry window
  • payment-pci-security
    — PCI compliance, Secure Proxy, and card data handling
  • payment-idempotency
    — 幂等性密钥(
    paymentId
    requestId
    )和重复请求预防状态机
  • payment-async-flow
    — 异步支付方式、
    callbackUrl
    和7天重试窗口
  • payment-pci-security
    — PCI合规、安全代理和卡片数据处理

Reference

参考资料