Skip to main content
Polar provides comprehensive refund handling with automatic benefit revocation, tax calculation adjustments, and transaction accounting.

Overview

Refunds can be issued for:
  • One-time purchases (orders)
  • Subscription charges
  • Both partial and full amounts
Refunds are processed through Stripe and typically appear in the customer’s account within 5-10 business days.

Creating a Refund

1

Find the Order

Navigate to Sales > Orders and locate the order you want to refund.
2

Initiate Refund

Click Refund and specify the amount:
{
  "order_id": "order_xxx",
  "amount": 5000,
  "reason": "requested_by_customer",
  "comment": "Customer requested refund via support ticket #123",
  "revoke_benefits": true
}
3

Review and Confirm

Review the refund details including tax adjustments and click Confirm.

Refund Reasons

When creating a refund, specify a reason:
  • duplicate: Duplicate charge
  • fraudulent: Fraudulent transaction
  • requested_by_customer: Customer requested
  • dispute_prevention: Preventing a chargeback/dispute
  • other: Other reason
The reason is sent to Stripe and helps with accounting and dispute management.

Partial Refunds

You can issue partial refunds up to the refundable amount:
// Refund $50 of a $100 order
await polar.refunds.create({
  orderId: 'order_xxx',
  amount: 5000,  // Amount in cents
  reason: 'requested_by_customer'
});
The refund amount cannot exceed the order’s remaining refundable amount (total - previous refunds).

Tax Handling

Polar automatically calculates and processes tax refunds:
  1. Tax Amount Calculation: Proportional tax is calculated based on the refund amount
  2. Tax Processor Update: The tax transaction is reverted in the tax processor (e.g., Stripe Tax)
  3. Accounting: Both the refund amount and tax amount are recorded separately
Example: $50 refund with $5 tax
{
  "amount": 5000,
  "tax_amount": 500,
  "total_refunded": 5500
}

Benefit Revocation

When refunding one-time purchases, you can choose to revoke associated benefits:
{
  "order_id": "order_xxx",
  "amount": 10000,
  "revoke_benefits": true
}
Subscription Limitations: Benefits cannot be revoked via refund for active subscriptions. Benefits are automatically revoked when the subscription is fully canceled.

How Benefit Revocation Works

1

Refund Created

Refund is processed and marked for benefit revocation.
2

Background Job

A background task identifies all benefits granted from the refunded order.
3

Benefits Revoked

Each benefit is revoked according to its type (API keys deleted, webhooks sent, etc.).

Refund Status

Refunds go through several statuses:
  • pending: Refund initiated, waiting for processor
  • succeeded: Refund completed successfully
  • failed: Refund failed (e.g., insufficient funds)
  • canceled: Refund was canceled before completion
Only succeeded refunds trigger benefit revocation and accounting entries.

Customer Balance Impact

When a refund is processed:
  1. Positive Balance Reduction: If the customer has a positive account balance, it’s reduced by the refund amount
  2. Transaction Recording: A refund transaction is created in the customer’s wallet
  3. Event Emission: An order.refunded event is emitted

Refund Blocking

Certain orders cannot be refunded:
  • Orders with active disputes/chargebacks
  • Orders already fully refunded
  • Orders in certain fraud prevention states
Error Response
{
  "error": "RefundsBlocked",
  "message": "Refunds are blocked for order: order_xxx",
  "status_code": 403
}

Dispute Prevention

Polar can automatically issue refunds to prevent chargebacks:
Automatic Refund
{
  "reason": "dispute_prevention",
  "dispute": {
    "id": "dispute_xxx",
    "amount": 10000,
    "reason": "fraudulent"
  }
}
When integrated with Chargeback Stop or similar services, Polar automatically issues refunds to prevent disputes and reduce chargeback fees.

Webhooks

Refund events trigger webhooks:
  • refund.created: When a refund is initiated
  • refund.updated: When refund status changes
  • order.refunded: When an order’s refund total changes
refund.created Event
{
  "type": "refund.created",
  "data": {
    "id": "refund_xxx",
    "amount": 5000,
    "tax_amount": 500,
    "status": "succeeded",
    "order_id": "order_xxx",
    "revoke_benefits": true
  }
}

Internal Comments

Add internal comments to refunds for record-keeping:
{
  "order_id": "order_xxx",
  "amount": 5000,
  "comment": "Customer reported billing issue. Verified and approved by support team."
}
Comments are internal only and not visible to customers.

Metadata

Attach custom metadata to refunds:
{
  "order_id": "order_xxx",
  "amount": 5000,
  "metadata": {
    "support_ticket": "TICKET-12345",
    "approved_by": "[email protected]"
  }
}

Best Practices

Document Everything

Always add internal comments explaining why refunds were issued

Review Before Revoking

Consider the customer relationship before revoking benefits

Track with Metadata

Use metadata to link refunds to support tickets or incidents

Monitor Patterns

Track refund reasons to identify product or service issues

API Reference

See the full API documentation:

Build docs developers (and LLMs) love