Skip to main content

Overview

The settle() method handles settlement (capture) requests for previously authorized payments. Settlement is the process of capturing the authorized funds and initiating the transfer from the customer to the merchant. This typically occurs when an order is invoiced or shipped.

Method signature

public async settle(
  settlement: SettlementRequest
): Promise<SettlementResponse>

Parameters

settlement
SettlementRequest
required
The settlement request object from VTEX.
settlement.paymentId
string
required
Unique identifier for the original payment transaction
settlement.requestId
string
required
Unique identifier for this settlement request
settlement.authorizationId
string
Authorization identifier from the original authorization response
settlement.value
number
required
Amount to settle in cents (may be partial)
settlement.tid
string
Transaction identifier from the original authorization

Response

response
SettlementResponse
The settlement response indicating the result of the settlement request.
paymentId
string
required
Echo of the payment identifier from the request
settleId
string
Unique identifier for this settlement (present when approved)
value
number
Actual amount settled in cents (may differ from requested amount)
code
string
Response code from the payment provider
message
string
Human-readable message describing the settlement result
requestId
string
required
Echo of the request identifier from the request

Response types

Settlement was successfully processed and funds will be captured.
{
  paymentId: "ABC123",
  requestId: "REQ-456",
  settleId: "SETTLE-789",
  value: 10000,
  code: "success",
  message: "Settlement approved"
}

Implementation

The test suite implementation shows the basic pattern:
public async settle(
  settlement: SettlementRequest
): Promise<SettlementResponse> {
  if (this.isTestSuite) {
    return Settlements.deny(settlement)
  }

  throw new Error('Not implemented')
}

Common scenarios

Full settlement

Capture the entire authorized amount:
public async settle(
  settlement: SettlementRequest
): Promise<SettlementResponse> {
  const authorization = await this.getAuthorization(settlement.paymentId)

  if (!authorization) {
    return Settlements.deny(settlement, {
      code: 'authorization-not-found',
      message: 'Original authorization not found',
    })
  }

  if (settlement.value !== authorization.value) {
    return Settlements.deny(settlement, {
      code: 'amount-mismatch',
      message: 'Settlement amount must match authorization',
    })
  }

  const providerResponse = await this.paymentProvider.settle({
    authorizationId: settlement.authorizationId,
    amount: settlement.value,
  })

  return Settlements.approve(settlement, {
    settleId: providerResponse.id,
    value: settlement.value,
  })
}

Partial settlement

Some payment providers support settling only part of the authorized amount:
public async settle(
  settlement: SettlementRequest
): Promise<SettlementResponse> {
  const authorization = await this.getAuthorization(settlement.paymentId)

  // Validate partial settlement is allowed
  if (settlement.value > authorization.value) {
    return Settlements.deny(settlement, {
      code: 'amount-exceeds-authorization',
      message: 'Settlement amount exceeds authorized amount',
    })
  }

  // Check if provider supports partial settlement
  if (!this.paymentProvider.supportsPartialSettlement) {
    return Settlements.deny(settlement, {
      code: 'partial-settlement-not-supported',
      message: 'Provider does not support partial settlement',
    })
  }

  const providerResponse = await this.paymentProvider.settle({
    authorizationId: settlement.authorizationId,
    amount: settlement.value,
  })

  return Settlements.approve(settlement, {
    settleId: providerResponse.id,
    value: settlement.value, // Actual settled amount
  })
}

Multiple settlements

Handle multiple settlement requests for the same payment (split settlements):
public async settle(
  settlement: SettlementRequest
): Promise<SettlementResponse> {
  const authorization = await this.getAuthorization(settlement.paymentId)
  const previousSettlements = await this.getSettlements(settlement.paymentId)

  // Calculate total settled amount
  const totalSettled = previousSettlements.reduce(
    (sum, s) => sum + s.value,
    0
  )

  // Validate remaining amount
  const remainingAmount = authorization.value - totalSettled
  
  if (settlement.value > remainingAmount) {
    return Settlements.deny(settlement, {
      code: 'insufficient-authorized-amount',
      message: `Only ${remainingAmount} cents remaining to settle`,
    })
  }

  const providerResponse = await this.paymentProvider.settle({
    authorizationId: settlement.authorizationId,
    amount: settlement.value,
  })

  return Settlements.approve(settlement, {
    settleId: providerResponse.id,
    value: settlement.value,
  })
}

Idempotent settlements

Handle duplicate settlement requests:
public async settle(
  settlement: SettlementRequest
): Promise<SettlementResponse> {
  // Check if this settlement was already processed
  const existing = await this.getSettlement(settlement.requestId)
  
  if (existing) {
    return existing // Return same response for idempotency
  }

  // Process new settlement
  const response = await this.processSettlement(settlement)
  
  // Persist for future duplicate requests
  await this.saveSettlement(settlement.requestId, response)
  
  return response
}

Helper methods

The @vtex/payment-provider package provides helper methods for creating responses:
import { Settlements } from '@vtex/payment-provider'

Settlements.approve(settlement, {
  settleId: 'SETTLE-123',
  value: 10000,
  code: 'success',
  message: 'Optional success message',
})

Error handling

If the original authorization doesn’t exist:
return Settlements.deny(settlement, {
  code: 'authorization-not-found',
  message: 'Original authorization not found',
})
If the authorization has expired (common after 5-7 days):
return Settlements.deny(settlement, {
  code: 'authorization-expired',
  message: 'Authorization has expired. Please reauthorize.',
})
If the full amount has already been settled:
return Settlements.deny(settlement, {
  code: 'already-settled',
  message: 'Payment has already been fully settled',
})
If trying to settle more than authorized:
return Settlements.deny(settlement, {
  code: 'amount-exceeds-authorization',
  message: `Cannot settle ${settlement.value}. Authorized: ${auth.value}`,
})
If the payment provider returns an error:
return Settlements.deny(settlement, {
  code: error.code || 'provider-error',
  message: error.message || 'Settlement failed at provider',
})

Best practices

  1. Validate authorization exists and is still valid before attempting settlement.
  2. Check authorization expiration - most providers only allow settlement within 5-7 days of authorization.
  3. Implement idempotency by storing settlement responses and returning the same response for duplicate requestId values.
  4. Support partial settlements if your payment provider allows capturing less than the authorized amount.
  5. Track total settled amount if supporting multiple settlements for the same authorization.
  6. Include detailed error messages to help merchants understand settlement failures.
  7. Log all settlement attempts for audit trails and reconciliation.

Payment lifecycle

Settlement can only occur for authorized payments that haven’t been cancelled. After settlement, only refund() operations are possible.
  • authorize() - Create the initial authorization
  • cancel() - Cancel an authorized payment before settlement
  • refund() - Refund a settled payment

Build docs developers (and LLMs) love