Skip to content

Commit

Permalink
Merge pull request #818 from recurly/invoice-and-line-items-refunds
Browse files Browse the repository at this point in the history
Added support for refunding invoices and line items
  • Loading branch information
amandamfielding authored Aug 20, 2024
2 parents 9d3dec1 + 7d855f2 commit a40b9f9
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 7 deletions.
36 changes: 36 additions & 0 deletions Tests/Recurly/Invoice_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,42 @@ public function testRefundAmount() {
$this->assertEquals($refund_invoice->subtotal_in_cents, -1000);
}

public function testRefundPercentage() {
$this->client->addResponse('POST', 'https://api.recurly.com/v2/invoices/1001/refund', 'invoices/refund-201.xml');
$invoice = Recurly_Invoice::get('1001', $this->client);

$refund_invoice = $invoice->refundPercentage(10);
$this->assertEquals($refund_invoice->subtotal_in_cents, -1000);
}

public function testLineItemRefundWithPercentage() {
$this->client->addResponse('POST', 'https://api.recurly.com/v2/invoices/1001/refund', 'invoices/refund-201.xml');
$invoice = Recurly_Invoice::get('1001', $this->client);
$line_items = $invoice->line_items;

$adjustment_map = function($line_item) {
return $line_item->toRefundAttributesWithPercentage(20);
};
$adjustments = array_map($adjustment_map, $line_items);

$refund_invoice = $invoice->refund($adjustments);
$this->assertEquals($refund_invoice->subtotal_in_cents, -1000);
}

public function testLineItemRefundWithAmountInCents() {
$this->client->addResponse('POST', 'https://api.recurly.com/v2/invoices/1001/refund', 'invoices/refund-201.xml');
$invoice = Recurly_Invoice::get('1001', $this->client);
$line_items = $invoice->line_items;

$adjustment_map = function($line_item) {
return $line_item->toRefundAttributesWithAmountInCents(100);
};
$adjustments = array_map($adjustment_map, $line_items);

$refund_invoice = $invoice->refund($adjustments);
$this->assertEquals($refund_invoice->subtotal_in_cents, -1000);
}

public function testRefund() {
$this->client->addResponse('POST', 'https://api.recurly.com/v2/invoices/1001/refund', 'invoices/refund-201.xml');
$invoice = Recurly_Invoice::get('1001', $this->client);
Expand Down
35 changes: 29 additions & 6 deletions lib/recurly/adjustment.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,29 @@ public function delete() {
* @throws Recurly_Error if the adjustment cannot be refunded.
*/
public function refund($quantity = null, $prorate = false, $refund_apply_order = 'credit_first') {
if ($this->state == 'pending') {
throw new Recurly_Error("Only invoiced adjustments can be refunded");
}
$this->checkRefundable();
$invoice = $this->invoice->get();
return $invoice->refund($this->toRefundAttributes($quantity, $prorate), $refund_apply_order);
}

public function refundWithDecimal($quantity_decimal = null, $prorate = false, $refund_apply_order = 'credit_first') {
if ($this->state == 'pending') {
throw new Recurly_Error("Only invoiced adjustments can be refunded");
}
$this->checkRefundable();
$invoice = $this->invoice->get();
return $invoice->refund($this->toRefundAttributesWithDecimal($quantity_decimal, $prorate), $refund_apply_order);
}

public function refundWithPercentage($percentage, $prorate = false, $refund_apply_order = 'credit_first') {
$this->checkRefundable();
$invoice = $this->invoice->get();
return $invoice->refund($this->toRefundAttributesWithPercentage($percentage, $prorate), $refund_apply_order);
}

public function refundWithAmountInCents($amount_in_cents, $prorate = false, $refund_apply_order = 'credit_first') {
$this->checkRefundable();
$invoice = $this->invoice->get();
return $invoice->refund($this->toRefundAttributesWithAmountInCents($amount_in_cents, $prorate), $refund_apply_order);
}

/**
* Converts this adjustment into the attributes needed for a refund.
*
Expand All @@ -113,6 +122,14 @@ public function toRefundAttributesWithDecimal($quantity_decimal = null, $prorate
return array('uuid' => $this->uuid, 'quantity_decimal' => $quantity_decimal, 'prorate' => $prorate);
}

public function toRefundAttributesWithPercentage($percentage, $prorate = false) {
return array('uuid' => $this->uuid, 'percentage' => $percentage, 'prorate' => $prorate);
}

public function toRefundAttributesWithAmountInCents($amount_in_cents, $prorate = false) {
return array('uuid' => $this->uuid, 'amount_in_cents' => $amount_in_cents, 'prorate' => $prorate);
}

protected function createUriForAccount() {
return self::_safeUri(Recurly_Client::PATH_ACCOUNTS, $this->account_code, Recurly_Client::PATH_ADJUSTMENTS);
}
Expand All @@ -126,6 +143,12 @@ public function populateXmlDoc(&$doc, &$node, &$obj, $nested = false) {
}
}

private function checkRefundable() {
if ($this->state == 'pending') {
throw new Recurly_Error("Only invoiced adjustments can be refunded");
}
}

protected function getNodeName() {
return 'adjustment';
}
Expand Down
21 changes: 20 additions & 1 deletion lib/recurly/invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,23 @@ public function refundAmount($amount_in_cents, $refund_method = 'credit_first')
return $this->createRefundInvoice(XmlTools::renderXML($doc));
}

/**
* Refunds a percentage from the invoice and returns a refund invoice
* @param int $percentage percentage to refund from this invoice
* @param string $refund_method indicates the refund order to apply, valid options: {'credit_first','transaction_first','all_transaction','all_credit'}, defaults to 'credit_first'
* @return object Recurly_Invoice a new refund invoice
* @throws Recurly_Error
*/
public function refundPercentage($percentage, $refund_method = 'credit_first') {
$doc = XmlTools::createDocument();

$root = $doc->appendChild($doc->createElement($this->getNodeName()));
$root->appendChild($doc->createElement('refund_method', $refund_method));
$root->appendChild($doc->createElement('percentage', $percentage));

return $this->createRefundInvoice(XmlTools::renderXML($doc));
}

/**
*
* Refunds given line items from an invoice and returns new refund invoice
Expand All @@ -216,8 +233,10 @@ public function refund($line_items, $refund_method = 'credit_first') {
foreach ($line_items as $line_item) {
$adjustment_node = $line_items_node->appendChild($doc->createElement('adjustment'));
$adjustment_node->appendChild($doc->createElement('uuid', $line_item['uuid']));
$adjustment_node->appendChild($doc->createElement('quantity', $line_item['quantity']));
$adjustment_node->appendChild($doc->createElement('prorate', $line_item['prorate'] ? 'true' : 'false'));
if (isset($line_item['quantity'])) { $adjustment_node->appendChild($doc->createElement('quantity', $line_item['quantity'])); }
if (isset($line_item['percentage'])) { $adjustment_node->appendChild($doc->createElement('percentage', $line_item['percentage'])); }
if (isset($line_item['amount_in_cents'])) { $adjustment_node->appendChild($doc->createElement('amount_in_cents', $line_item['amount_in_cents'])); }
}

return $this->createRefundInvoice(XmlTools::renderXML($doc));
Expand Down

0 comments on commit a40b9f9

Please sign in to comment.