Skip to main content

Overview

The cancel() method handles cancellation requests for authorized payments that have not yet been settled. This is typically called when a customer cancels an order or when a payment needs to be voided before capture.

Method signature

public async cancel(
  cancellation: CancellationRequest
): Promise<CancellationResponse>

Parameters

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

Response

response
CancellationResponse
The cancellation response indicating the result of the cancellation request.
paymentId
string
required
Echo of the payment identifier from the request
cancellationId
string
Unique identifier for this cancellation (present when approved)
code
string
Response code from the payment provider
message
string
Human-readable message describing the cancellation result
requestId
string
required
Echo of the request identifier from the request

Response types

Cancellation was successfully processed.
{
  paymentId: "ABC123",
  requestId: "REQ-456",
  cancellationId: "CANCEL-789",
  code: "success",
  message: "Cancellation approved"
}

Implementation

The test suite implementation shows the basic pattern:
public async cancel(
  cancellation: CancellationRequest
): Promise<CancellationResponse> {
  if (this.isTestSuite) {
    return Cancellations.approve(cancellation, {
      cancellationId: randomString(),
    })
  }

  throw new Error('Not implemented')
}

Common scenarios

Full cancellation

Cancel the entire authorized amount:
public async cancel(
  cancellation: CancellationRequest
): Promise<CancellationResponse> {
  try {
    // Call your payment provider's cancellation API
    const providerResponse = await this.paymentProvider.cancelPayment({
      transactionId: cancellation.tid,
      authorizationId: cancellation.authorizationId,
    })

    return Cancellations.approve(cancellation, {
      cancellationId: providerResponse.cancellationId,
      code: providerResponse.code,
      message: 'Payment successfully cancelled',
    })
  } catch (error) {
    return Cancellations.deny(cancellation, {
      code: 'cancellation-failed',
      message: error.message,
    })
  }
}

Partial cancellation

Some payment providers support canceling only part of the authorized amount:
public async cancel(
  cancellation: CancellationRequest
): Promise<CancellationResponse> {
  const { value, paymentId, authorizationId } = cancellation

  // Check if partial cancellation is supported
  if (value && value < this.getOriginalAmount(paymentId)) {
    // Partial cancellation logic
    const providerResponse = await this.paymentProvider.partialCancel({
      authorizationId,
      amount: value,
    })

    return Cancellations.approve(cancellation, {
      cancellationId: providerResponse.id,
    })
  }

  // Full cancellation logic
  // ...
}

Idempotent cancellations

Handle duplicate cancellation requests:
public async cancel(
  cancellation: CancellationRequest
): Promise<CancellationResponse> {
  // Check if this cancellation was already processed
  const existing = await this.getCancellation(cancellation.requestId)
  
  if (existing) {
    return existing // Return same response for idempotency
  }

  // Process new cancellation
  const response = await this.processCancellation(cancellation)
  
  // Persist for future duplicate requests
  await this.saveCancellation(cancellation.requestId, response)
  
  return response
}

Helper methods

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

Cancellations.approve(cancellation, {
  cancellationId: 'CANCEL-123',
  code: 'success',
  message: 'Optional success message',
})

Error handling

If the payment ID doesn’t exist, deny the cancellation with an appropriate message:
return Cancellations.deny(cancellation, {
  code: 'payment-not-found',
  message: 'Original payment not found',
})
If the payment has already been settled, cancellation is not possible:
return Cancellations.deny(cancellation, {
  code: 'already-settled',
  message: 'Cannot cancel settled payment. Use refund instead.',
})
If the payment was already cancelled, return the original cancellation:
const existing = await this.getCancellation(cancellation.paymentId)
if (existing) {
  return existing
}
If the payment provider times out, you can return a pending status or deny:
return Cancellations.deny(cancellation, {
  code: 'timeout',
  message: 'Payment provider timeout. Please retry.',
})

Best practices

  1. Always verify payment status before attempting cancellation. Check that the payment is authorized but not yet settled.
  2. Implement idempotency by storing cancellation responses and returning the same response for duplicate requestId values.
  3. Include meaningful error messages to help merchants understand why a cancellation was denied.
  4. Log cancellation attempts for audit trails and troubleshooting.
  5. Handle partial cancellations if your payment provider supports them.

Payment lifecycle

Cancellation is only possible for authorized payments. Once settled, use refund() instead.

Build docs developers (and LLMs) love