Process refunds for paid invoices using the refund endpoint. This section covers refund conditions, processing, and error handling for different payment types.
Overview
When an invoice has been paid, you can trigger a refund event to return money to the customer. Refunds are only available for specific payment types and have certain conditions that must be met.
Refund Conditions
Prerequisites
- The merchant must have a Card- or MobilePay Recurring merchant agreement
- The invoice must be paid with a refundable payment option
- The refundable amount must be equal to or less than the original invoice amount
- The payment must comply with general refundable payment terms
Refundable Payment Types
- Cards: Dankort, Visa, MasterCard
- MobilePay: MobilePay Recurring, MobilePay E-Payment
Non-Refundable Payment Types
- FI: Payment slip (manual payment)
- LS: LeverandørService (direct debit for businesses)
- BS: BetalingsService (direct debit for private customers)
API Endpoint
Refund Invoice
POST https://api.farpay.io/v2/invoices/{id}/refund
Parameters:
id
(path) - The invoice ID to refund
Request Body:
{
"Amount": 250.50
}
Headers:
X-API-KEY: your-api-key
Content-Type: application/json
Accept: application/json
Refund Processing
Amount Specification
- Partial refund: Specify the amount to refund
- Full refund: Omit the Amount field or set to original amount
- Multiple refunds: You can refund multiple times until the original amount is exhausted
Refund Example
Original Invoice: 500.00 DKK
Partial Refund: 250.50 DKK
{
"Amount": 250.50
}
Full Refund: 500.00 DKK
{
"Amount": 500.00
}
Full Refund (no amount specified):
{}
Response Handling
Success Response (HTTP 200)
Returns a text description confirming the refund amount (partial or full).
Error Response (HTTP 400)
Returns specific error codes with descriptions:
Error Code | Description |
---|---|
20050:100 | Den oprindelige faktura findes ikke. (Original invoice not found) |
20050:200 | Beløbet er større end det oprindelige betalte beløb. (Amount is greater than original paid amount) |
20050:300 | Fakturaen har ingen angivelse af den oprindelige betalingstype (kort, eller MobilePay), der kan refunderes. (Invoice has no indication of original payment type that can be refunded) |
20050:400 | Den oprindelige betaling blev ikke fundet. (Original payment was not found) |
20050:450 | Beløbet er større end det oprindelige betalte beløb. (Amount is greater than original paid amount) |
20050:460 | Ønskede beløb er for højt. Det kan højest være: {beløb} {valuta} (Requested amount is too high. It can be at most: {amount} {currency} ) |
Implementation Examples
JavaScript Example
async function refundInvoice(invoiceId, amount) {
try {
const response = await fetch(`https://api.farpay.io/v2/invoices/${invoiceId}/refund`, {
method: 'POST',
headers: {
'X-API-KEY': 'your-api-key',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({ Amount: amount })
});
if (response.ok) {
const result = await response.text();
console.log('Refund successful:', result);
return { success: true, message: result };
} else {
const error = await response.json();
console.error('Refund failed:', error);
return { success: false, error: error };
}
} catch (error) {
console.error('Request failed:', error);
throw error;
}
}
// Usage examples
// Partial refund
refundInvoice(12345, 250.50)
.then(result => {
if (result.success) {
console.log('Partial refund processed');
} else {
console.error('Refund failed:', result.error);
}
});
// Full refund
refundInvoice(12345, 500.00)
.then(result => {
if (result.success) {
console.log('Full refund processed');
}
});
Python Example
import requests
import json
def refund_invoice(invoice_id, amount, api_key):
url = f'https://api.farpay.io/v2/invoices/{invoice_id}/refund'
headers = {
'X-API-KEY': api_key,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
payload = {
'Amount': amount
}
try:
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
result = response.text
print('Refund successful:', result)
return {'success': True, 'message': result}
else:
error = response.json()
print('Refund failed:', error)
return {'success': False, 'error': error}
except requests.exceptions.RequestException as e:
print('Request failed:', e)
raise e
# Usage
api_key = 'your-api-key'
# Partial refund
result = refund_invoice(12345, 250.50, api_key)
if result['success']:
print('Partial refund processed')
else:
print('Refund failed:', result['error'])
# Full refund
result = refund_invoice(12345, 500.00, api_key)
if result['success']:
print('Full refund processed')
cURL Example
# Partial refund
curl -X POST \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--header "X-API-KEY: mySecretKey" \
-d '{ "Amount": 250.50 }' \
"https://api.farpay.io/v2/invoices/12345/refund"
# Full refund
curl -X POST \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--header "X-API-KEY: mySecretKey" \
-d '{ "Amount": 500.00 }' \
"https://api.farpay.io/v2/invoices/12345/refund"
Error Handling
Common Error Scenarios
async function handleRefundError(error) {
const errorCode = error.errorCode || error;
switch (errorCode) {
case '20050:100':
console.error('Original invoice not found');
break;
case '20050:200':
case '20050:450':
console.error('Amount exceeds original payment');
break;
case '20050:300':
console.error('Payment type not refundable');
break;
case '20050:400':
console.error('Original payment not found');
break;
case '20050:460':
console.error('Amount too high - check maximum refundable amount');
break;
default:
console.error('Unknown refund error:', error);
}
}
Validation Before Refund
async function validateRefundEligibility(invoiceId) {
try {
// Get invoice details
const response = await fetch(`https://api.farpay.io/v2/invoices/${invoiceId}`, {
headers: {
'X-API-KEY': 'your-api-key',
'Accept': 'application/json'
}
});
const invoice = await response.json();
// Check if invoice is paid
if (invoice.PaymentStatus !== 'Paid') {
return { eligible: false, reason: 'Invoice not paid' };
}
// Check payment type
const refundableTypes = ['Visa', 'MasterCard', 'Dankort', 'MobilePayRecurring', 'MobilePayEPayment'];
if (!refundableTypes.includes(invoice.PaymentType)) {
return { eligible: false, reason: 'Payment type not refundable' };
}
// Check if already refunded
if (invoice.RefundedAmount > 0) {
const remainingAmount = invoice.PaidAmount - invoice.RefundedAmount;
return {
eligible: true,
reason: 'Partial refund possible',
maxRefundAmount: remainingAmount
};
}
return {
eligible: true,
reason: 'Full refund possible',
maxRefundAmount: invoice.PaidAmount
};
} catch (error) {
console.error('Error validating refund eligibility:', error);
return { eligible: false, reason: 'Validation error' };
}
}
Refund Tracking
Check Refund Status
async function checkRefundStatus(invoiceId) {
const response = await fetch(`https://api.farpay.io/v2/invoices/${invoiceId}`, {
headers: {
'X-API-KEY': 'your-api-key',
'Accept': 'application/json'
}
});
const invoice = await response.json();
return {
originalAmount: invoice.PaidAmount,
refundedAmount: invoice.RefundedAmount || 0,
remainingAmount: invoice.PaidAmount - (invoice.RefundedAmount || 0),
refundStatus: invoice.RefundStatus
};
}
Best Practices
Before Processing Refunds
- Validate eligibility - Check if invoice and payment type are refundable
- Verify amounts - Ensure refund amount doesn't exceed original payment
- Check payment status - Confirm invoice is actually paid
- Review customer request - Ensure refund is legitimate
During Processing
- Handle errors gracefully - Implement proper error handling
- Log all refunds - Keep detailed records of refund transactions
- Notify customers - Inform customers when refunds are processed
- Monitor refunds - Track refund patterns for fraud detection
After Processing
- Update records - Update your internal records
- Reconcile accounts - Ensure accounting records are accurate
- Follow up - Confirm refund appears on customer's statement
- Document reasons - Keep records of why refunds were issued
Use Cases
Customer Service Refund
async function processCustomerServiceRefund(invoiceId, amount, reason) {
// Validate refund eligibility
const eligibility = await validateRefundEligibility(invoiceId);
if (!eligibility.eligible) {
throw new Error(`Refund not eligible: ${eligibility.reason}`);
}
if (amount > eligibility.maxRefundAmount) {
throw new Error(`Amount exceeds maximum refundable amount: ${eligibility.maxRefundAmount}`);
}
// Process refund
const result = await refundInvoice(invoiceId, amount);
if (result.success) {
// Log refund for customer service
await logRefund(invoiceId, amount, reason, 'customer-service');
// Notify customer
await notifyCustomerOfRefund(invoiceId, amount, reason);
return result;
} else {
throw new Error(`Refund failed: ${result.error}`);
}
}
Partial Refund for Defective Product
async function refundDefectiveProduct(invoiceId, defectiveItems) {
// Calculate refund amount based on defective items
const refundAmount = calculateRefundAmount(defectiveItems);
// Process partial refund
const result = await refundInvoice(invoiceId, refundAmount);
if (result.success) {
// Update inventory
await updateInventory(defectiveItems);
// Schedule replacement shipment
await scheduleReplacement(defectiveItems);
return result;
}
}
Related Endpoints
- Invoices - Invoice creation and management
- Payments - Payment tracking and refunds
- Agreements - Payment agreement management
- Customers - Customer information