集成指南
PIX Refund-In(退款)
概述
PIX Refund-In 端点允许您退还(退款)通过 Cash-In 生成的收款码收到的 PIX 付款。退款可以是全额或部分退款,必须在收款后 89 天内申请。
此端点需要有效的 Bearer token。详情请参阅认证文档。
功能特性
- 全额或部分退款
- 同一交易支持多次部分退款
- 89 天退款窗口期
- 即时处理
- 按退款原因追踪
何时使用退款
全额退款
将收到的全部金额退还给原始付款方。
使用场景:
- 完全取消订单
- 商品未发货
- 重复付款
- 收款金额错误
部分退款
仅退还收到金额的一部分。
使用场景:
- 退回特定商品
- 产品/服务问题补偿
- 金额调整
- 追溯折扣
端点
POST /api/pix/refund-in/{id}
请求退还已收到的付款。
必需请求头
Authorization: Bearer {token}
Content-Type: application/json路径参数
idstringobrigatorio要退款的原始交易(Cash-In)的 ID。
示例: "7845"
请求体
{
"refundValue": 75.00,
"reason": "Cliente solicitou devolução de 1 item do pedido"
}请求
curl -X POST https://api.ntxpay.com/api/pix/refund-in/7845 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"refundValue": 75.00,
"reason": "Cliente solicitou devolução de 1 item do pedido"
}'响应 (201 Created)
{
"transactionId": "7846",
"externalId": "D123456789",
"status": "PENDING",
"refundValue": 75.00,
"providerTransactionId": "7ef4fc3f-a187-495e-857c-e84d70612761",
"generateTime": "2024-01-19T16:30:00.000Z"
}请求参数
refundValuenumberobrigatorio退款金额,单位为巴西雷亚尔 (BRL)。最多 2 位小数。
验证规则:
- 必须大于等于 0.01
- 不能超过可退款金额
- 所有退款总额不能超过原始金额
示例: 75.00
reasonstring退款原因(可选,但建议填写)。
最大长度: 255 个字符
示例: "Cliente solicitou devolução de 1 item do pedido"
建议: 为审计目的始终提供清晰的原因
externalIdstring退款标识的外部 ID(可选)。
在 BACEN API 中,对应 URL 参数中的 'id'。
示例: "D123456789"
响应结构
transactionIdstringsempre presente新生成的退款交易 ID。
示例: "7846"
注意: 这是与原始交易不同的 ID
externalIdstringsempre presente退款交易的外部 ID。
示例: "D123456789"
statusstringsempre presente当前退款交易状态。
可选值:
PENDING:退款处理中CONFIRMED:退款已确认并完成ERROR:处理错误
示例: "PENDING"
refundValuenumbersempre presente退款金额(BRL)。
示例: 75.00
providerTransactionIdstringsempre presente提供商的交易 ID(用于与 webhooks 关联)。
示例: "7ef4fc3f-a187-495e-857c-e84d70612761"
generateTimestringsempre presente退款生成日期和时间(ISO 8601 UTC)。
示例: "2024-01-19T16:30:00.000Z"
实现示例
Node.js / TypeScript
import axios from 'axios';
interface RefundRequest {
refundValue: number;
reason?: string;
externalId?: string;
}
interface RefundResponse {
transactionId: string;
externalId: string;
status: 'PENDING' | 'CONFIRMED' | 'ERROR';
refundValue: number;
providerTransactionId: string;
generateTime: string;
}
async function refundPixPayment(
token: string,
originalTransactionId: string,
refundAmount: number,
reason?: string
): Promise<RefundResponse> {
const payload: RefundRequest = {
refundValue: refundAmount,
reason: reason || 'Estorno solicitado pelo cliente'
};
try {
const response = await axios.post<RefundResponse>(
`https://api.ntxpay.com/api/pix/refund-in/${originalTransactionId}`,
payload,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log('PIX refund initiated successfully!');
console.log(`Refund Transaction ID: ${response.data.transactionId}`);
console.log(`Original External ID: ${response.data.externalId}`);
console.log(`Refund Amount: R$ ${response.data.refundValue.toFixed(2)}`);
console.log(`Status: ${response.data.status}`);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
const errorData = error.response?.data;
console.error('Error processing refund:', errorData);
// Handle specific errors
if (error.response?.status === 400) {
if (errorData?.message?.includes('prazo excedido')) {
throw new Error('The 89-day refund window has expired');
}
if (errorData?.message?.includes('valor inválido')) {
throw new Error('Refund amount exceeds the available amount for refund');
}
}
if (error.response?.status === 404) {
throw new Error('Original transaction not found');
}
throw new Error(errorData?.message || 'Error processing refund');
}
throw error;
}
}
// Usage - Full Refund
async function fullRefund(token: string, transactionId: string, originalValue: number) {
return await refundPixPayment(
token,
transactionId,
originalValue,
'Cancelamento total do pedido'
);
}
// Usage - Partial Refund
async function partialRefund(token: string, transactionId: string, itemValue: number) {
return await refundPixPayment(
token,
transactionId,
itemValue,
'Devolução de 1 item do pedido'
);
}
// Practical example
const token = 'your_token_here';
const transactionId = '7845';
// Refund R$ 75.00 from a R$ 150.00 transaction
refundPixPayment(token, transactionId, 75.00, 'Cliente solicitou devolução parcial');Python
import requests
from datetime import datetime
from typing import Dict, Optional
def refund_pix_payment(
token: str,
original_transaction_id: str,
refund_amount: float,
reason: Optional[str] = None
) -> Dict:
"""
Refund a received PIX payment
Args:
token: Valid Bearer token
original_transaction_id: Original transaction ID (Cash-In)
refund_amount: Amount to be refunded
reason: Refund reason (optional)
Returns:
Created refund data
"""
url = f'https://api.ntxpay.com/api/pix/refund-in/{original_transaction_id}'
payload = {
'refundValue': round(refund_amount, 2),
'reason': reason or 'Estorno solicitado pelo cliente'
}
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
try:
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
print('PIX refund initiated successfully!')
print(f"Refund Transaction ID: {data['transactionId']}")
print(f"Original External ID: {data['externalId']}")
print(f"Refund Amount: R$ {data['refundValue']:.2f}")
print(f"Status: {data['status']}")
return data
except requests.exceptions.HTTPError as e:
error_data = e.response.json() if e.response else {}
error_message = error_data.get('message', str(e))
# Handle specific errors
if e.response.status_code == 400:
if 'prazo excedido' in error_message:
raise Exception('The 89-day refund window has expired')
if 'valor inválido' in error_message:
raise Exception('Refund amount exceeds the available amount for refund')
raise Exception(f'Invalid data: {error_message}')
if e.response.status_code == 404:
raise Exception('Original transaction not found')
raise Exception(f'Error processing refund: {error_message}')
# Usage
token = 'your_token_here'
transaction_id = '7845'
# Partial refund
refund = refund_pix_payment(
token=token,
original_transaction_id=transaction_id,
refund_amount=75.00,
reason='Cliente solicitou devolução de 1 item do pedido'
)
# Full refund
def full_refund(token: str, transaction_id: str, original_value: float):
"""Performs a full refund"""
return refund_pix_payment(
token=token,
original_transaction_id=transaction_id,
refund_amount=original_value,
reason='Cancelamento total do pedido'
)响应状态码
| 状态码 | 描述 | 含义 |
|---|---|---|
201 | 退款已创建 | PIX 退款发起成功 |
400 | 金额无效 | 退款金额超过可退款金额 |
400 | 窗口期已过 | 89 天退款窗口期已过期 |
401 | 令牌无效 | 未提供令牌、令牌已过期或无效 |
404 | 交易未找到 | 原始交易未找到 |
请参阅 API 参考 获取响应字段的完整详情。
最佳实践
重要说明
退款一旦发起即无法撤销。请在处理前确认金额正确。
- 最长窗口期: 收款后 89 天
- 最小金额: R$ 0.01
- 多次退款: 允许,只要总额不超过原始金额