Procare Pay Integration Service

Complete API Documentation for Payment Processing

Delete Payor

DELETE /v1/rest/merchants/{merchantId}/payors/{payorId}

Description: Deletes the payor profile and all associated saved payment methods for a merchant with the given IDs. This is a soft delete operation - the payor record is marked as deleted but remains in the system for historical and audit purposes.

Important: This operation will delete the payor profile AND all associated saved payment methods (credit cards and bank accounts). This action cannot be undone. Ensure you have confirmed the deletion with the user before proceeding.

Path Parameters

merchantId (required)
Type: string
Pattern: Must be an 11-digit number (e.g., "12345678901")
Description: The 11-digit merchant identifier
payorId (required)
Type: string
Description: The unique payor identifier to delete

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 PayorClient
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl = "https://your-api-domain.com"; // Replace with actual base URL
    private readonly string _bearerToken;

    public PayorClient(string bearerToken)
    {
        _bearerToken = bearerToken;
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri(_baseUrl);

        // Set the Authorization header with bearer token
        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", _bearerToken);

        // Set Accept header to request JSON
        _httpClient.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));
    }

    public async Task<string> DeletePayorAsync(
        string merchantId,
        string payorId)
    {
        try
        {
            // Construct the endpoint URL
            string endpoint = $"/v1/rest/merchants/{merchantId}/payors/{payorId}";

            // Make the DELETE request
            HttpResponseMessage response = await _httpClient.DeleteAsync(endpoint);

            // Ensure the request was successful
            response.EnsureSuccessStatusCode();

            // Read and return the response content as a string
            string responseBody = await response.Content.ReadAsStringAsync();
            return responseBody;
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine($"Request error: {e.Message}");
            throw;
        }
    }

    public async Task<bool> DeletePayorWithConfirmationAsync(
        string merchantId,
        string payorId)
    {
        try
        {
            Console.WriteLine("WARNING: This will delete the payor and all saved payment methods.");
            Console.Write("Are you sure you want to proceed? (yes/no): ");

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

            // Proceed with deletion
            string endpoint = $"/v1/rest/merchants/{merchantId}/payors/{payorId}";
            HttpResponseMessage response = await _httpClient.DeleteAsync(endpoint);

            if (response.IsSuccessStatusCode)
            {
                Console.WriteLine("Payor deleted successfully.");
                return true;
            }
            else if (response.StatusCode == System.Net.HttpStatusCode.Conflict)
            {
                Console.WriteLine("Payor was already deleted.");
                return false;
            }
            else
            {
                Console.WriteLine($"Delete failed with status code: {response.StatusCode}");
                return false;
            }
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine($"Request error: {e.Message}");
            throw;
        }
    }
}

// Example usage: Simple delete
var client = new PayorClient("your-bearer-token-here");
string result = await client.DeletePayorAsync(
    "12345678901",              // merchantId
    "5513027774438108364"       // payorId
);
Console.WriteLine(result);

// Example usage: Delete with confirmation
bool wasDeleted = await client.DeletePayorWithConfirmationAsync(
    "12345678901",              // merchantId
    "5513027774438108364"       // payorId
);
Imports System
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Threading.Tasks

Public Class PayorClient
    Private ReadOnly _httpClient As HttpClient
    Private ReadOnly _baseUrl As String = "https://your-api-domain.com" ' Replace with actual base URL
    Private ReadOnly _bearerToken As String

    Public Sub New(bearerToken As String)
        _bearerToken = bearerToken
        _httpClient = New HttpClient()
        _httpClient.BaseAddress = New Uri(_baseUrl)

        ' Set the Authorization header with bearer token
        _httpClient.DefaultRequestHeaders.Authorization = _
            New AuthenticationHeaderValue("Bearer", _bearerToken)

        ' Set Accept header to request JSON
        _httpClient.DefaultRequestHeaders.Accept.Add( _
            New MediaTypeWithQualityHeaderValue("application/json"))
    End Sub

    Public Async Function DeletePayorAsync(
        merchantId As String,
        payorId As String) As Task(Of String)

        Try
            ' Construct the endpoint URL
            Dim endpoint As String = $"/v1/rest/merchants/{merchantId}/payors/{payorId}"

            ' Make the DELETE request
            Dim response As HttpResponseMessage = Await _httpClient.DeleteAsync(endpoint)

            ' Ensure the request was successful
            response.EnsureSuccessStatusCode()

            ' Read and return the response content as a string
            Dim responseBody As String = Await response.Content.ReadAsStringAsync()
            Return responseBody

        Catch e As HttpRequestException
            Console.WriteLine($"Request error: {e.Message}")
            Throw
        End Try
    End Function

    Public Async Function DeletePayorWithConfirmationAsync(
        merchantId As String,
        payorId As String) As Task(Of Boolean)

        Try
            Console.WriteLine("WARNING: This will delete the payor and all saved payment methods.")
            Console.Write("Are you sure you want to proceed? (yes/no): ")

            Dim confirmation As String = Console.ReadLine()
            If confirmation?.ToLower() <> "yes" Then
                Console.WriteLine("Delete operation cancelled.")
                Return False
            End If

            ' Proceed with deletion
            Dim endpoint As String = $"/v1/rest/merchants/{merchantId}/payors/{payorId}"
            Dim response As HttpResponseMessage = Await _httpClient.DeleteAsync(endpoint)

            If response.IsSuccessStatusCode Then
                Console.WriteLine("Payor deleted successfully.")
                Return True
            ElseIf response.StatusCode = Net.HttpStatusCode.Conflict Then
                Console.WriteLine("Payor was already deleted.")
                Return False
            Else
                Console.WriteLine($"Delete failed with status code: {response.StatusCode}")
                Return False
            End If

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

' Example usage: Simple delete
Dim client As New PayorClient("your-bearer-token-here")
Dim result As String = Await client.DeletePayorAsync(
    "12345678901",              ' merchantId
    "5513027774438108364"       ' payorId
)
Console.WriteLine(result)

' Example usage: Delete with confirmation
Dim wasDeleted As Boolean = Await client.DeletePayorWithConfirmationAsync(
    "12345678901",              ' merchantId
    "5513027774438108364"       ' payorId
)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

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

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

    public String deletePayor(String merchantId, String payorId)
            throws IOException, InterruptedException {
        try {
            // Construct the endpoint URL
            String endpoint = String.format("%s/v1/rest/merchants/%s/payors/%s",
                baseUrl, merchantId, payorId);

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

            // Send the request and get response
            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());

            // Check if request was successful (status code 200-299)
            if (response.statusCode() < 200 || response.statusCode() >= 300) {
                throw new RuntimeException("HTTP Error: " + response.statusCode() +
                    " - " + response.body());
            }

            // Return the response body
            return response.body();

        } catch (IOException | InterruptedException e) {
            System.err.println("Request error: " + e.getMessage());
            throw e;
        }
    }

    public boolean deletePayorWithConfirmation(String merchantId, String payorId)
            throws IOException, InterruptedException {
        try {
            System.out.println("WARNING: This will delete the payor and all saved payment methods.");
            System.out.print("Are you sure you want to proceed? (yes/no): ");

            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            String confirmation = reader.readLine();

            if (!"yes".equalsIgnoreCase(confirmation)) {
                System.out.println("Delete operation cancelled.");
                return false;
            }

            // Proceed with deletion
            String endpoint = String.format("%s/v1/rest/merchants/%s/payors/%s",
                baseUrl, merchantId, payorId);

            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 && response.statusCode() < 300) {
                System.out.println("Payor deleted successfully.");
                return true;
            } else if (response.statusCode() == 409) {
                System.out.println("Payor was already deleted.");
                return false;
            } else {
                System.out.println("Delete failed with status code: " + response.statusCode());
                return false;
            }

        } catch (IOException | InterruptedException e) {
            System.err.println("Request error: " + e.getMessage());
            throw e;
        }
    }

    // Example usage
    public static void main(String[] args) {
        try {
            PayorClient client = new PayorClient(
                "https://your-api-domain.com",
                "your-bearer-token-here"
            );

            // Simple delete
            String result = client.deletePayor("12345678901", "5513027774438108364");
            System.out.println(result);

            // Delete with confirmation
            boolean wasDeleted = client.deletePayorWithConfirmation(
                "12345678901",
                "5513027774438108364"
            );
            System.out.println("Was deleted: " + wasDeleted);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
require 'net/http'
require 'uri'
require 'json'

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

  def delete_payor(merchant_id, payor_id)
    # Construct the endpoint URL
    endpoint = "#{@base_url}/v1/rest/merchants/#{merchant_id}/payors/#{payor_id}"
    uri = URI(endpoint)

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

    # Make the request
    response = Net::HTTP.start(uri.hostname, uri.port,
                               use_ssl: uri.scheme == 'https') do |http|
      http.request(request)
    end

    # Check response status
    unless response.is_a?(Net::HTTPSuccess)
      raise "HTTP Error: #{response.code} - #{response.message}\n#{response.body}"
    end

    # Return the response body
    response.body

  rescue StandardError => e
    puts "Request error: #{e.message}"
    raise
  end

  def delete_payor_with_confirmation(merchant_id, payor_id)
    puts 'WARNING: This will delete the payor and all saved payment methods.'
    print 'Are you sure you want to proceed? (yes/no): '

    confirmation = gets.chomp

    unless confirmation.downcase == 'yes'
      puts 'Delete operation cancelled.'
      return false
    end

    # Proceed with deletion
    endpoint = "#{@base_url}/v1/rest/merchants/#{merchant_id}/payors/#{payor_id}"
    uri = URI(endpoint)

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

    response = Net::HTTP.start(uri.hostname, uri.port,
                               use_ssl: uri.scheme == 'https') do |http|
      http.request(request)
    end

    case response
    when Net::HTTPSuccess
      puts 'Payor deleted successfully.'
      true
    when Net::HTTPConflict
      puts 'Payor was already deleted.'
      false
    else
      puts "Delete failed with status code: #{response.code}"
      false
    end

  rescue StandardError => e
    puts "Request error: #{e.message}"
    raise
  end
end

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

  # Simple delete
  result = client.delete_payor('12345678901', '5513027774438108364')
  puts result

  # Delete with confirmation
  was_deleted = client.delete_payor_with_confirmation(
    '12345678901',
    '5513027774438108364'
  )
  puts "Was deleted: #{was_deleted}"

rescue => e
  puts "Error: #{e.message}"
end

Response Format

The response returns the deleted payor's information with the isDeleted flag set to true and the deletedDate populated. The response follows the same PayorResponse schema as the GET endpoint.

Success Response (HTTP 200)

{
  "responseStatus": "Accepted",
  "responseMessage": "Payor deleted successfully",
  "responseCode": "200",
  "responseReason": null,
  "responseResult": "success",
  "legacyResponseCode": "A",
  "correlationId": "delete-123-456-789",
  "customAttributes": null,
  "payorId": "5513027774438108364",
  "payorIdLimitedBitFlag": true,
  "applicationId": "14",
  "customerAccountId": "CUST-12345",
  "createdDate": "2024-01-15T10:30:00Z",
  "isDeleted": true,
  "accountStatus": "Deleted",
  "deletedDate": "2026-04-02T14:23:45Z",
  "defaultSavedPaymentMethodId": null,
  "savedPaymentMethodIds": [],
  "savedPaymentMethods": [],
  "customerData": {
    "originalAccountType": "Premium",
    "deletedBy": "CustomerService"
  }
}

Response Fields

The response contains all standard PayorResponse fields. Key fields to note after deletion:

Field Type Description
responseStatus string Typically "Accepted" for successful deletion
isDeleted boolean Set to true after successful deletion
deletedDate string (date-time) The timestamp when the payor was deleted (UTC)
accountStatus string Changed to indicate deleted status
savedPaymentMethodIds array Empty array - all payment methods are deleted
savedPaymentMethods array Empty array - all payment methods are deleted
defaultSavedPaymentMethodId string Set to null - no default payment method after deletion
correlationId string Logging correlation ID for troubleshooting

HTTP Status Codes

Status Code Description
200 Success - Payor and all associated payment methods deleted successfully
400 Bad Request - Invalid merchantId or payorId format
404 Not Found - Payor with the specified ID does not exist for this merchant
409 Conflict - Payor has already been deleted (idempotent operation detected)
500 Internal Server Error - Server encountered an unexpected error

Error Responses

Already Deleted Error (HTTP 409)

{
  "responseStatus": "Error",
  "responseMessage": "Payor has already been deleted",
  "responseCode": "409",
  "responseReason": "The payor was previously deleted and cannot be deleted again",
  "responseResult": "failure",
  "correlationId": "error-409-123-456",
  "payorId": "5513027774438108364",
  "isDeleted": true,
  "deletedDate": "2026-03-15T10:00:00Z"
}

Not Found Error (HTTP 404)

{
  "responseStatus": "NotFound",
  "responseMessage": "Payor not found",
  "responseCode": "404",
  "responseReason": "No payor exists with the specified ID for this merchant",
  "responseResult": "failure",
  "correlationId": "error-404-789-012"
}

What Gets Deleted

Deletion Scope:

When you delete a payor, the following are affected:

  • Payor Profile: The payor record is soft-deleted (marked as deleted but retained in the database)
  • All Saved Payment Methods: All credit cards and ACH accounts associated with this payor are deleted (marked as deleted but retained in the database)
  • Default Payment Method: The default payment method designation is removed
  • Historical Data: Transaction history remains intact for reporting and audit purposes

What is NOT deleted:

  • Historical transaction records - these are preserved for accounting and compliance
  • The payor record itself (soft delete) - it's marked as deleted but remains in the database
  • The saved payment records (soft delete) - all are marked as deleted but remain in the database

Soft Delete vs Hard Delete

This endpoint performs a soft delete, which means:

Aspect Soft Delete (This Endpoint) Hard Delete
Data Retention Record remains in database, marked as deleted Record permanently removed from database
Audit Trail Full audit trail preserved Audit trail lost
Recovery Possible to restore (with support assistance) Not recoverable
Historical Reports Payor still appears in historical reports Payor missing from all reports
Compliance Meets regulatory retention requirements May violate retention policies

Best Practices

1. Always Confirm Before Deleting

Implement a confirmation dialog or step to ensure the user really wants to delete the payor. Consider requiring explicit user action (typing "DELETE" or clicking a confirmation button).

// Example confirmation pattern
public async Task<bool> SafeDeletePayorAsync(string merchantId, string payorId)
{
    // First, retrieve the payor to show user what will be deleted
    var payor = await GetPayorByIdAsync(merchantId, payorId);

    Console.WriteLine($"You are about to delete:");
    Console.WriteLine($"  Payor ID: {payor.PayorId}");
    Console.WriteLine($"  Customer Account: {payor.CustomerAccountId}");
    Console.WriteLine($"  Payment Methods: {payor.SavedPaymentMethodIds?.Count ?? 0}");
    Console.WriteLine();
    Console.Write("Type 'DELETE' to confirm: ");

    if (Console.ReadLine() == "DELETE")
    {
        await DeletePayorAsync(merchantId, payorId);
        return true;
    }

    return false;
}

2. Check for Active Subscriptions or Pending Transactions

Before deleting a payor, verify there are no active subscriptions or pending transactions that rely on this payor's payment methods.

3. Handle the 409 Conflict Status Gracefully

A 409 status means the payor was already deleted. This is not necessarily an error - treat it as idempotent and inform the user that the payor is already deleted.

if (response.StatusCode == HttpStatusCode.Conflict)
{
    Console.WriteLine("This payor was already deleted.");
    // Don't throw an exception - this is an acceptable state
}

4. Log All Deletion Requests

Maintain an audit log of all payor deletions, including who requested the deletion, when it occurred, and the reason (if available).

await AuditLog.LogAsync(new AuditEntry
{
    Action = "DeletePayor",
    UserId = currentUserId,
    MerchantId = merchantId,
    PayorId = payorId,
    Timestamp = DateTime.UtcNow,
    Reason = deletionReason,
    CorrelationId = response.CorrelationId
});

5. Notify Affected Users

If appropriate for your application, send a notification to the account holder confirming their payment profile has been removed.

6. Handle Cascading Effects

Consider what else in your system depends on this payor:

  • Cancel any scheduled/recurring payments
  • Update any UI that displays this payor
  • Invalidate any cached payor data
  • Check for any saved references to the deleted payment methods

Common Use Cases

1. Customer Account Closure

When a customer closes their account and wants all payment information removed:

// Complete account closure workflow
await CancelAllScheduledPayments(merchantId, payorId);
await DeletePayorAsync(merchantId, payorId);
await SendAccountClosureEmail(customerEmail);
await UpdateCustomerAccountStatus(customerAccountId, "Closed");

2. Data Privacy Compliance (GDPR, CCPA)

When a customer requests deletion of their personal data:

// Right to be forgotten / data deletion request
await DeletePayorAsync(merchantId, payorId);
await AnonymizeTransactionHistory(payorId);
await LogDataDeletionRequest(customerId, "GDPR Request");

3. Duplicate Account Cleanup

When duplicate payor profiles are identified and need to be consolidated:

// Merge and cleanup duplicate accounts
await MigrateTransactionHistory(duplicatePayorId, primaryPayorId);
await DeletePayorAsync(merchantId, duplicatePayorId);
await LogAccountMerge(primaryPayorId, duplicatePayorId);

4. Test Data Cleanup

When cleaning up test or demo accounts from production:

// Batch cleanup of test accounts
var testPayors = await GetTestPayorsAsync(merchantId);
foreach (var testPayorId in testPayors)
{
    try
    {
        await DeletePayorAsync(merchantId, testPayorId);
        Console.WriteLine($"Deleted test payor: {testPayorId}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Failed to delete {testPayorId}: {ex.Message}");
    }
}

Idempotency

Understanding Idempotency:

This endpoint exhibits idempotent behavior for already-deleted payors. If you attempt to delete a payor that was already deleted, you'll receive a 409 Conflict response. This allows you to safely retry delete operations without worrying about causing errors.

First delete: Returns 200 Success
Subsequent deletes: Returns 409 Conflict (already deleted)

Recovery from Accidental Deletion

Important: While the payor record is soft-deleted and remains in the database, there is no self-service recovery mechanism. If a payor is deleted accidentally:
  • Contact Procare Pay support immediately
  • Provide the correlationId from the delete response
  • Provide the merchantId and payorId
  • Support may be able to restore the payor profile, but this is not guaranteed
  • Time is critical - contact support as soon as possible

Troubleshooting

Issue: Getting 409 Conflict error
  • This means the payor was already deleted - this is normal for repeated delete requests
  • Check the isDeleted flag and deletedDate in the response to see when it was deleted
  • This is not an error condition - treat as a successful idempotent operation
  • Update your UI to reflect that the payor is already deleted
Issue: Getting 404 Not Found error
  • Verify the payorId is correct and exists for the specified merchant
  • Check that the merchantId is correct (must be exactly 11 digits)
  • The payor may have never existed or was hard-deleted (rare)
  • Verify you have the correct environment (production vs. sandbox)
Issue: Delete succeeds but payor still appears in lists
  • The payor is soft-deleted and may still appear in historical queries
  • Check the isDeleted flag when retrieving payors
  • Filter out deleted payors in your application logic: where !isDeleted
  • Clear any cached data after deletion
Issue: Need to recover accidentally deleted payor
  • Contact Procare Pay support immediately with the correlationId
  • Provide merchantId, payorId, and approximate deletion time
  • Support may be able to restore the payor (not guaranteed)
  • The sooner you contact support, the better chance of recovery

Security Considerations

  • Authorization: Ensure the user has permission to delete payors for this merchant
  • Audit Logging: Always log who deleted the payor, when, and why
  • Confirmation: Require explicit user confirmation before deleting
  • Rate Limiting: Monitor for unusual deletion patterns that might indicate abuse
  • Notification: Consider notifying the account holder when their profile is deleted
  • Data Retention: Understand your organization's data retention policies before implementing deletion

Additional Notes

  • The merchantId must be exactly 11 digits. Requests with invalid formats will return a 400 Bad Request error.
  • All saved payment methods associated with the payor are automatically deleted as part of this operation.
  • Transaction history is preserved even after payor deletion for compliance and reporting purposes.
  • Deleted payors cannot be used for new transactions but remain visible in historical reports.
  • The correlationId in the response is crucial for troubleshooting and should be logged.
  • This is a soft delete - the record remains in the database with isDeleted: true.
  • You cannot create a new payor with the same ID as a deleted payor.
  • Attempting to update or retrieve a deleted payor may return the deleted record with isDeleted: true.

Related Endpoints

  • GET /v1/rest/merchants/{merchantId}/payors/{payorId} - Retrieve payor details (may return deleted payors)
  • GET /v1/rest/merchants/{merchantId}/payors - List payors (may include deleted payors based on filters)
  • DELETE /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{savedPaymentMethodId} - Delete individual payment methods without deleting the payor