From 7d855f2b5ff807b226170ef8d417a50122f0c119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andre=CC=81s=20Coca?= Date: Wed, 24 Jul 2024 09:14:34 -0500 Subject: [PATCH] Added support for refunding invoices and line items --- Tests/Recurly/Invoice_Test.php | 36 ++++++++++++++++++++++++++++++++++ lib/recurly/adjustment.php | 35 +++++++++++++++++++++++++++------ lib/recurly/invoice.php | 21 +++++++++++++++++++- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/Tests/Recurly/Invoice_Test.php b/Tests/Recurly/Invoice_Test.php index ae3c807e..8e40b8d3 100644 --- a/Tests/Recurly/Invoice_Test.php +++ b/Tests/Recurly/Invoice_Test.php @@ -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); diff --git a/lib/recurly/adjustment.php b/lib/recurly/adjustment.php index f2686882..396ba810 100644 --- a/lib/recurly/adjustment.php +++ b/lib/recurly/adjustment.php @@ -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. * @@ -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); } @@ -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'; } diff --git a/lib/recurly/invoice.php b/lib/recurly/invoice.php index f5570ed5..37f5132e 100644 --- a/lib/recurly/invoice.php +++ b/lib/recurly/invoice.php @@ -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 @@ -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));