Delete Saved Payment Method
Description: Deletes (soft deletes) a single saved payment method from a payor's profile. The payment method is marked as deleted but remains in the system for transaction history and reporting purposes. This operation cannot be undone.
This endpoint removes a specific payment method while keeping others active. Common scenarios include:
- Remove Old Card: Customer's card was replaced and they no longer need the old expired card in their profile
- Consolidate Payment Methods: Customer has too many saved cards and wants to remove unused ones
- Security Concern: Customer suspects a card was compromised and wants it immediately removed
- Account Closed: Bank account or credit card account was closed and should no longer be available
- Switch Payment Types: Customer switching from ACH to credit card (or vice versa) and removing the old method
- Compliance: Remove payment methods that haven't been used in a certain period per your retention policy
- Soft Delete: This is a soft delete operation. The payment method is marked as deleted (isDeleted: true) but remains in the database for audit and reporting purposes
- Cannot Delete Last Method: You cannot delete a payor's only remaining payment method. The payor must have at least one active payment method
- Default Payment Handling: If you delete the default payment method, another active payment method will be automatically promoted to default
- No Undo: This operation cannot be undone. The payment method cannot be restored - you must create a new one if needed
To delete ALL payment methods for a payor at once, use DELETE /merchants/{merchantId}/payors/{payorId}/savedPaymentMethods (without the savedPaymentMethodId). To delete the payor profile entirely including all payment methods, use DELETE /merchants/{merchantId}/payors/{payorId}.
Path Parameters
Request Body
This endpoint does not require a request body. All necessary information is provided in the URL path parameters.
Response
Returns HTTP 200 (OK) with a SavedPaymentMethodResponse object containing the deleted payment method details:
| Field | Type | Description |
|---|---|---|
| payorId | string | The payor identifier |
| savedPaymentMethodId | string | The identifier of the deleted payment method |
| maskedAccountNumber | string | Masked version of the account number (e.g., "************1234") |
| accountType | string | The card brand or account type (e.g., "VISA", "MC", "ECHK", "SAV") |
| isDefault | boolean | Will be false after deletion (if it was the default, another method was promoted) |
| isDeleted | boolean | Will be true, indicating the payment method is now deleted |
| sponsorKey | string | The sponsor key if one was associated with this payment method |
Error Responses
| Status Code | Meaning | Common Causes |
|---|---|---|
| 400 | Bad Request |
|
| 404 | Not Found |
|
| 500 | Internal Server Error | Server-side error. Check error message for details and retry if appropriate |
Code Examples
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
public class PaymentMethodClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
private readonly string _bearerToken;
public PaymentMethodClient(string baseUrl, string bearerToken)
{
_baseUrl = baseUrl;
_bearerToken = bearerToken;
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {bearerToken}");
}
// Delete a single saved payment method
public async Task DeleteSavedPaymentMethodAsync(
string merchantId,
string payorId,
string savedPaymentMethodId)
{
string endpoint = $"{_baseUrl}/v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{savedPaymentMethodId}";
HttpResponseMessage response = await _httpClient.DeleteAsync(endpoint);
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
{
throw new Exception("Payment method not found. It may have already been deleted or does not exist.");
}
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize(json);
}
// Helper: Get all active payment methods before deleting
public async Task> GetActiveSavedPaymentMethodsAsync(
string merchantId,
string payorId)
{
string endpoint = $"{_baseUrl}/v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods";
HttpResponseMessage response = await _httpClient.GetAsync(endpoint);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
List allMethods = JsonSerializer.Deserialize>(json);
// Filter to only active (non-deleted) methods
return allMethods.Where(m => !m.IsDeleted).ToList();
}
// Safe delete: Verify not the last payment method before deleting
public async Task SafeDeleteSavedPaymentMethodAsync(
string merchantId,
string payorId,
string savedPaymentMethodId)
{
// First, get all active payment methods
List activeMethods = await GetActiveSavedPaymentMethodsAsync(merchantId, payorId);
if (activeMethods.Count <= 1)
{
throw new InvalidOperationException(
"Cannot delete the last remaining payment method. The payor must have at least one active payment method.");
}
// Safe to delete
return await DeleteSavedPaymentMethodAsync(merchantId, payorId, savedPaymentMethodId);
}
// Delete and optionally set a new default
public async Task DeleteAndSetNewDefaultAsync(
string merchantId,
string payorId,
string savedPaymentMethodIdToDelete,
string newDefaultPaymentMethodId)
{
// First, delete the old payment method
SavedPaymentMethodResponse deleted = await DeleteSavedPaymentMethodAsync(
merchantId, payorId, savedPaymentMethodIdToDelete);
// If the deleted method was the default, set a new default
if (deleted.IsDefault)
{
// Update the new default method
var updateRequest = new { setAsDefaultPaymentMethod = true };
string endpoint = $"{_baseUrl}/v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{newDefaultPaymentMethodId}";
string json = JsonSerializer.Serialize(updateRequest);
HttpContent content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.PutAsync(endpoint, content);
response.EnsureSuccessStatusCode();
}
return deleted;
}
}
// Usage example
public class Program
{
public static async Task Main()
{
string baseUrl = "https://api.example.com";
string bearerToken = "your-oauth-token-here";
string merchantId = "12345678901";
string payorId = "550e8400-e29b-41d4-a716-446655440000";
string paymentMethodToDelete = "7c9e6679-7425-40de-944b-e07fc1f90ae7";
PaymentMethodClient client = new PaymentMethodClient(baseUrl, bearerToken);
try
{
// Safe delete with validation
SavedPaymentMethodResponse result = await client.SafeDeleteSavedPaymentMethodAsync(
merchantId, payorId, paymentMethodToDelete);
Console.WriteLine($"Payment method deleted: {result.MaskedAccountNumber}");
Console.WriteLine($"Account type: {result.AccountType}");
Console.WriteLine($"Is deleted: {result.IsDeleted}");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Cannot delete: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
public class SavedPaymentMethodResponse
{
public string PayorId { get; set; }
public string SavedPaymentMethodId { get; set; }
public string MaskedAccountNumber { get; set; }
public string AccountType { get; set; }
public bool IsDefault { get; set; }
public bool IsDeleted { get; set; }
public string SponsorKey { get; set; }
}
Imports System.Net.Http
Imports System.Text
Imports System.Text.Json
Imports System.Linq
Public Class PaymentMethodClient
Private ReadOnly _httpClient As HttpClient
Private ReadOnly _baseUrl As String
Private ReadOnly _bearerToken As String
Public Sub New(baseUrl As String, bearerToken As String)
_baseUrl = baseUrl
_bearerToken = bearerToken
_httpClient = New HttpClient()
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {bearerToken}")
End Sub
' Delete a single saved payment method
Public Async Function DeleteSavedPaymentMethodAsync(
merchantId As String,
payorId As String,
savedPaymentMethodId As String) As Task(Of SavedPaymentMethodResponse)
Dim endpoint As String = $"{_baseUrl}/v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods/{savedPaymentMethodId}"
Dim response As HttpResponseMessage = Await _httpClient.DeleteAsync(endpoint)
If response.StatusCode = Net.HttpStatusCode.NotFound Then
Throw New Exception("Payment method not found. It may have already been deleted or does not exist.")
End If
response.EnsureSuccessStatusCode()
Dim json As String = Await response.Content.ReadAsStringAsync()
Return JsonSerializer.Deserialize(Of SavedPaymentMethodResponse)(json)
End Function
' Helper: Get all active payment methods before deleting
Public Async Function GetActiveSavedPaymentMethodsAsync(
merchantId As String,
payorId As String) As Task(Of List(Of SavedPaymentMethodResponse))
Dim endpoint As String = $"{_baseUrl}/v1/rest/merchants/{merchantId}/payors/{payorId}/savedPaymentMethods"
Dim response As HttpResponseMessage = Await _httpClient.GetAsync(endpoint)
response.EnsureSuccessStatusCode()
Dim json As String = Await response.Content.ReadAsStringAsync()
Dim allMethods As List(Of SavedPaymentMethodResponse) = JsonSerializer.Deserialize(Of List(Of SavedPaymentMethodResponse))(json)
' Filter to only active (non-deleted) methods
Return allMethods.Where(Function(m) Not m.IsDeleted).ToList()
End Function
' Safe delete: Verify not the last payment method before deleting
Public Async Function SafeDeleteSavedPaymentMethodAsync(
merchantId As String,
payorId As String,
savedPaymentMethodId As String) As Task(Of SavedPaymentMethodResponse)
' First, get all active payment methods
Dim activeMethods As List(Of SavedPaymentMethodResponse) = Await GetActiveSavedPaymentMethodsAsync(merchantId, payorId)
If activeMethods.Count <= 1 Then
Throw New InvalidOperationException(
"Cannot delete the last remaining payment method. The payor must have at least one active payment method.")
End If
' Safe to delete
Return Await DeleteSavedPaymentMethodAsync(merchantId, payorId, savedPaymentMethodId)
End Function
End Class
' Usage example
Module Program
Sub Main()
MainAsync().Wait()
End Sub
Async Function MainAsync() As Task
Dim baseUrl As String = "https://api.example.com"
Dim bearerToken As String = "your-oauth-token-here"
Dim merchantId As String = "12345678901"
Dim payorId As String = "550e8400-e29b-41d4-a716-446655440000"
Dim paymentMethodToDelete As String = "7c9e6679-7425-40de-944b-e07fc1f90ae7"
Dim client As New PaymentMethodClient(baseUrl, bearerToken)
Try
' Safe delete with validation
Dim result As SavedPaymentMethodResponse = Await client.SafeDeleteSavedPaymentMethodAsync(
merchantId, payorId, paymentMethodToDelete)
Console.WriteLine($"Payment method deleted: {result.MaskedAccountNumber}")
Console.WriteLine($"Account type: {result.AccountType}")
Console.WriteLine($"Is deleted: {result.IsDeleted}")
Catch ex As InvalidOperationException
Console.WriteLine($"Cannot delete: {ex.Message}")
Catch ex As Exception
Console.WriteLine($"Error: {ex.Message}")
End Try
End Function
End Module
Public Class SavedPaymentMethodResponse
Public Property PayorId As String
Public Property SavedPaymentMethodId As String
Public Property MaskedAccountNumber As String
Public Property AccountType As String
Public Property IsDefault As Boolean
Public Property IsDeleted As Boolean
Public Property SponsorKey As String
End Class
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.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
public class PaymentMethodClient {
private final HttpClient httpClient;
private final String baseUrl;
private final String bearerToken;
private final ObjectMapper objectMapper;
public PaymentMethodClient(String baseUrl, String bearerToken) {
this.baseUrl = baseUrl;
this.bearerToken = bearerToken;
this.httpClient = HttpClient.newHttpClient();
this.objectMapper = new ObjectMapper();
}
// Delete a single saved payment method
public SavedPaymentMethodResponse deleteSavedPaymentMethod(
String merchantId,
String payorId,
String savedPaymentMethodId) throws IOException, InterruptedException {
String endpoint = String.format("%s/v1/rest/merchants/%s/payors/%s/savedPaymentMethods/%s",
baseUrl, merchantId, payorId, savedPaymentMethodId);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(endpoint))
.header("Authorization", "Bearer " + bearerToken)
.DELETE()
.build();
HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 404) {
throw new RuntimeException("Payment method not found. It may have already been deleted or does not exist.");
}
if (response.statusCode() != 200) {
throw new RuntimeException("Failed to delete payment method: " + response.statusCode() +
" - " + response.body());
}
return objectMapper.readValue(response.body(), SavedPaymentMethodResponse.class);
}
// Helper: Get all active payment methods before deleting
public List getActiveSavedPaymentMethods(
String merchantId,
String payorId) throws IOException, InterruptedException {
String endpoint = String.format("%s/v1/rest/merchants/%s/payors/%s/savedPaymentMethods",
baseUrl, merchantId, payorId);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(endpoint))
.header("Authorization", "Bearer " + bearerToken)
.GET()
.build();
HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("Failed to get payment methods: " + response.statusCode());
}
List allMethods = objectMapper.readValue(
response.body(),
new TypeReference>() {}
);
// Filter to only active (non-deleted) methods
return allMethods.stream()
.filter(m -> !m.isDeleted())
.collect(Collectors.toList());
}
// Safe delete: Verify not the last payment method before deleting
public SavedPaymentMethodResponse safeDeleteSavedPaymentMethod(
String merchantId,
String payorId,
String savedPaymentMethodId) throws IOException, InterruptedException {
// First, get all active payment methods
List activeMethods = getActiveSavedPaymentMethods(merchantId, payorId);
if (activeMethods.size() <= 1) {
throw new IllegalStateException(
"Cannot delete the last remaining payment method. The payor must have at least one active payment method.");
}
// Safe to delete
return deleteSavedPaymentMethod(merchantId, payorId, savedPaymentMethodId);
}
// Usage example
public static void main(String[] args) {
try {
String baseUrl = "https://api.example.com";
String bearerToken = "your-oauth-token-here";
String merchantId = "12345678901";
String payorId = "550e8400-e29b-41d4-a716-446655440000";
String paymentMethodToDelete = "7c9e6679-7425-40de-944b-e07fc1f90ae7";
PaymentMethodClient client = new PaymentMethodClient(baseUrl, bearerToken);
// Safe delete with validation
SavedPaymentMethodResponse result = client.safeDeleteSavedPaymentMethod(
merchantId, payorId, paymentMethodToDelete);
System.out.println("Payment method deleted: " + result.getMaskedAccountNumber());
System.out.println("Account type: " + result.getAccountType());
System.out.println("Is deleted: " + result.isDeleted());
} catch (IllegalStateException ex) {
System.out.println("Cannot delete: " + ex.getMessage());
} catch (Exception ex) {
System.out.println("Error: " + ex.getMessage());
ex.printStackTrace();
}
}
}
// Response model
class SavedPaymentMethodResponse {
@JsonProperty("payorId")
private String payorId;
@JsonProperty("savedPaymentMethodId")
private String savedPaymentMethodId;
@JsonProperty("maskedAccountNumber")
private String maskedAccountNumber;
@JsonProperty("accountType")
private String accountType;
@JsonProperty("isDefault")
private boolean isDefault;
@JsonProperty("isDeleted")
private boolean isDeleted;
@JsonProperty("sponsorKey")
private String sponsorKey;
public String getPayorId() { return payorId; }
public String getSavedPaymentMethodId() { return savedPaymentMethodId; }
public String getMaskedAccountNumber() { return maskedAccountNumber; }
public String getAccountType() { return accountType; }
public boolean isDefault() { return isDefault; }
public boolean isDeleted() { return isDeleted; }
public String getSponsorKey() { return sponsorKey; }
}
require 'net/http'
require 'json'
class PaymentMethodClient
def initialize(base_url, bearer_token)
@base_url = base_url
@bearer_token = bearer_token
end
# Delete a single saved payment method
def delete_saved_payment_method(merchant_id:, payor_id:, saved_payment_method_id:)
uri = URI("#{@base_url}/v1/rest/merchants/#{merchant_id}/payors/#{payor_id}/savedPaymentMethods/#{saved_payment_method_id}")
request = Net::HTTP::Delete.new(uri)
request['Authorization'] = "Bearer #{@bearer_token}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(request)
end
if response.code == '404'
raise "Payment method not found. It may have already been deleted or does not exist."
end
raise "Failed to delete payment method: #{response.code} - #{response.body}" unless response.code == '200'
JSON.parse(response.body)
end
# Helper: Get all active payment methods before deleting
def get_active_saved_payment_methods(merchant_id:, payor_id:)
uri = URI("#{@base_url}/v1/rest/merchants/#{merchant_id}/payors/#{payor_id}/savedPaymentMethods")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{@bearer_token}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(request)
end
raise "Failed to get payment methods: #{response.code}" unless response.code == '200'
all_methods = JSON.parse(response.body)
# Filter to only active (non-deleted) methods
all_methods.select { |m| !m['isDeleted'] }
end
# Safe delete: Verify not the last payment method before deleting
def safe_delete_saved_payment_method(merchant_id:, payor_id:, saved_payment_method_id:)
# First, get all active payment methods
active_methods = get_active_saved_payment_methods(merchant_id: merchant_id, payor_id: payor_id)
if active_methods.length <= 1
raise ArgumentError, "Cannot delete the last remaining payment method. The payor must have at least one active payment method."
end
# Safe to delete
delete_saved_payment_method(
merchant_id: merchant_id,
payor_id: payor_id,
saved_payment_method_id: saved_payment_method_id
)
end
end
# Usage example
if __FILE__ == $0
base_url = 'https://api.example.com'
bearer_token = 'your-oauth-token-here'
merchant_id = '12345678901'
payor_id = '550e8400-e29b-41d4-a716-446655440000'
payment_method_to_delete = '7c9e6679-7425-40de-944b-e07fc1f90ae7'
client = PaymentMethodClient.new(base_url, bearer_token)
begin
# Safe delete with validation
result = client.safe_delete_saved_payment_method(
merchant_id: merchant_id,
payor_id: payor_id,
saved_payment_method_id: payment_method_to_delete
)
puts "Payment method deleted: #{result['maskedAccountNumber']}"
puts "Account type: #{result['accountType']}"
puts "Is deleted: #{result['isDeleted']}"
rescue ArgumentError => e
puts "Cannot delete: #{e.message}"
rescue => e
puts "Error: #{e.message}"
end
end
Common Use Cases
1. Remove Expired Card After Adding Replacement
// Step 1: Add new card with updated expiration
SavedPaymentMethodResponse newCard = await client.AddCreditCardAsync(..., setAsDefault: true);
// Step 2: Delete the old expired card
SavedPaymentMethodResponse deletedCard = await client.DeleteSavedPaymentMethodAsync(
merchantId: "12345678901",
payorId: "550e8400-e29b-41d4-a716-446655440000",
savedPaymentMethodId: oldCardId
);
Console.WriteLine($"Replaced {deletedCard.MaskedAccountNumber} with new card");
2. Clean Up Unused Payment Methods
// Get all active payment methods Listmethods = await client.GetActiveSavedPaymentMethodsAsync(merchantId, payorId); // Keep the default, remove all others foreach (var method in methods.Where(m => !m.IsDefault)) { await client.DeleteSavedPaymentMethodAsync(merchantId, payorId, method.SavedPaymentMethodId); Console.WriteLine($"Removed: {method.MaskedAccountNumber}"); }
3. Switch from Credit Card to ACH
// Step 1: Add new ACH account and set as default SavedPaymentMethodResponse achAccount = await client.AddBankAccountAsync(..., setAsDefault: true); // Step 2: Remove all old credit cards ListcreditCards = (await client.GetActiveSavedPaymentMethodsAsync(merchantId, payorId)) .Where(m => m.AccountType == "VISA" || m.AccountType == "MC" || m.AccountType == "AMEX" || m.AccountType == "DISC") .ToList(); foreach (var card in creditCards) { await client.DeleteSavedPaymentMethodAsync(merchantId, payorId, card.SavedPaymentMethodId); }
Troubleshooting
Error: "Cannot delete the last remaining payment method" (400)
Solution: A payor must have at least one active payment method. Before deleting, either add a new payment method or delete the entire payor profile using DELETE /payors/{payorId} instead.
Error: "Saved payment method not found" (404)
Solution: The payment method may have already been deleted, or the ID is incorrect. Call GET /savedPaymentMethods to list all payment methods and verify the ID. Remember that deleted methods still appear in the list with isDeleted: true.
Payment method deleted but still showing in UI
Solution: This is expected behavior. Deleted payment methods are soft-deleted (marked with isDeleted: true) but remain in the system. Filter them out in your UI by checking the isDeleted flag. Example: activeMethods = allMethods.Where(m => !m.IsDeleted).
Deleted default payment method - which one becomes new default?
Solution: The API automatically promotes another active payment method to default. To control which one becomes default, explicitly set it before or after deletion using PUT /savedPaymentMethods/{id} with setAsDefaultPaymentMethod: true.
Best Practices
- Validate before delete: Use the safe delete pattern shown in the examples to verify the payor has multiple active payment methods before attempting deletion
- Add before remove: When replacing a payment method, add the new one first (and optionally set it as default), then delete the old one. This ensures continuous payment capability
- Confirm with user: Since this operation cannot be undone, implement a confirmation step in your UI before calling this endpoint
- Handle default promotion: If deleting the default payment method, either set a new default explicitly or inform the user which payment method was automatically promoted
- Audit logging: Log all deletion operations in your system for audit trails and customer support purposes
- Grace period: Consider implementing a "disable" flag in your UI before actually calling this API, giving users a grace period to change their mind
- Transaction history: Inform users that past transactions will still show this payment method even after deletion (for reporting and reconciliation)
Security Considerations
- Authorization: Ensure the authenticated user has permission to delete payment methods for this payor. Implement proper access control in your application
- Audit trail: The soft delete preserves payment method data for audit purposes. Deleted methods remain in the system for regulatory compliance and dispute resolution
- No recovery: Once deleted, a payment method cannot be restored. Users must re-enter payment information to create a new payment method
- Fraud detection: Monitor deletion patterns - excessive deletion and re-creation of payment methods may indicate testing stolen card numbers
- PCI compliance: Even deleted payment methods maintain PCI compliance - actual account numbers remain encrypted and are never exposed