Procare Pay Integration Service

Complete API Documentation for Payment Processing

Return Transaction (Void or Refund)

DELETE /v1/rest/merchants/{merchantId}/transactions/{transactionId}

Description: Returns a transaction for a given merchant. The system automatically determines whether to void or refund the transaction based on its settlement status. Unsettled transactions are voided (no funds transferred), while settled transactions are refunded (funds returned to customer).

Void vs Refund - What's the difference?
  • Void: Cancels a transaction before funds are settled (usually same day). The customer never sees the charge on their statement.
  • Refund: Returns funds after the transaction has settled (1-2 days after transaction). The customer sees both the original charge and the refund on their statement.

This endpoint handles both automatically - you don't need to specify which one to perform. The system determines the appropriate action based on settlement status.

Important Considerations:
  • Voids completely cancel the transaction - no funds are ever transferred
  • Refunds return money to the customer and may take 3-5 business days to appear
  • Partial returns are not supported by this endpoint - use the refunds endpoint for partial refunds
  • Once returned, a transaction cannot be un-returned
ACH Considerations: ACH transactions cannot be voided after submission - they can only be returned, which may take several days. Due to the way ACH funds are handled and the flow of money, it is possible that an ACH transaction could be returned while the transaction is in-flight (before settlement) and then the original transaction is returned by the originating bank for non-sufficient funds resulting in the merchant never getting the original funds and losing the returned funds (a net negative to the merchant). As a result, it is strongly recommended not to use this endpoint for returning or voiding an ACH transaction if there is a high likelihood that the transaction cannot be voided (e.g. after 7pm merchant local time).

Path Parameters

merchantId (required)
Type: string
Description: The merchant ID
transactionId (required)
Type: string
Description: The transaction ID (GUID or retref) to return

Authentication

This endpoint requires bearer token authentication using the Authorization header.

Note: The bearer token must be a valid Cognito token.

Code Examples

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

public class TransactionClient
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl = "https://your-api-domain.com";
    private readonly string _bearerToken;

    public TransactionClient(string bearerToken)
    {
        _bearerToken = bearerToken;
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri(_baseUrl);
        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", _bearerToken);
        _httpClient.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));
    }

    public async Task<string> ReturnTransactionAsync(
        string merchantId,
        string transactionId)
    {
        try
        {
            string endpoint = $"/v1/rest/merchants/{merchantId}/transactions/{transactionId}";

            HttpResponseMessage response = await _httpClient.DeleteAsync(endpoint);
            response.EnsureSuccessStatusCode();

            return await response.Content.ReadAsStringAsync();
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine($"Request error: {e.Message}");
            throw;
        }
    }

    // With confirmation prompt
    public async Task<bool> ReturnTransactionWithConfirmationAsync(
        string merchantId,
        string transactionId,
        string amount)
    {
        Console.WriteLine($"WARNING: You are about to return a transaction for ${amount}");
        Console.Write("Are you sure you want to proceed? (yes/no): ");

        string confirmation = Console.ReadLine();
        if (confirmation?.ToLower() != "yes")
        {
            Console.WriteLine("Return cancelled.");
            return false;
        }

        try
        {
            string result = await ReturnTransactionAsync(merchantId, transactionId);
            Console.WriteLine("Transaction returned successfully.");
            Console.WriteLine(result);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Failed to return transaction: {ex.Message}");
            return false;
        }
    }
}

// Example usage: Return a transaction
var client = new TransactionClient("your-bearer-token-here");
string result = await client.ReturnTransactionAsync(
    "12345678901",                              // merchantId
    "a1b2c3d4-e5f6-7890-abcd-ef1234567890"     // transactionId
);
Console.WriteLine(result);

// With confirmation
bool returned = await client.ReturnTransactionWithConfirmationAsync(
    "12345678901",
    "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "150.00"
);
Imports System
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Threading.Tasks

Public Class TransactionClient
    Private ReadOnly _httpClient As HttpClient
    Private ReadOnly _baseUrl As String = "https://your-api-domain.com"
    Private ReadOnly _bearerToken As String

    Public Sub New(bearerToken As String)
        _bearerToken = bearerToken
        _httpClient = New HttpClient()
        _httpClient.BaseAddress = New Uri(_baseUrl)
        _httpClient.DefaultRequestHeaders.Authorization =
            New AuthenticationHeaderValue("Bearer", _bearerToken)
        _httpClient.DefaultRequestHeaders.Accept.Add(
            New MediaTypeWithQualityHeaderValue("application/json"))
    End Sub

    Public Async Function ReturnTransactionAsync(
        merchantId As String,
        transactionId As String) As Task(Of String)

        Try
            Dim endpoint As String = $"/v1/rest/merchants/{merchantId}/transactions/{transactionId}"

            Dim response As HttpResponseMessage = Await _httpClient.DeleteAsync(endpoint)
            response.EnsureSuccessStatusCode()

            Return Await response.Content.ReadAsStringAsync()
        Catch e As HttpRequestException
            Console.WriteLine($"Request error: {e.Message}")
            Throw
        End Try
    End Function
End Class

' Example usage
Dim client As New TransactionClient("your-bearer-token-here")
Dim result As String = Await client.ReturnTransactionAsync(
    "12345678901",
    "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)
Console.WriteLine(result)
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class TransactionClient {
    private final String baseUrl;
    private final String bearerToken;
    private final HttpClient httpClient;

    public TransactionClient(String baseUrl, String bearerToken) {
        this.baseUrl = baseUrl;
        this.bearerToken = bearerToken;
        this.httpClient = HttpClient.newHttpClient();
    }

    public String returnTransaction(String merchantId, String transactionId)
            throws IOException, InterruptedException {

        String endpoint = baseUrl + "/v1/rest/merchants/" + merchantId +
                         "/transactions/" + transactionId;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(endpoint))
            .header("Authorization", "Bearer " + bearerToken)
            .header("Accept", "application/json")
            .DELETE()
            .build();

        HttpResponse<String> response = httpClient.send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

        if (response.statusCode() != 200) {
            throw new IOException("HTTP Error: " + response.statusCode() +
                                 " - " + response.body());
        }

        return response.body();
    }
}

// Example usage
TransactionClient client = new TransactionClient(
    "https://your-api-domain.com",
    "your-bearer-token-here"
);

String result = client.returnTransaction(
    "12345678901",
    "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
);
System.out.println(result);
require 'net/http'
require 'uri'
require 'json'

class TransactionClient
  def initialize(base_url, bearer_token)
    @base_url = base_url
    @bearer_token = bearer_token
  end

  def return_transaction(merchant_id, transaction_id)
    endpoint = "#{@base_url}/v1/rest/merchants/#{merchant_id}/transactions/#{transaction_id}"
    uri = URI.parse(endpoint)

    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true

    request = Net::HTTP::Delete.new(uri.request_uri)
    request['Authorization'] = "Bearer #{@bearer_token}"
    request['Accept'] = 'application/json'

    response = http.request(request)

    if response.code.to_i != 200
      raise "HTTP Error: #{response.code} - #{response.body}"
    end

    response.body
  end
end

# Example usage
client = TransactionClient.new(
  'https://your-api-domain.com',
  'your-bearer-token-here'
)

result = client.return_transaction(
  '12345678901',
  'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
)
puts result

Response Format

Success Response (HTTP 200) - Void

{
  "responseStatus": "TransactionRefunded",
  "responseMessage": "Transaction voided successfully",
  "responseCode": "200",
  "responseReason": null,
  "responseResult": "success",
  "legacyResponseCode": "A",
  "correlationId": "void-123-456-789",
  "transactionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "retref": 123456789,
  "merchid": "12345678901",
  "transactionAmount": "150.00",
  "authAmount": "150.00",
  "accttype": "VISA",
  "maskedAccountNumber": "************1234",
  "lastfour": "1234",
  "accountHolderName": "John Doe",
  "orderid": "ORD-2026-12345",
  "settlementStatus": "Voided",
  "requestDate": "2026-04-02T10:30:00Z",
  "authDate": "2026-04-02T10:30:03Z",
  "retdecdate": "2026-04-02T14:15:00Z"
}

Success Response (HTTP 200) - Refund

{
  "responseStatus": "TransactionRefunded",
  "responseMessage": "Transaction refunded successfully",
  "responseCode": "200",
  "responseReason": null,
  "responseResult": "success",
  "legacyResponseCode": "A",
  "correlationId": "refund-789-012-345",
  "transactionId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "retref": 123456790,
  "merchid": "12345678901",
  "transactionAmount": "150.00",
  "authAmount": "150.00",
  "accttype": "VISA",
  "maskedAccountNumber": "************1234",
  "lastfour": "1234",
  "accountHolderName": "John Doe",
  "orderid": "ORD-2026-12345",
  "settlementStatus": "Refunded",
  "requestDate": "2026-04-02T10:30:00Z",
  "authDate": "2026-04-02T10:30:03Z",
  "settlementDate": "2026-04-03T03:00:00Z",
  "retdecdate": "2026-04-04T12:00:00Z",
  "linkedTransactionId": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}

HTTP Status Codes

Status Code Description
200 Success - Transaction voided or refunded successfully
400 Bad Request - Invalid request or transaction cannot be returned
403 Forbidden - Merchant does not own this transaction
404 Not Found - Transaction with specified ID does not exist
409 Conflict - Transaction already returned/voided
500 Internal Server Error - Server error processing return

Error Responses

Already Returned (HTTP 409)

{
  "responseStatus": "Error",
  "responseMessage": "Transaction already returned",
  "responseCode": "409",
  "responseReason": "This transaction was previously refunded or voided",
  "responseResult": "failure",
  "correlationId": "error-409-123-456"
}

Forbidden - Not Your Transaction (HTTP 403)

{
  "responseStatus": "Error",
  "responseMessage": "Forbidden",
  "responseCode": "403",
  "responseReason": "Merchant does not own this transaction",
  "responseResult": "failure",
  "correlationId": "error-403-789-012"
}

Understanding Settlement Status After Return

Settlement Status Meaning Customer Impact
Voided Transaction cancelled before settlement Charge never appears on customer's statement
Refunded Funds returned after settlement Customer sees original charge and separate refund
PartiallyRefunded Only part of the transaction was refunded Customer receives partial refund amount

Void vs Refund Decision Tree

How the system decides:
  1. Check transaction's settlement status
  2. If settlement status is "Authorized" or "QueuedForCapture" → VOID
  3. If settlement status is "Accepted" or "Settled" → REFUND
  4. If already voided/refunded → Return 409 Conflict

You don't need to specify which action to take - the API determines this automatically.

Best Practices

1. Always Confirm Before Returning

Implement a confirmation step before processing returns, especially for large amounts. Returns cannot be undone.

// Good - requires confirmation
Console.WriteLine($"Returning transaction for ${amount}");
Console.Write("Type 'CONFIRM' to proceed: ");
if (Console.ReadLine() == "CONFIRM")
{
    await client.ReturnTransactionAsync(merchantId, transactionId);
}

2. Handle 409 Conflicts Gracefully

A 409 status means the transaction was already returned. This isn't necessarily an error - inform the user that the return was already processed.

try
{
    await client.ReturnTransactionAsync(merchantId, transactionId);
}
catch (HttpRequestException ex) when (response.StatusCode == HttpStatusCode.Conflict)
{
    Console.WriteLine("This transaction was already returned.");
    // This is acceptable - no need to show as error
}

3. Log All Return Requests

Keep an audit log of who requested returns, when, and why. This is important for accounting and fraud prevention.

await AuditLog.LogAsync(new AuditEntry
{
    Action = "ReturnTransaction",
    UserId = currentUserId,
    TransactionId = transactionId,
    Amount = amount,
    Reason = returnReason,
    Timestamp = DateTime.UtcNow
});

await client.ReturnTransactionAsync(merchantId, transactionId);

4. Notify Customers

Send an email or notification to the customer when their transaction is voided or refunded. Include expected timeframe for refund to appear.

5. Act Quickly for Voids

If you need to cancel a transaction, do it as soon as possible (same day) to ensure it's voided rather than refunded. Voids are instant; refunds take days.

6. Check Settlement Status First

Before returning a transaction, retrieve it first to check its current status and inform the user whether it will be voided or refunded.

// Get transaction details first
var txn = await client.GetTransactionAsync(merchantId, transactionId);

if (txn.SettlementStatus == "Authorized")
{
    Console.WriteLine("This will be VOIDED (immediate cancellation)");
}
else if (txn.SettlementStatus == "Accepted")
{
    Console.WriteLine("This will be REFUNDED (funds returned in 3-5 days)");
}

// Then proceed with return
await client.ReturnTransactionAsync(merchantId, transactionId);

Common Use Cases

1. Order Cancellation (Same Day)

Customer cancels order before shipment:

// Cancel the transaction (will be voided if same day)
await client.ReturnTransactionAsync(merchantId, transactionId);

// Update order status
await UpdateOrderStatus(orderId, "Cancelled");

// Notify customer
await SendEmail(customerEmail, "Order Cancelled - No Charge");

2. Product Return (After Delivery)

Customer returns product and requests refund:

// Process refund (transaction already settled)
await client.ReturnTransactionAsync(merchantId, transactionId);

// Update inventory
await RestockProduct(productId, quantity);

// Notify customer
await SendEmail(customerEmail,
    "Refund Processed - Please allow 3-5 business days for funds to appear");

3. Duplicate Charge

Customer was accidentally charged twice:

// Find duplicate transactions
var duplicates = await FindDuplicateTransactions(orderId);

// Return the duplicate
foreach (var dupTxnId in duplicates.Skip(1))
{
    await client.ReturnTransactionAsync(merchantId, dupTxnId);
    Console.WriteLine($"Returned duplicate charge: {dupTxnId}");
}

4. Fraudulent Transaction

Transaction flagged as fraudulent:

// Immediately return the transaction
await client.ReturnTransactionAsync(merchantId, transactionId);

// Flag account
await FlagAccountForReview(accountId, "Fraudulent transaction");

// Log incident
await LogSecurityIncident(transactionId, "Fraud detected");

5. Customer Service Goodwill Refund

Refunding due to service issue:

// Process goodwill refund
await client.ReturnTransactionAsync(merchantId, transactionId);

// Log the reason
await UpdateTransaction(transactionId, new {
    customAttributes = new {
        returnReason = "Customer service goodwill refund",
        approvedBy = currentUserId,
        supportTicket = ticketNumber
    }
});

// Notify customer with apology
await SendEmail(customerEmail,
    "We apologize for the inconvenience - Full refund processed");

Timing Considerations

Credit Card Transactions:
  • Void window: Usually same business day (before batch closes)
  • Refund timing: 3-5 business days for funds to appear on customer's card
ACH Transactions:
  • Void window: Very limited or not available (ACH processes in batches)
  • Return timing: 5-7 business days for funds to return
  • Important: ACH returns can happen up to 60 days after the transaction

Troubleshooting

Issue: Getting 409 Conflict - Already Returned
  • The transaction was already voided or refunded
  • Check the transaction's settlement status to confirm
  • This is not an error - the desired state is achieved
  • Inform the user the return was already processed
Issue: Getting 403 Forbidden
  • The merchant ID doesn't own this transaction
  • Verify you're using the correct merchant ID
  • The transaction may belong to a different merchant in your organization
  • Check transaction ownership using the GET endpoint first
Issue: Transaction returned but customer hasn't received refund
  • Refunds take 3-5 business days for credit cards, 5-7 for ACH
  • Verify the return was successful by checking settlement status
  • Check that the transaction was refunded, not just voided (voided transactions don't show refunds)
  • Contact the payment processor if refund is delayed beyond expected timeframe
Issue: Want to return only part of transaction
  • This endpoint returns the full transaction amount
  • For partial refunds, use the POST refunds endpoint instead
  • POST /merchants/{merchantId}/transactions/{transactionId}/refunds

Additional Notes

  • This endpoint returns the FULL transaction amount - use the refunds endpoint for partial returns
  • Returns cannot be undone - ensure you have proper authorization before processing
  • The retdecdate field in the response indicates when the return was processed
  • Voided transactions never settled, so funds were never transferred
  • Refunded transactions return funds that were already transferred
  • The linkedTransactionId field links the refund to the original transaction
  • Settlement status changes to "Voided" or "Refunded" after successful return
  • Always store the correlationId for customer service and troubleshooting
  • ACH returns may show as pending for several days before completing