Procare Pay Integration Service

Complete API Documentation for Payment Processing

Delete All Saved Payment Methods

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

Description: Deletes all saved payment methods (credit cards and ACH accounts) for a merchant payor. The payor profile itself remains intact - only the payment methods are removed.

Key Difference: This endpoint deletes ALL payment methods but keeps the payor profile. To delete both the payor and all payment methods, use the DELETE /v1/rest/merchants/{merchantId}/payors/{payorId} endpoint instead.
Important: This operation will delete ALL saved payment methods (credit cards and bank accounts) associated with the payor. 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 whose payment methods will be deleted

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;
using System.Collections.Generic;
using Newtonsoft.Json;

public class SavedPaymentMethodResponse
{
    [JsonProperty("responseStatus")]
    public string ResponseStatus { get; set; }

    [JsonProperty("responseMessage")]
    public string ResponseMessage { get; set; }

    [JsonProperty("responseResult")]
    public string ResponseResult { get; set; }

    [JsonProperty("savedPaymentMethodId")]
    public string SavedPaymentMethodId { get; set; }

    [JsonProperty("isDeleted")]
    public bool IsDeleted { get; set; }

    [JsonProperty("deletedDate")]
    public DateTime? DeletedDate { get; set; }
}

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> DeleteAllSavedPaymentMethodsAsync(
        string merchantId,
        string payorId)
    {
        try
        {
            // Construct the endpoint URL
            string endpoint = $"/v1/rest/merchants/{merchantId}/payors/{payorId}" +
                            "/savedPaymentMethods";

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

            // Check for partial success (207 Multi-Status)
            if (response.StatusCode == System.Net.HttpStatusCode.MultiStatus)
            {
                string partialResponseBody = await response.Content.ReadAsStringAsync();
                Console.WriteLine("WARNING: Some payment methods failed to delete.");
                Console.WriteLine(partialResponseBody);
                return partialResponseBody;
            }

            // 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 Success, int DeletedCount, List<string> Errors)>
        DeleteAllWithDetailsAsync(string merchantId, string payorId)
    {
        try
        {
            // First, get the list of payment methods to know what we're deleting
            var getEndpoint = $"/v1/rest/merchants/{merchantId}/payors/{payorId}" +
                            "/savedPaymentMethods";
            var getResponse = await _httpClient.GetAsync(getEndpoint);
            var paymentMethodsJson = await getResponse.Content.ReadAsStringAsync();
            var paymentMethods = JsonConvert.DeserializeObject<List<SavedPaymentMethodResponse>>
                (paymentMethodsJson);

            int originalCount = paymentMethods?.Count ?? 0;
            Console.WriteLine($"Found {originalCount} payment method(s) to delete.");

            // Confirm with user
            Console.Write("Are you sure you want to delete ALL payment methods? (yes/no): ");
            string confirmation = Console.ReadLine();
            if (confirmation?.ToLower() != "yes")
            {
                Console.WriteLine("Delete operation cancelled.");
                return (false, 0, new List<string> { "Operation cancelled by user" });
            }

            // Proceed with deletion
            var deleteEndpoint = $"/v1/rest/merchants/{merchantId}/payors/{payorId}" +
                               "/savedPaymentMethods";
            var deleteResponse = await _httpClient.DeleteAsync(deleteEndpoint);

            var errors = new List<string>();

            if (deleteResponse.StatusCode == System.Net.HttpStatusCode.OK)
            {
                Console.WriteLine($"Successfully deleted all {originalCount} payment method(s).");
                return (true, originalCount, errors);
            }
            else if (deleteResponse.StatusCode == System.Net.HttpStatusCode.MultiStatus)
            {
                Console.WriteLine("Partial success - some payment methods failed to delete.");
                var responseBody = await deleteResponse.Content.ReadAsStringAsync();
                errors.Add(responseBody);
                return (false, 0, errors);
            }
            else
            {
                var errorBody = await deleteResponse.Content.ReadAsStringAsync();
                errors.Add($"HTTP {deleteResponse.StatusCode}: {errorBody}");
                return (false, 0, errors);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine($"Error: {e.Message}");
            return (false, 0, new List<string> { e.Message });
        }
    }
}

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

// Example usage: Delete with detailed feedback
var (success, deletedCount, errors) = await client.DeleteAllWithDetailsAsync(
    "12345678901",              // merchantId
    "5513027774438108364"       // payorId
);

if (success)
{
    Console.WriteLine($"Successfully deleted {deletedCount} payment method(s).");
}
else
{
    Console.WriteLine("Deletion failed or partially completed:");
    errors.ForEach(error => Console.WriteLine($"  - {error}"));
}
Imports System
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Threading.Tasks
Imports System.Collections.Generic
Imports Newtonsoft.Json

Public Class SavedPaymentMethodResponse
    <JsonProperty("responseStatus")>
    Public Property ResponseStatus As String

    <JsonProperty("responseMessage")>
    Public Property ResponseMessage As String

    <JsonProperty("responseResult")>
    Public Property ResponseResult As String

    <JsonProperty("savedPaymentMethodId")>
    Public Property SavedPaymentMethodId As String

    <JsonProperty("isDeleted")>
    Public Property IsDeleted As Boolean

    <JsonProperty("deletedDate")>
    Public Property DeletedDate As DateTime?
End Class

Public Class PayorClient
    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 DeleteAllSavedPaymentMethodsAsync( _
        merchantId As String, _
        payorId As String) As Task(Of String)

        Try
            Dim endpoint As String = $"/v1/rest/merchants/{merchantId}/payors/{payorId}" & _
                                   "/savedPaymentMethods"

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

            If response.StatusCode = Net.HttpStatusCode.MultiStatus Then
                Dim partialResponseBody As String = Await response.Content.ReadAsStringAsync()
                Console.WriteLine("WARNING: Some payment methods failed to delete.")
                Console.WriteLine(partialResponseBody)
                Return partialResponseBody
            End If

            response.EnsureSuccessStatusCode()

            Dim responseBody As String = Await response.Content.ReadAsStringAsync()
            Return responseBody
        Catch ex As HttpRequestException
            Console.WriteLine($"Request error: {ex.Message}")
            Throw
        End Try
    End Function

    Public Async Function DeleteAllWithDetailsAsync( _
        merchantId As String, _
        payorId As String) As Task(Of (Success As Boolean, DeletedCount As Integer, Errors As List(Of String)))

        Try
            Dim getEndpoint As String = $"/v1/rest/merchants/{merchantId}/payors/{payorId}" & _
                                      "/savedPaymentMethods"
            Dim getResponse As HttpResponseMessage = Await _httpClient.GetAsync(getEndpoint)
            Dim paymentMethodsJson As String = Await getResponse.Content.ReadAsStringAsync()
            Dim paymentMethods As List(Of SavedPaymentMethodResponse) = _
                JsonConvert.DeserializeObject(Of List(Of SavedPaymentMethodResponse))(paymentMethodsJson)

            Dim originalCount As Integer = If(paymentMethods IsNot Nothing, paymentMethods.Count, 0)
            Console.WriteLine($"Found {originalCount} payment method(s) to delete.")

            Console.Write("Are you sure you want to delete ALL payment methods? (yes/no): ")
            Dim confirmation As String = Console.ReadLine()
            If confirmation?.ToLower() <> "yes" Then
                Console.WriteLine("Delete operation cancelled.")
                Return (False, 0, New List(Of String) From {"Operation cancelled by user"})
            End If

            Dim deleteEndpoint As String = $"/v1/rest/merchants/{merchantId}/payors/{payorId}" & _
                                         "/savedPaymentMethods"
            Dim deleteResponse As HttpResponseMessage = Await _httpClient.DeleteAsync(deleteEndpoint)

            Dim errors As New List(Of String)

            If deleteResponse.StatusCode = Net.HttpStatusCode.OK Then
                Console.WriteLine($"Successfully deleted all {originalCount} payment method(s).")
                Return (True, originalCount, errors)
            ElseIf deleteResponse.StatusCode = Net.HttpStatusCode.MultiStatus Then
                Console.WriteLine("Partial success - some payment methods failed to delete.")
                Dim responseBody As String = Await deleteResponse.Content.ReadAsStringAsync()
                errors.Add(responseBody)
                Return (False, 0, errors)
            Else
                Dim errorBody As String = Await deleteResponse.Content.ReadAsStringAsync()
                errors.Add($"HTTP {deleteResponse.StatusCode}: {errorBody}")
                Return (False, 0, errors)
            End If
        Catch ex As Exception
            Console.WriteLine($"Error: {ex.Message}")
            Return (False, 0, New List(Of String) From {ex.Message})
        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.DeleteAllSavedPaymentMethodsAsync( _
    "12345678901", _
    "5513027774438108364")
Console.WriteLine(result)

' Example usage: Delete with detailed feedback
Dim deleteResult = Await client.DeleteAllWithDetailsAsync( _
    "12345678901", _
    "5513027774438108364")

If deleteResult.Success Then
    Console.WriteLine($"Successfully deleted {deleteResult.DeletedCount} payment method(s).")
Else
    Console.WriteLine("Deletion failed or partially completed:")
    For Each errorMsg In deleteResult.Errors
        Console.WriteLine($"  - {errorMsg}")
    Next
End If
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;

public class SavedPaymentMethodResponse {
    @JsonProperty("responseStatus")
    private String responseStatus;

    @JsonProperty("responseMessage")
    private String responseMessage;

    @JsonProperty("responseResult")
    private String responseResult;

    @JsonProperty("savedPaymentMethodId")
    private String savedPaymentMethodId;

    @JsonProperty("isDeleted")
    private boolean isDeleted;

    @JsonProperty("deletedDate")
    private String deletedDate;

    // Getters and setters omitted for brevity
}

public class PayorClient {
    private final HttpClient httpClient;
    private final String baseUrl = "https://your-api-domain.com";
    private final String bearerToken;
    private final ObjectMapper objectMapper;

    public PayorClient(String bearerToken) {
        this.bearerToken = bearerToken;
        this.httpClient = HttpClient.newHttpClient();
        this.objectMapper = new ObjectMapper();
    }

    public String deleteAllSavedPaymentMethods(String merchantId, String payorId)
            throws IOException, InterruptedException {
        try {
            String endpoint = String.format("/v1/rest/merchants/%s/payors/%s/savedPaymentMethods",
                                          merchantId, payorId);

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

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

            if (response.statusCode() == 207) {
                System.out.println("WARNING: Some payment methods failed to delete.");
                System.out.println(response.body());
                return response.body();
            }

            if (response.statusCode() < 200 || response.statusCode() >= 300) {
                throw new IOException("HTTP " + response.statusCode() + ": " + response.body());
            }

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

    public DeleteResult deleteAllWithDetails(String merchantId, String payorId)
            throws IOException, InterruptedException {
        try {
            // First, get the list of payment methods
            String getEndpoint = String.format("/v1/rest/merchants/%s/payors/%s/savedPaymentMethods",
                                             merchantId, payorId);

            HttpRequest getRequest = HttpRequest.newBuilder()
                .uri(URI.create(baseUrl + getEndpoint))
                .header("Authorization", "Bearer " + bearerToken)
                .header("Accept", "application/json")
                .GET()
                .build();

            HttpResponse<String> getResponse = httpClient.send(getRequest,
                HttpResponse.BodyHandlers.ofString());

            SavedPaymentMethodResponse[] paymentMethods = objectMapper.readValue(
                getResponse.body(), SavedPaymentMethodResponse[].class);

            int originalCount = paymentMethods != null ? paymentMethods.length : 0;
            System.out.println("Found " + originalCount + " payment method(s) to delete.");

            // Confirm with user
            Scanner scanner = new Scanner(System.in);
            System.out.print("Are you sure you want to delete ALL payment methods? (yes/no): ");
            String confirmation = scanner.nextLine();
            if (!"yes".equalsIgnoreCase(confirmation)) {
                System.out.println("Delete operation cancelled.");
                List<String> errors = new ArrayList<>();
                errors.add("Operation cancelled by user");
                return new DeleteResult(false, 0, errors);
            }

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

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

            HttpResponse<String> deleteResponse = httpClient.send(deleteRequest,
                HttpResponse.BodyHandlers.ofString());

            List<String> errors = new ArrayList<>();

            if (deleteResponse.statusCode() == 200) {
                System.out.println("Successfully deleted all " + originalCount + " payment method(s).");
                return new DeleteResult(true, originalCount, errors);
            } else if (deleteResponse.statusCode() == 207) {
                System.out.println("Partial success - some payment methods failed to delete.");
                errors.add(deleteResponse.body());
                return new DeleteResult(false, 0, errors);
            } else {
                errors.add("HTTP " + deleteResponse.statusCode() + ": " + deleteResponse.body());
                return new DeleteResult(false, 0, errors);
            }
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            List<String> errors = new ArrayList<>();
            errors.add(e.getMessage());
            return new DeleteResult(false, 0, errors);
        }
    }

    public static class DeleteResult {
        public final boolean success;
        public final int deletedCount;
        public final List<String> errors;

        public DeleteResult(boolean success, int deletedCount, List<String> errors) {
            this.success = success;
            this.deletedCount = deletedCount;
            this.errors = errors;
        }
    }
}

// Example usage: Simple delete
PayorClient client = new PayorClient("your-bearer-token-here");
String result = client.deleteAllSavedPaymentMethods(
    "12345678901",
    "5513027774438108364"
);
System.out.println(result);

// Example usage: Delete with detailed feedback
PayorClient.DeleteResult deleteResult = client.deleteAllWithDetails(
    "12345678901",
    "5513027774438108364"
);

if (deleteResult.success) {
    System.out.println("Successfully deleted " + deleteResult.deletedCount + " payment method(s).");
} else {
    System.out.println("Deletion failed or partially completed:");
    for (String error : deleteResult.errors) {
        System.out.println("  - " + error);
    }
}
require 'net/http'
require 'uri'
require 'json'
require 'io/console'

class SavedPaymentMethodResponse
  attr_accessor :response_status, :response_message, :response_result,
                :saved_payment_method_id, :is_deleted, :deleted_date

  def initialize(data)
    @response_status = data['responseStatus']
    @response_message = data['responseMessage']
    @response_result = data['responseResult']
    @saved_payment_method_id = data['savedPaymentMethodId']
    @is_deleted = data['isDeleted']
    @deleted_date = data['deletedDate']
  end
end

class PayorClient
  def initialize(bearer_token)
    @bearer_token = bearer_token
    @base_url = 'https://your-api-domain.com'
  end

  def delete_all_saved_payment_methods(merchant_id, payor_id)
    endpoint = "/v1/rest/merchants/#{merchant_id}/payors/#{payor_id}" +
               '/savedPaymentMethods'
    uri = URI("#{@base_url}#{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: true) do |http|
      http.request(request)
    end

    if response.code == '207'
      puts 'WARNING: Some payment methods failed to delete.'
      puts response.body
      return response.body
    end

    unless response.is_a?(Net::HTTPSuccess)
      raise "HTTP #{response.code}: #{response.body}"
    end

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

  def delete_all_with_details(merchant_id, payor_id)
    # First, get the list of payment methods
    get_endpoint = "/v1/rest/merchants/#{merchant_id}/payors/#{payor_id}" +
                   '/savedPaymentMethods'
    get_uri = URI("#{@base_url}#{get_endpoint}")

    get_request = Net::HTTP::Get.new(get_uri)
    get_request['Authorization'] = "Bearer #{@bearer_token}"
    get_request['Accept'] = 'application/json'

    get_response = Net::HTTP.start(get_uri.hostname, get_uri.port, use_ssl: true) do |http|
      http.request(get_request)
    end

    payment_methods = JSON.parse(get_response.body).map do |pm|
      SavedPaymentMethodResponse.new(pm)
    end

    original_count = payment_methods.length
    puts "Found #{original_count} payment method(s) to delete."

    # Confirm with user
    print 'Are you sure you want to delete ALL payment methods? (yes/no): '
    confirmation = gets.chomp
    unless confirmation.downcase == 'yes'
      puts 'Delete operation cancelled.'
      return { success: false, deleted_count: 0, errors: ['Operation cancelled by user'] }
    end

    # Proceed with deletion
    delete_endpoint = "/v1/rest/merchants/#{merchant_id}/payors/#{payor_id}" +
                     '/savedPaymentMethods'
    delete_uri = URI("#{@base_url}#{delete_endpoint}")

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

    delete_response = Net::HTTP.start(delete_uri.hostname, delete_uri.port, use_ssl: true) do |http|
      http.request(delete_request)
    end

    errors = []

    case delete_response.code
    when '200'
      puts "Successfully deleted all #{original_count} payment method(s)."
      { success: true, deleted_count: original_count, errors: errors }
    when '207'
      puts 'Partial success - some payment methods failed to delete.'
      errors << delete_response.body
      { success: false, deleted_count: 0, errors: errors }
    else
      errors << "HTTP #{delete_response.code}: #{delete_response.body}"
      { success: false, deleted_count: 0, errors: errors }
    end
  rescue StandardError => e
    puts "Error: #{e.message}"
    { success: false, deleted_count: 0, errors: [e.message] }
  end
end

# Example usage: Simple delete
client = PayorClient.new('your-bearer-token-here')
result = client.delete_all_saved_payment_methods(
  '12345678901',
  '5513027774438108364'
)
puts result

# Example usage: Delete with detailed feedback
delete_result = client.delete_all_with_details(
  '12345678901',
  '5513027774438108364'
)

if delete_result[:success]
  puts "Successfully deleted #{delete_result[:deleted_count]} payment method(s)."
else
  puts 'Deletion failed or partially completed:'
  delete_result[:errors].each do |error|
    puts "  - #{error}"
  end
end

Response Format

The response returns details about the deleted payment methods. The exact format follows the SavedPaymentMethodResponse schema.

Success Response (HTTP 200)

{
  "responseStatus": "SavedPaymentMethodDeleted",
  "responseMessage": "All saved payment methods deleted successfully",
  "responseCode": "200",
  "responseReason": null,
  "responseResult": "success",
  "legacyResponseCode": "A",
  "correlationId": "delete-all-123-456",
  "payorId": "5513027774438108364",
  "savedPaymentMethodId": null,
  "deletedDate": "2026-04-02T15:30:00Z"
}

Partial Success Response (HTTP 207)

When some payment methods fail to delete, you'll receive a 207 Multi-Status response:

{
  "responseStatus": "PartialSuccess",
  "responseMessage": "Some payment methods failed to delete",
  "responseCode": "207",
  "responseReason": "2 of 5 payment methods were deleted successfully",
  "responseResult": "retry",
  "correlationId": "partial-207-789-012",
  "payorId": "5513027774438108364",
  "deletedCount": 2,
  "failedCount": 3,
  "errors": [
    {
      "savedPaymentMethodId": "5513027774438108366",
      "error": "Payment method is locked due to pending transaction"
    },
    {
      "savedPaymentMethodId": "5513027774438108367",
      "error": "Payment method is associated with active recurring payment"
    },
    {
      "savedPaymentMethodId": "5513027774438108368",
      "error": "Unable to process deletion request"
    }
  ]
}

HTTP Status Codes

Status Code Description
200 Success - All saved payment methods deleted successfully
207 Multi-Status - Some payment methods were deleted successfully, but others failed
400 Bad Request - Invalid merchantId or payorId format
404 Not Found - Payor with the specified ID does not exist for this merchant
500 Internal Server Error - Server encountered an unexpected error

Error Responses

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-123-456"
}

Bad Request Error (HTTP 400)

{
  "responseStatus": "BadRequest",
  "responseMessage": "Invalid request parameters",
  "responseCode": "400",
  "responseReason": "Merchant ID must be exactly 11 digits",
  "responseResult": "failure",
  "correlationId": "error-400-789-012"
}

What Gets Deleted

Deletion Scope:

When you delete all saved payment methods for a payor:

  • All Credit Cards: All saved credit card payment methods are deleted
  • All ACH Accounts: All saved checking and savings accounts are deleted
  • Default Payment Method: The default payment method designation is cleared
  • BIN Information: Associated BIN data is removed
  • Custom Metadata: Any custom data associated with the payment methods is deleted

What is NOT deleted:

  • Payor Profile: The payor record itself remains active and unchanged
  • Transaction History: Past transactions using these payment methods are preserved
  • Payor Metadata: Custom data on the payor profile is retained

After Deletion

After successfully deleting all payment methods:

  • The payor profile remains active with isDeleted: false
  • The savedPaymentMethodIds array will be empty
  • The defaultSavedPaymentMethodId will be null
  • The payor can still be used to create new payment methods
  • The payor cannot process transactions until new payment methods are added

Understanding Multi-Status (207) Responses

Partial Success Handling:

A 207 Multi-Status response indicates that the deletion operation was only partially successful. This can happen when:

  • Some payment methods are locked due to pending transactions
  • Some payment methods are associated with active recurring payments
  • Network issues occur during the deletion of some payment methods
  • Some payment methods have downstream dependencies

Recommended Action: Review the error details in the response, resolve the issues (e.g., wait for pending transactions to complete), and retry the deletion.

Best Practices

1. Always Confirm Before Deleting

Show the user how many payment methods will be deleted and require explicit confirmation. Consider displaying the masked account numbers so users know what they're deleting.

// Example confirmation with details
var paymentMethods = await GetSavedPaymentMethodsAsync(merchantId, payorId);

Console.WriteLine("You are about to delete the following payment methods:");
foreach (var pm in paymentMethods)
{
    Console.WriteLine($"  - {pm.AccountType} ending in {pm.LastFour}");
}
Console.WriteLine($"\nTotal: {paymentMethods.Count} payment method(s)");
Console.Write("\nType 'DELETE ALL' to confirm: ");

if (Console.ReadLine() == "DELETE ALL")
{
    await DeleteAllSavedPaymentMethodsAsync(merchantId, payorId);
}

2. Check for Active Subscriptions First

Before deleting all payment methods, verify there are no active recurring payments or scheduled transactions that depend on these payment methods.

// Check for active subscriptions before deletion
var activeSubscriptions = await GetActiveSubscriptionsAsync(payorId);
if (activeSubscriptions.Any())
{
    Console.WriteLine("ERROR: Cannot delete payment methods.");
    Console.WriteLine($"This payor has {activeSubscriptions.Count} active subscription(s).");
    Console.WriteLine("Cancel or reassign subscriptions before deleting payment methods.");
    return;
}

// Safe to proceed with deletion
await DeleteAllSavedPaymentMethodsAsync(merchantId, payorId);

3. Handle 207 Multi-Status Properly

Don't treat 207 as a complete failure. Some payment methods may have been successfully deleted. Parse the response to determine which ones failed and why.

if (response.StatusCode == HttpStatusCode.MultiStatus)
{
    var result = JsonConvert.DeserializeObject<PartialDeleteResponse>(responseBody);

    Console.WriteLine($"Deleted: {result.DeletedCount} payment method(s)");
    Console.WriteLine($"Failed: {result.FailedCount} payment method(s)");

    foreach (var error in result.Errors)
    {
        Console.WriteLine($"  - {error.SavedPaymentMethodId}: {error.Error}");
    }

    // Allow user to retry failed deletions
    Console.Write("\nRetry failed deletions? (yes/no): ");
    if (Console.ReadLine()?.ToLower() == "yes")
    {
        await RetryFailedDeletionsAsync(merchantId, payorId, result.Errors);
    }
}

4. Provide Alternative: Delete Individual Payment Methods

Instead of deleting all payment methods, consider offering the option to delete them individually. This gives users more control and reduces the risk of accidentally deleting payment methods they want to keep.

5. Log All Deletion Operations

Maintain an audit trail of all bulk payment method deletions:

await AuditLog.LogAsync(new AuditEntry
{
    Action = "DeleteAllSavedPaymentMethods",
    UserId = currentUserId,
    MerchantId = merchantId,
    PayorId = payorId,
    PaymentMethodCount = paymentMethods.Count,
    Timestamp = DateTime.UtcNow,
    CorrelationId = response.CorrelationId
});

6. Update UI and Cache

After successful deletion, ensure your UI reflects the changes:

  • Clear any cached payment method data
  • Update the payor's payment method list to show empty state
  • Disable any "Pay Now" buttons that require a payment method
  • Show a prompt to add a new payment method

Common Use Cases

1. Compromised Account Security

When a customer reports their account may be compromised:

// Security incident response - remove all payment methods
await DeleteAllSavedPaymentMethodsAsync(merchantId, payorId);
await SendSecurityAlertEmail(customerEmail);
await LogSecurityIncident(payorId, "Payment methods removed due to security concern");

// Require re-authentication before adding new payment methods
await SetPayorRequireReauth(payorId, true);

2. Customer Requested Payment Method Reset

When a customer wants to start fresh with new payment methods:

// Clean slate - remove all old payment methods
Console.WriteLine("Removing all existing payment methods...");
await DeleteAllSavedPaymentMethodsAsync(merchantId, payorId);

Console.WriteLine("Payment methods removed. Ready to add new payment methods.");
// Redirect user to add payment method flow

3. Expired or Invalid Payment Methods Cleanup

When all payment methods are expired or invalid:

// Check if all payment methods are expired
var paymentMethods = await GetSavedPaymentMethodsAsync(merchantId, payorId);
var allExpired = paymentMethods.All(pm => IsExpired(pm));

if (allExpired)
{
    Console.WriteLine("All payment methods are expired. Removing all...");
    await DeleteAllSavedPaymentMethodsAsync(merchantId, payorId);
    await NotifyCustomerToAddNewPaymentMethod(customerEmail);
}

4. Merchant Account Type Change

When switching between different merchant processors that require different tokenization:

// Migrate to new payment processor
Console.WriteLine("Preparing to migrate to new payment processor...");

// Export transaction history first
await ExportTransactionHistory(merchantId, payorId);

// Remove all payment methods (old tokens won't work with new processor)
await DeleteAllSavedPaymentMethodsAsync(merchantId, payorId);

// Update merchant processor configuration
await UpdateMerchantProcessor(merchantId, newProcessorId);

Console.WriteLine("Migration complete. Customers need to re-add payment methods.");

5. Batch Cleanup During Testing

When cleaning up test data:

// Batch cleanup of test payor payment methods
var testPayors = await GetTestPayorsAsync(merchantId);

Console.WriteLine($"Cleaning up {testPayors.Count} test payor(s)...");
int successCount = 0;
int failureCount = 0;

foreach (var payorId in testPayors)
{
    try
    {
        await DeleteAllSavedPaymentMethodsAsync(merchantId, payorId);
        successCount++;
        Console.WriteLine($"✓ Cleaned payor {payorId}");
    }
    catch (Exception ex)
    {
        failureCount++;
        Console.WriteLine($"✗ Failed to clean payor {payorId}: {ex.Message}");
    }
}

Console.WriteLine($"\nCleanup complete: {successCount} success, {failureCount} failed");

Comparison with Related Endpoints

Endpoint What Gets Deleted Payor Retained?
DELETE /merchants/{merchantId}/payors/{payorId} Payor + ALL payment methods No (soft-deleted)
DELETE /merchants/{merchantId}/payors/{payorId}/savedPaymentMethods ALL payment methods only Yes
DELETE /merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{id} Single payment method Yes

Troubleshooting

Issue: Getting 207 Multi-Status response
  • Some payment methods may be locked due to pending transactions - wait for transactions to complete
  • Some payment methods may be associated with active recurring payments - cancel or reassign subscriptions
  • Review the error details in the response to identify which payment methods failed and why
  • Retry the operation after resolving the issues, or delete failed payment methods individually
Issue: Getting 404 Not Found error
  • Verify the payorId exists and belongs to the specified merchant
  • Check that the merchantId is correct (must be exactly 11 digits)
  • The payor may have been deleted - check the payor's isDeleted status
  • Verify you're using the correct environment (production vs. sandbox)
Issue: Deletion succeeds but payor still shows payment methods
  • Clear your application's cache - payment method data may be cached
  • Verify the deletion was successful by making a fresh GET request
  • Check if you're looking at the correct payor (verify payorId)
  • Some UIs may display historical payment methods - ensure you're filtering by isDeleted status
Issue: Unable to delete payment methods with pending transactions
  • Wait for pending transactions to settle or complete
  • Check transaction status using the transactions endpoint
  • Contact support if transactions remain pending for an extended period
  • Consider canceling pending transactions first (if appropriate)

Recovery from Accidental Deletion

Important: Unlike payor deletion (which is a soft delete), payment method deletion may be permanent. If you accidentally delete all payment methods:
  • Contact Procare Pay support immediately
  • Provide the correlationId from the delete response
  • Provide the merchantId and payorId
  • Recovery is not guaranteed - payment methods may need to be re-added manually
  • The customer will need to provide their payment information again

Security Considerations

  • Authorization: Ensure the user has permission to delete payment methods for this payor
  • Confirmation Required: Always require explicit user confirmation before bulk deletion
  • Audit Logging: Log who deleted the payment methods, when, and why
  • Rate Limiting: Monitor for unusual bulk deletion patterns that might indicate abuse
  • Customer Notification: Consider notifying the account holder when all payment methods are removed
  • Session Validation: Require recent authentication before allowing bulk deletions
  • Dependent Resource Check: Verify no active subscriptions or scheduled payments depend on these payment methods

Additional Notes

  • The merchantId must be exactly 11 digits. Requests with invalid formats will return a 400 Bad Request error.
  • This operation deletes ALL payment methods - there is no way to selectively exclude certain payment methods using this endpoint.
  • To delete individual payment methods, use the DELETE /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{savedPaymentMethodId} endpoint.
  • The payor profile remains active after deletion - the payor can still be used to create new payment methods.
  • Transaction history is preserved even after payment method deletion.
  • The correlationId in the response should be logged for troubleshooting purposes.
  • After deletion, the payor cannot process transactions until new payment methods are added.
  • A 207 Multi-Status response is not a complete failure - some payment methods may have been deleted successfully.
  • This endpoint is particularly useful for security incidents where all payment data needs to be cleared immediately.

Related Endpoints

  • GET /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods - List all payment methods (to see what will be deleted)
  • DELETE /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{savedPaymentMethodId} - Delete a single payment method
  • DELETE /v1/rest/merchants/{merchantId}/payors/{payorId} - Delete payor and all payment methods together
  • POST /v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods - Add new payment methods after deletion