diff --git a/src/Contracts/Gateway.php b/src/Contracts/Gateway.php index 58e66df..cb99d6e 100644 --- a/src/Contracts/Gateway.php +++ b/src/Contracts/Gateway.php @@ -66,5 +66,17 @@ public function refundInvoice(Invoice $invoice): Invoice; * * @return string */ + + /** + * Charge an invoice with a credit card + * + * @param \Potelo\MultiPayment\Models\Invoice $invoice + * @return \Potelo\MultiPayment\Models\Invoice + * @throws \Potelo\MultiPayment\Exceptions\ChargingException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + * @throws \Potelo\MultiPayment\Exceptions\ModelAttributeValidationException + */ + public function chargeInvoiceWithCreditCard(Invoice $invoice): Invoice; + public function __toString(); } diff --git a/src/Exceptions/MultiPaymentException.php b/src/Exceptions/MultiPaymentException.php index ad1eeb6..7c23d1c 100644 --- a/src/Exceptions/MultiPaymentException.php +++ b/src/Exceptions/MultiPaymentException.php @@ -2,7 +2,7 @@ namespace Potelo\MultiPayment\Exceptions; -abstract class MultiPaymentException extends \Exception +class MultiPaymentException extends \Exception { // } diff --git a/src/Facades/MultiPayment.php b/src/Facades/MultiPayment.php index f1deda9..350b381 100644 --- a/src/Facades/MultiPayment.php +++ b/src/Facades/MultiPayment.php @@ -15,6 +15,7 @@ * @method static CustomerBuilder newCustomer() * @method static CreditCardBuilder newCreditCard() * @method static \Potelo\MultiPayment\MultiPayment setGateway($gateway) + * @method static Invoice chargeInvoiceWithCreditCard($invoice, ?string $creditCardToken = null, ?string $creditCardId = null) */ class MultiPayment extends Facade { diff --git a/src/Gateways/IuguGateway.php b/src/Gateways/IuguGateway.php index 175c7f3..e2f369f 100644 --- a/src/Gateways/IuguGateway.php +++ b/src/Gateways/IuguGateway.php @@ -92,19 +92,7 @@ public function createInvoice(Invoice $invoice): Invoice $invoice->creditCard = $this->createCreditCard($invoice->creditCard); } $iuguInvoiceData['customer_payment_method_id'] = $invoice->creditCard->id; - try { - $iuguCharge = \Iugu_Charge::create($iuguInvoiceData); - } catch (\Exception $e) { - throw new GatewayException($e->getMessage()); - } - if ($iuguCharge->errors) { - throw new GatewayException('Error charging invoice', $iuguCharge->errors); - } elseif (!$iuguCharge->success) { - $exception = new ChargingException('Error charging invoice: ' . $iuguCharge->info_message); - $exception->chargeResponse = $iuguCharge; - throw $exception; - } - $iuguInvoice = $iuguCharge->invoice(); + $iuguInvoice = $this->chargeIuguInvoice($iuguInvoiceData); } else { try { $iuguInvoice = \Iugu_Invoice::create($iuguInvoiceData); @@ -486,4 +474,63 @@ private function parseInvoice($iuguInvoice, ?Invoice $invoice = null): Invoice return $invoice; } + + /** + * @inheritDoc + * @param \Potelo\MultiPayment\Models\Invoice $invoice + * @return \Potelo\MultiPayment\Models\Invoice + * @throws \Potelo\MultiPayment\Exceptions\ChargingException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + * @throws \Potelo\MultiPayment\Exceptions\ModelAttributeValidationException + */ + public function chargeInvoiceWithCreditCard(Invoice $invoice): Invoice + { + if (empty($invoice->id)) { + throw ModelAttributeValidationException::required('Invoice', 'id'); + } + + if (empty($invoice->creditCard)) { + throw ModelAttributeValidationException::required('Invoice', 'creditCard'); + } + + if (empty($invoice->creditCard->token) && empty($invoice->creditCard->id)) { + throw new ModelAttributeValidationException('Credit card token or id is required'); + } + + $iuguInvoiceData = []; + $iuguInvoiceData['invoice_id'] = $invoice->id; + + if (!empty($invoice->creditCard->id)) { + $iuguInvoiceData['customer_payment_method_id'] = $invoice->creditCard->id; + } else { + $iuguInvoiceData['token'] = $invoice->creditCard->token; + } + + $iuguInvoice = $this->chargeIuguInvoice($iuguInvoiceData); + + return $this->parseInvoice($iuguInvoice, $invoice); + } + + /** + * @param array $iuguInvoiceData + * @return mixed + * @throws \Potelo\MultiPayment\Exceptions\ChargingException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + */ + private function chargeIuguInvoice(array $iuguInvoiceData) + { + try { + $iuguCharge = \Iugu_Charge::create($iuguInvoiceData); + } catch (\Exception $e) { + throw new GatewayException($e->getMessage()); + } + if ($iuguCharge->errors) { + throw new GatewayException('Error charging invoice', $iuguCharge->errors); + } elseif (!$iuguCharge->success) { + $exception = new ChargingException('Error charging invoice: ' . $iuguCharge->info_message); + $exception->chargeResponse = $iuguCharge; + throw $exception; + } + return $iuguCharge->invoice(); + } } diff --git a/src/Models/Invoice.php b/src/Models/Invoice.php index e4344e7..9e81abe 100644 --- a/src/Models/Invoice.php +++ b/src/Models/Invoice.php @@ -290,4 +290,23 @@ public function refund(): Invoice $gateway = ConfigurationHelper::resolveGateway($this->gateway); return $gateway->refundInvoice($this); } + + /** + * Charge invoice with credit card + * + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + * @throws \Potelo\MultiPayment\Exceptions\ModelAttributeValidationException + * @throws \Potelo\MultiPayment\Exceptions\ChargingException + * @throws \Potelo\MultiPayment\Exceptions\ConfigurationException + */ + public function chargeInvoiceWithCreditCard(?CreditCard $creditCard = null): Invoice + { + if (!empty($creditCard)) { + $this->creditCard = $creditCard; + } + + $gateway = ConfigurationHelper::resolveGateway($this->gateway); + + return $gateway->chargeInvoiceWithCreditCard($this); + } } diff --git a/src/MultiPayment.php b/src/MultiPayment.php index c39b794..21460da 100644 --- a/src/MultiPayment.php +++ b/src/MultiPayment.php @@ -2,6 +2,8 @@ namespace Potelo\MultiPayment; +use Potelo\MultiPayment\Exceptions\MultiPaymentException; +use Potelo\MultiPayment\Models\CreditCard; use Potelo\MultiPayment\Models\Invoice; use Potelo\MultiPayment\Models\Customer; use Potelo\MultiPayment\Contracts\Gateway; @@ -125,4 +127,49 @@ public function refundInvoice(string $id, ?int $partialValueCents = null): Invoi return $invoice->refund(); } + + /** + * Charge invoice with credit card + * + * @param Invoice|string $invoice + * @param string|null $creditCardToken + * @param string|null $creditCardId + * + * @return \Potelo\MultiPayment\Models\Invoice + * + * @throws \Potelo\MultiPayment\Exceptions\ChargingException + * @throws \Potelo\MultiPayment\Exceptions\ConfigurationException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + * @throws \Potelo\MultiPayment\Exceptions\ModelAttributeValidationException + * @throws \Potelo\MultiPayment\Exceptions\MultiPaymentException + */ + public function chargeInvoiceWithCreditCard($invoice, ?string $creditCardToken = null, ?string $creditCardId = null): Invoice + { + if (is_string($invoice)) { + $invoiceInstance = new Invoice(); + $invoiceInstance->id = $invoice; + $invoice = $invoiceInstance; + } + + if (!empty($creditCardToken) && !empty($creditCardId)) { + throw new MultiPaymentException('"creditCardToken" and "creditCardId" are mutually exclusive'); + } + + if (!empty($creditCardToken)) { + $invoice->creditCard = new CreditCard(); + $invoice->creditCard->token = $creditCardToken; + } elseif (!empty($creditCardId)) { + $invoice->creditCard = new CreditCard(); + $invoice->creditCard->id = $creditCardId; + } + + if (empty($invoice->creditCard)) { + throw new MultiPaymentException('"invoice->creditCard" or "creditCardToken" or "creditCardId" must be provided'); + } + + $invoice->gateway = $this->gateway; + $invoice->creditCard->gateway = $this->gateway; + + return $invoice->chargeInvoiceWithCreditCard(); + } } diff --git a/tests/Unit/MultiPaymentTest.php b/tests/Unit/MultiPaymentTest.php index a45e08f..acd5963 100644 --- a/tests/Unit/MultiPaymentTest.php +++ b/tests/Unit/MultiPaymentTest.php @@ -2,6 +2,7 @@ namespace Potelo\MultiPayment\Tests\Unit; +use Potelo\MultiPayment\Models\CreditCard; use Potelo\MultiPayment\Tests\TestCase; use Potelo\MultiPayment\Models\Invoice; use Potelo\MultiPayment\Facades\MultiPayment; @@ -131,4 +132,101 @@ public function shouldRefundInvoiceDataProvider(): array ], ]; } + + + /** + * Test if can refund the invoice + * + * @dataProvider shouldChargeInvoiceWithCreditCard + * + * @param string $gateway + * @param array $data + * @param string $status + * @param string $creditCardDataMethod + * @return void + * @throws \Potelo\MultiPayment\Exceptions\ChargingException + * @throws \Potelo\MultiPayment\Exceptions\ConfigurationException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + * @throws \Potelo\MultiPayment\Exceptions\GatewayNotAvailableException + * @throws \Potelo\MultiPayment\Exceptions\ModelAttributeValidationException + * @throws \Potelo\MultiPayment\Exceptions\MultiPaymentException + */ + public function testShouldChargeInvoiceWithCreditCard(string $gateway, array $data, string $status, string $creditCardDataMethod) + { + $multiPayment = new \Potelo\MultiPayment\MultiPayment($gateway); + + $invoiceBuilder = $multiPayment->newInvoice(); + $invoiceBuilder->addCustomer( + $data['customer']['name'] ?? null, + $data['customer']['email'] ?? null, + $data['customer']['taxDocument'] ?? null, + $data['customer']['birthDate'] ?? null, + $data['customer']['phoneArea'] ?? null, + $data['customer']['phoneNumber'] ?? null + ); + foreach ($data['items'] as $item) { + $invoiceBuilder->addItem($item['description'], $item['price'], $item['quantity']); + } + $invoice = $invoiceBuilder->create(); + sleep(3); + + if ($creditCardDataMethod == 'creditCard') { + $invoice->creditCard = new CreditCard(); + $invoice->creditCard->fill(self::creditCard()); + $invoice->creditCard->customer = $invoice->customer; + $invoice->creditCard->save(); + + $invoice = $multiPayment->chargeInvoiceWithCreditCard($invoice); + } elseif ($creditCardDataMethod == 'token') { + $creditCardToken = self::iuguCreditCardToken(); + $invoice = $multiPayment->chargeInvoiceWithCreditCard($invoice->id, $creditCardToken); + } elseif ($creditCardDataMethod == 'id') { + $creditCard = new CreditCard(); + $creditCard->fill(self::creditCard()); + $creditCard->customer = $invoice->customer; + $creditCard->save(); + $invoice = $multiPayment->chargeInvoiceWithCreditCard($invoice->id, null, $creditCard->id); + } + + $this->assertEquals($status, $invoice->status); + } + + /** + * @return array + */ + public function shouldChargeInvoiceWithCreditCard(): array + { + return [ + 'iugu - credit card object' => [ + 'gateway' => 'iugu', + 'data' => [ + 'items' => [['description' => 'Teste', 'quantity' => 1, 'price' => 10000,]], + 'customer' => self::customerWithoutAddress(), + 'paymentMethod' => 'credit_card', + ], + 'status' => Invoice::STATUS_PAID, + 'creditCardDataMethod' => 'creditCard', + ], + 'iugu - credit card token' => [ + 'gateway' => 'iugu', + 'data' => [ + 'items' => [['description' => 'Teste', 'quantity' => 1, 'price' => 10000,]], + 'customer' => self::customerWithoutAddress(), + 'paymentMethod' => 'credit_card', + ], + 'status' => Invoice::STATUS_PAID, + 'creditCardDataMethod' => 'token', + ], + 'iugu - credit card id' => [ + 'gateway' => 'iugu', + 'data' => [ + 'items' => [['description' => 'Teste', 'quantity' => 1, 'price' => 10000,]], + 'customer' => self::customerWithoutAddress(), + 'paymentMethod' => 'credit_card', + ], + 'status' => Invoice::STATUS_PAID, + 'creditCardDataMethod' => 'id', + ], + ]; + } } \ No newline at end of file