Refunds
When your app collects a payment but fails to process the request, you can request a refund through the PayWeave API. The workspace owner then reviews and processes the refund from the dashboard.
How it works
Unlike Gateways, Functions, and Files where PayWeave can detect failures automatically, Apps run on your own server. PayWeave has no visibility into whether your app processed the request successfully. So refunds for Apps are app-initiated — your code tells PayWeave when a refund is needed.
1. Payer sends paid request → SDK verifies payment → returns receipt
2. Your app tries to process → fails (DB error, external API down, etc.)
3. Your app calls POST /api/mpp/app/:appId/refund
4. PayWeave creates a pending refund
5. Workspace owner signs and sends the refund from the dashboardCharge response
The charge endpoint now returns a transactionId (PayWeave DB ID) alongside the on-chain transaction hash. Store this ID to reference the payment when requesting a refund.
{
"success": true,
"transactionId": "a3cdf4ea-78ad-4e39-a5b6-d57578c7b4a7",
"transaction": "0xabc...def",
"network": "eip155:42431",
"payer": "did:pkh:eip155:42431:0x...",
"currency": "pathUSD",
"receipt": "..."
}Requesting a refund
/api/mpp/app/:appId/refundAuthenticated with the same appSecret Bearer token used for charges. Accepts either transactionId or transactionHash to identify the payment.
{
"transactionId": "a3cdf4ea-78ad-4e39-a5b6-d57578c7b4a7",
"reason": "Database write failed: connection timeout"
}Or by on-chain hash:
{
"transactionHash": "0xabc...def",
"reason": "External API returned 503"
}Response
{
"success": true,
"refundId": "f1b2c3d4-...",
"transactionId": "a3cdf4ea-...",
"refundAmountUsd": "0.01000000",
"payerAddress": "did:pkh:eip155:42431:0x...",
"status": "pending"
}Example: Express
app.post('/api/summarize', payweave.charge('$0.05'), async (req, res) => {
const { transactionId } = req.payweave; // from charge response
try {
const result = await summarize(req.body.text);
res.json({ result });
} catch (err) {
// Request refund on failure
await fetch(`${PAYWEAVE_URL}/api/mpp/app/${APP_ID}/refund`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${APP_SECRET}`,
},
body: JSON.stringify({
transactionId,
reason: err.message,
}),
});
res.status(500).json({ error: 'Processing failed. Refund requested.' });
}
});Error responses
| Status | Error | Meaning |
|---|---|---|
| 404 | transaction_not_found | Transaction does not exist or does not belong to this app |
| 400 | invalid_status | Transaction is not in settled status |
| 409 | already_refunded | A refund already exists for this transaction |
| 409 | refund_exists | Duplicate refund request |
transactionId from every charge response. It makes refund requests straightforward and avoids needing to parse on-chain hashes.
PayWeave