PayWeavePayWeaveBack to Home
Apps

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.

Plain Text
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 dashboard

Charge 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.

JSON
{
  "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

POST/api/mpp/app/:appId/refund

Authenticated with the same appSecret Bearer token used for charges. Accepts either transactionId or transactionHash to identify the payment.

JSON
{
  "transactionId": "a3cdf4ea-78ad-4e39-a5b6-d57578c7b4a7",
  "reason": "Database write failed: connection timeout"
}

Or by on-chain hash:

JSON
{
  "transactionHash": "0xabc...def",
  "reason": "External API returned 503"
}

Response

JSON
{
  "success": true,
  "refundId": "f1b2c3d4-...",
  "transactionId": "a3cdf4ea-...",
  "refundAmountUsd": "0.01000000",
  "payerAddress": "did:pkh:eip155:42431:0x...",
  "status": "pending"
}

Example: Express

TypeScript
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

StatusErrorMeaning
404transaction_not_foundTransaction does not exist or does not belong to this app
400invalid_statusTransaction is not in settled status
409already_refundedA refund already exists for this transaction
409refund_existsDuplicate refund request
Store the transactionId from every charge response. It makes refund requests straightforward and avoids needing to parse on-chain hashes.