From c8e6f631e9458d9d4260a13f032cad3afb545bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Walker?= Date: Fri, 28 Nov 2025 21:39:27 -0300 Subject: [PATCH 01/10] feat: add methods to manage credit cards for customers --- src/Contracts/CreditCardContract.php | 14 ++ src/Contracts/CustomerContract.php | 17 ++- src/Contracts/InvoiceContract.php | 10 +- src/Exceptions/GatewayException.php | 8 +- src/Facades/MultiPayment.php | 3 + src/Gateways/IuguGateway.php | 125 +++++++++++++++--- src/Models/Customer.php | 49 +++++++ src/Models/Model.php | 31 ++++- src/MultiPayment.php | 61 ++++++++- src/Traits/MultiPaymentTrait.php | 29 ++++ tests/TestCase.php | 34 +++++ tests/Unit/Builders/CreditCardBuilderTest.php | 35 +---- tests/Unit/Builders/InvoiceBuilderTest.php | 2 +- tests/Unit/MultiPaymentTest.php | 122 +++++++++++++++++ 14 files changed, 471 insertions(+), 69 deletions(-) diff --git a/src/Contracts/CreditCardContract.php b/src/Contracts/CreditCardContract.php index 4200518..5b4e831 100644 --- a/src/Contracts/CreditCardContract.php +++ b/src/Contracts/CreditCardContract.php @@ -19,4 +19,18 @@ interface CreditCardContract * @throws GatewayException|GatewayNotAvailableException */ public function createCreditCard(CreditCard $creditCard): CreditCard; + + /** + * Get a credit card by its ID + * + * @throws GatewayException|GatewayNotAvailableException + */ + public function getCreditCard(CreditCard $creditCard): CreditCard; + + /** + * Delete a credit card + * + * @throws GatewayException|GatewayNotAvailableException + */ + public function deleteCreditCard(CreditCard $creditCard): void; } diff --git a/src/Contracts/CustomerContract.php b/src/Contracts/CustomerContract.php index 740eec6..cdc67e6 100644 --- a/src/Contracts/CustomerContract.php +++ b/src/Contracts/CustomerContract.php @@ -24,12 +24,12 @@ public function createCustomer(Customer $customer): Customer; /** * Return one customer based on the customer ID * - * @param string $id - * + * @param \Potelo\MultiPayment\Models\Customer $customer * @return Customer - * @throws GatewayException|GatewayNotAvailableException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + * @throws \Potelo\MultiPayment\Exceptions\GatewayNotAvailableException */ - public function getCustomer(string $id): Customer; + public function getCustomer(Customer $customer): Customer; /** * Update an existing customer @@ -40,4 +40,13 @@ public function getCustomer(string $id): Customer; * @throws GatewayException|GatewayNotAvailableException */ public function updateCustomer(Customer $customer): Customer; + + /** + * Set the customer's default card + * + * @param \Potelo\MultiPayment\Models\Customer $customer + * @param string $cardId + * @return \Potelo\MultiPayment\Models\Customer + */ + public function setCustomerDefaultCard(Customer $customer, string $cardId): Customer; } diff --git a/src/Contracts/InvoiceContract.php b/src/Contracts/InvoiceContract.php index 0979c4b..b8e2ac0 100644 --- a/src/Contracts/InvoiceContract.php +++ b/src/Contracts/InvoiceContract.php @@ -24,13 +24,13 @@ public function createInvoice(Invoice $invoice): Invoice; /** * Return one invoice based on the invoice ID - * - * @param string $id - * + * + * @param \Potelo\MultiPayment\Models\Invoice $invoice * @return Invoice - * @throws GatewayException|GatewayNotAvailableException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + * @throws \Potelo\MultiPayment\Exceptions\GatewayNotAvailableException */ - public function getInvoice(string $id): Invoice; + public function getInvoice(Invoice $invoice): Invoice; /** * Refund an invoice diff --git a/src/Exceptions/GatewayException.php b/src/Exceptions/GatewayException.php index 62f2b7e..f8455c7 100644 --- a/src/Exceptions/GatewayException.php +++ b/src/Exceptions/GatewayException.php @@ -16,7 +16,13 @@ class GatewayException extends MultiPaymentException public function __construct(string $message = "", $errors = null) { $this->errors = $errors; - parent::__construct($message . ' - ' . $this->parseErrorsToString($errors)); + $appends = $this->parseErrorsToString($errors); + + if (!empty($appends)) { + $message .= ' - ' . $appends; + } + + parent::__construct($message); } /** diff --git a/src/Facades/MultiPayment.php b/src/Facades/MultiPayment.php index 350b381..d637cc1 100644 --- a/src/Facades/MultiPayment.php +++ b/src/Facades/MultiPayment.php @@ -3,6 +3,7 @@ namespace Potelo\MultiPayment\Facades; use Illuminate\Support\Facades\Facade; +use Potelo\MultiPayment\Models\CreditCard; use Potelo\MultiPayment\Models\Invoice; use Potelo\MultiPayment\Builders\InvoiceBuilder; use Potelo\MultiPayment\Builders\CustomerBuilder; @@ -14,6 +15,8 @@ * @method static InvoiceBuilder newInvoice() * @method static CustomerBuilder newCustomer() * @method static CreditCardBuilder newCreditCard() + * @method static CreditCard getCard(string $customerId, string $creditCardId) + * @method static CreditCard deleteCard(string $customerId, string $creditCardId) * @method static \Potelo\MultiPayment\MultiPayment setGateway($gateway) * @method static Invoice chargeInvoiceWithCreditCard($invoice, ?string $creditCardToken = null, ?string $creditCardId = null) */ diff --git a/src/Gateways/IuguGateway.php b/src/Gateways/IuguGateway.php index 93670f3..720c76a 100644 --- a/src/Gateways/IuguGateway.php +++ b/src/Gateways/IuguGateway.php @@ -278,29 +278,16 @@ public function createCreditCard(CreditCard $creditCard): CreditCard throw new GatewayException('Error creating creditCard: ', $iuguCreditCard->errors); } - $creditCard->id = $iuguCreditCard->id ?? null; - $creditCard->brand = $iuguCreditCard->data->brand ?? null; - $creditCard->year = $iuguCreditCard->data->year ?? null; - $creditCard->month = $iuguCreditCard->data->month ?? null; - if (!empty($iuguCreditCard->data->holder_name)) { - $names = explode(' ', $iuguCreditCard->data->holder_name); - $creditCard->firstName = $names[0] ?? null; - $creditCard->lastName = $names[array_key_last($names)] ?? null; - } - $creditCard->lastDigits = $iuguCreditCard->data->last_digits ?? substr($iuguCreditCard->data->display_number, -4); - $creditCard->gateway = 'iugu'; - $creditCard->original = $iuguCreditCard; - $creditCard->createdAt = new Carbon($iuguCreditCard->created_at_iso) ?? null; - return $creditCard; + return $this->parseIuguCard($iuguCreditCard, $creditCard); } /** * @inheritDoc */ - public function getInvoice(string $id): Invoice + public function getInvoice(Invoice $invoice): Invoice { try { - $iuguInvoice = \Iugu_Invoice::fetch($id); + $iuguInvoice = \Iugu_Invoice::fetch($invoice->id); } catch (\IuguRequestException | IuguObjectNotFound $e) { if (str_contains($e->getMessage(), '502 Bad Gateway')) { throw new GatewayNotAvailableException($e->getMessage()); @@ -314,7 +301,7 @@ public function getInvoice(string $id): Invoice throw new GatewayException('Error getting invoice', $iuguInvoice->errors); } - return $this->parseInvoice($iuguInvoice); + return $this->parseInvoice($iuguInvoice, $invoice); } /** @@ -587,10 +574,13 @@ private function chargeIuguInvoice(array $iuguInvoiceData) return $iuguCharge->invoice(); } - public function getCustomer(string $id): Customer + /** + * @inheritDoc + */ + public function getCustomer(Customer $customer): Customer { try { - $iuguCustomer = Iugu_Customer::fetch($id); + $iuguCustomer = Iugu_Customer::fetch($customer->id); } catch (\IuguRequestException | IuguObjectNotFound $e) { if (str_contains($e->getMessage(), '502 Bad Gateway')) { throw new GatewayNotAvailableException($e->getMessage()); @@ -605,7 +595,7 @@ public function getCustomer(string $id): Customer throw new GatewayException('Error getting customer', $iuguCustomer->errors); } - return $this->parseCustomer($iuguCustomer); + return $this->parseCustomer($iuguCustomer, $customer); } public function updateCustomer(Customer $customer): Customer @@ -747,6 +737,101 @@ private function customerToIuguData(Customer $customer): array } } + if (!empty($customer->defaultCard) && !empty($customer->defaultCard->id)) { + $iuguCustomerData['default_payment_method_id'] = $customer->defaultCard->id; + } + return $iuguCustomerData; } + + /** + * @inheritDoc + */ + public function setCustomerDefaultCard(Customer $customer, string $cardId): Customer + { + $customer->defaultCard = new CreditCard(); + $customer->defaultCard->id = $cardId; + + return $this->updateCustomer($customer); + } + + /** + * @inheritDoc + */ + public function deleteCreditCard(CreditCard $creditCard): void + { + try { + $iuguCreditCard = new Iugu_PaymentMethod([ + 'id' => $creditCard->id, + 'customer_id' => $creditCard->customer->id + ]); + $iuguCreditCard->delete(); + } catch (\IuguRequestException | IuguObjectNotFound $e) { + if (str_contains($e->getMessage(), '502 Bad Gateway')) { + throw new GatewayNotAvailableException($e->getMessage()); + } else { + throw new GatewayException($e->getMessage()); + } + } catch (\IuguAuthenticationException $e) { + throw new GatewayNotAvailableException($e->getMessage()); + } catch (\Exception $e) { + throw new GatewayException($e->getMessage()); + } + } + + /** + * @inheritDoc + */ + public function getCreditCard(CreditCard $creditCard): CreditCard + { + try { + $iuguCustomer = new Iugu_Customer(['id' => $creditCard->customer->id]); + $iuguCreditCard = $iuguCustomer->payment_methods()->fetch($creditCard->id); + } catch (\IuguRequestException | IuguObjectNotFound $e) { + if (str_contains($e->getMessage(), '502 Bad Gateway')) { + throw new GatewayNotAvailableException($e->getMessage()); + } else { + throw new GatewayException($e->getMessage()); + } + } catch (\IuguAuthenticationException $e) { + throw new GatewayNotAvailableException($e->getMessage()); + } catch (\Exception $e) { + throw new GatewayException($e->getMessage()); + } + if ($iuguCreditCard->errors) { + throw new GatewayException('Error getting creditCard: ', $iuguCreditCard->errors); + } + + $creditCard = new CreditCard(); + return $this->parseIuguCard($iuguCreditCard, $creditCard); + } + + /** + * @param mixed $iuguCreditCard + * @param \Potelo\MultiPayment\Models\CreditCard|null $creditCard + * @return \Potelo\MultiPayment\Models\CreditCard + */ + private function parseIuguCard(mixed $iuguCreditCard, ?CreditCard $creditCard = null): CreditCard + { + if (is_null($creditCard)) { + $creditCard = new CreditCard(); + } + + $creditCard->id = $iuguCreditCard->id ?? null; + $creditCard->brand = $iuguCreditCard->data->brand ?? null; + $creditCard->year = $iuguCreditCard->data->year ?? null; + $creditCard->month = $iuguCreditCard->data->month ?? null; + $creditCard->description = $iuguCreditCard->description ?? null; + + if (!empty($iuguCreditCard->data->holder_name)) { + $names = explode(' ', $iuguCreditCard->data->holder_name); + $creditCard->firstName = $names[0] ?? null; + $creditCard->lastName = $names[array_key_last($names)] ?? null; + } + $creditCard->lastDigits = $iuguCreditCard->data->last_digits ?? substr($iuguCreditCard->data->display_number, -4); + $creditCard->gateway = 'iugu'; + $creditCard->original = $iuguCreditCard; + $creditCard->createdAt = new Carbon($iuguCreditCard->created_at_iso) ?? null; + return $creditCard; + } } diff --git a/src/Models/Customer.php b/src/Models/Customer.php index 37a9768..fc1babb 100644 --- a/src/Models/Customer.php +++ b/src/Models/Customer.php @@ -3,7 +3,9 @@ namespace Potelo\MultiPayment\Models; use Carbon\Carbon; +use Potelo\MultiPayment\Contracts\GatewayContract; use Potelo\MultiPayment\Exceptions\ModelAttributeValidationException; +use Potelo\MultiPayment\Helpers\ConfigurationHelper; /** * Class Customer @@ -167,4 +169,51 @@ public function toArray(): array } return $array; } + + public function setDefaultCard(string $cardId): Customer + { + $gateway = ConfigurationHelper::resolveGateway($this->gateway); + return $gateway->setCustomerDefaultCard($this, $cardId); + } + + /** + * Get a customer credit card by id from the gateway. + * + * @param string $creditCardId + * @param string|GatewayContract|null $gateway + * + * @return static + * @throws \Potelo\MultiPayment\Exceptions\ConfigurationException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + * @throws \Potelo\MultiPayment\Exceptions\GatewayNotAvailableException + */ + public function getCreditCard(string $creditCardId, GatewayContract|string $gateway = null): CreditCard + { + $gateway = ConfigurationHelper::resolveGateway($gateway); + $creditCard = new CreditCard(); + $creditCard->customer = $this; + $creditCard->id = $creditCardId; + + return $gateway->getCreditCard($creditCard); + } + + /** + * Delete a customer credit card by id from the gateway. + * + * @param string $creditCardId + * @param \Potelo\MultiPayment\Contracts\GatewayContract|string|null $gateway + * @return void + * @throws \Potelo\MultiPayment\Exceptions\ConfigurationException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + * @throws \Potelo\MultiPayment\Exceptions\GatewayNotAvailableException + */ + public function deleteCreditCard(string $creditCardId, GatewayContract|string $gateway = null): void + { + $gateway = ConfigurationHelper::resolveGateway($gateway); + $creditCard = new CreditCard(); + $creditCard->customer = $this; + $creditCard->id = $creditCardId; + + $gateway->deleteCreditCard($creditCard); + } } diff --git a/src/Models/Model.php b/src/Models/Model.php index 9d451d5..36db79b 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -161,13 +161,40 @@ protected static function getClassName(): string * @throws \Potelo\MultiPayment\Exceptions\ConfigurationException * @throws \Potelo\MultiPayment\Exceptions\GatewayException */ - public static function get(string $id, GatewayContract|string $gateway = null): static + public function get(GatewayContract|string $gateway = null): static { $method = 'get' . static::getClassName(); $gateway = ConfigurationHelper::resolveGateway($gateway); if (!method_exists($gateway, $method)) { throw GatewayException::methodNotFound(get_class($gateway), $method); } - return $gateway->$method($id); + return $gateway->$method($this); + } + + /** + * Delete the model instance by id in the gateway. + * + * @param \Potelo\MultiPayment\Contracts\GatewayContract|string|null $gateway + * @return void + * @throws \Potelo\MultiPayment\Exceptions\ConfigurationException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + */ + public function delete(GatewayContract|string $gateway = null): void + { + $method = 'delete' . static::getClassName(); + $gateway = ConfigurationHelper::resolveGateway($gateway); + if (!method_exists($gateway, $method)) { + throw GatewayException::methodNotFound(get_class($gateway), $method); + } + $gateway->$method($this); + } + + /** + * Refresh the model instance with the latest data from the gateway. + */ + public function refresh(GatewayContract|string $gateway = null): static + { + $gateway = ConfigurationHelper::resolveGateway($gateway); + return $this->get($gateway); } } diff --git a/src/MultiPayment.php b/src/MultiPayment.php index 6bfbd9d..6ee3686 100644 --- a/src/MultiPayment.php +++ b/src/MultiPayment.php @@ -104,7 +104,9 @@ public function newCreditCard(): CreditCardBuilder */ public function getInvoice(string $id): Invoice { - return Invoice::get($id, $this->gateway); + $invoice = new Invoice(); + $invoice->id = $id; + return $invoice->get($this->gateway); } /** @@ -140,7 +142,9 @@ public function duplicateInvoice(Invoice|string $invoice, Carbon $expiresAt, arr */ public function getCustomer(string $id): Customer { - return Customer::get($id, $this->gateway); + $customer = new Customer(); + $customer->id = $id; + return $customer->get($this->gateway); } /** @@ -210,4 +214,57 @@ public function chargeInvoiceWithCreditCard($invoice, ?string $creditCardToken = return $invoice->chargeInvoiceWithCreditCard(); } + + /** + * Get a credit card + * + * @param string $customerId + * @param string $creditCardId + * @return CreditCard + * @throws \Potelo\MultiPayment\Exceptions\ConfigurationException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + */ + public function getCard(string $customerId, string $creditCardId): CreditCard + { + $creditCard = new CreditCard(); + $creditCard->customer = new Customer(); + $creditCard->customer->id = $customerId; + $creditCard->id = $creditCardId; + + return $creditCard->get($this->gateway); + } + + /** + * Delete a credit card + * + * @param string $customerId + * @param string $creditCardId + * @return void + * @throws \Potelo\MultiPayment\Exceptions\ConfigurationException + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + */ + public function deleteCard(string $customerId, string $creditCardId): void + { + $creditCard = new CreditCard(); + $creditCard->customer = new Customer(); + $creditCard->customer->id = $customerId; + $creditCard->id = $creditCardId; + + $creditCard->delete($this->gateway); + } + + /** + * Set a credit card as default for a customer + * + * @param string $customerId + * @param string $creditCardId + * @return \Potelo\MultiPayment\Models\Customer + */ + public function setDefaultCard(string $customerId, string $creditCardId): Customer + { + $customer = new Customer(); + $customer->id = $customerId; + return $customer->setDefaultCard($creditCardId); + } + } diff --git a/src/Traits/MultiPaymentTrait.php b/src/Traits/MultiPaymentTrait.php index fa4bea2..9b0a524 100644 --- a/src/Traits/MultiPaymentTrait.php +++ b/src/Traits/MultiPaymentTrait.php @@ -2,6 +2,7 @@ namespace Potelo\MultiPayment\Traits; +use Potelo\MultiPayment\Models\CreditCard; use Potelo\MultiPayment\MultiPayment; use Illuminate\Support\Facades\Config; use Potelo\MultiPayment\Models\Invoice; @@ -80,4 +81,32 @@ private function getGatewayCustomerColumn($gatewayName) return Config::get("multi-payment.gateways.$gatewayName.customer_column"); } + /** + * Set the default credit card of the customer + */ + public function setDefaultCreditCard(string $gatewayName, string $cardId): void + { + $payment = new MultiPayment($gatewayName); + $payment->setDefaultCard($this->getGatewayCustomerId($gatewayName), $cardId); + } + + /** + * Delete a credit card of the customer + */ + public function deleteCreditCard(string $gatewayName, string $cardId): void + { + $payment = new MultiPayment($gatewayName); + $payment->deleteCard($this->getGatewayCustomerId($gatewayName), $cardId); + } + + /** + * Get the default credit card of the customer + */ + public function defaultCreditCard(string $gatewayName): ?CreditCard + { + $payment = new MultiPayment($gatewayName); + $customer = $payment->getCustomer($this->getGatewayCustomerId($gatewayName)); + return $customer->defaultCard?->refresh($gatewayName); + } + } diff --git a/tests/TestCase.php b/tests/TestCase.php index a80e9ca..3a25863 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,6 +3,7 @@ use Carbon\Carbon; use Illuminate\Support\Facades\Config; +use Potelo\MultiPayment\Facades\MultiPayment; use Potelo\MultiPayment\Providers\MultiPaymentServiceProvider; class TestCase extends \Orchestra\Testbench\TestCase @@ -103,4 +104,37 @@ public static function iuguCreditCardToken() { ], ]); } + + public function createCustomer($gateway, $data): \Potelo\MultiPayment\Models\Customer + { + $customerBuilder = MultiPayment::setGateway($gateway)->newCustomer(); + if (!empty($data['email'])) { + $customerBuilder->setEmail($data['email']); + } + if (!empty($data['name'])) { + $customerBuilder->setName($data['name']); + } + if (!empty($data['taxDocument'])) { + $customerBuilder->setTaxDocument($data['taxDocument']); + } + if (!empty($data['phoneNumber']) && !empty($data['phoneArea'])) { + $customerBuilder->setPhone($data['phoneNumber'], $data['phoneArea']); + } + if (!empty($data['birthDate'])) { + $customerBuilder->setBirthDate($data['birthDate']); + } + if (!empty($data['address'])) { + $customerBuilder->addAddress( + $data['address']['zipCode'], + $data['address']['street'], + $data['address']['number'], + $data['address']['complement'], + $data['address']['district'], + $data['address']['city'], + $data['address']['state'], + $data['address']['country'] + ); + } + return $customerBuilder->create(); + } } \ No newline at end of file diff --git a/tests/Unit/Builders/CreditCardBuilderTest.php b/tests/Unit/Builders/CreditCardBuilderTest.php index af8d2d3..f3ef06f 100644 --- a/tests/Unit/Builders/CreditCardBuilderTest.php +++ b/tests/Unit/Builders/CreditCardBuilderTest.php @@ -70,7 +70,7 @@ public function testShouldCreateACreditCard($gateway, $data) $this->assertEquals($data['default'], $creditCard->default); if ($data['default']) { - $customer = $customer->get($customer->id); + $customer = $customer->refresh(); $this->assertEquals($creditCard->id, $customer->defaultCard->id); } @@ -133,37 +133,4 @@ public function testShouldCreateACreditCardWithHash($gateway, $data) $this->assertEquals($gateway, $creditCard->gateway); } - - public function createCustomer($gateway, $data): \Potelo\MultiPayment\Models\Customer - { - $customerBuilder = MultiPayment::setGateway($gateway)->newCustomer(); - if (!empty($data['email'])) { - $customerBuilder->setEmail($data['email']); - } - if (!empty($data['name'])) { - $customerBuilder->setName($data['name']); - } - if (!empty($data['taxDocument'])) { - $customerBuilder->setTaxDocument($data['taxDocument']); - } - if (!empty($data['phoneNumber']) && !empty($data['phoneArea'])) { - $customerBuilder->setPhone($data['phoneNumber'], $data['phoneArea']); - } - if (!empty($data['birthDate'])) { - $customerBuilder->setBirthDate($data['birthDate']); - } - if (!empty($data['address'])) { - $customerBuilder->addAddress( - $data['address']['zipCode'], - $data['address']['street'], - $data['address']['number'], - $data['address']['complement'], - $data['address']['district'], - $data['address']['city'], - $data['address']['state'], - $data['address']['country'] - ); - } - return $customerBuilder->create(); - } } \ No newline at end of file diff --git a/tests/Unit/Builders/InvoiceBuilderTest.php b/tests/Unit/Builders/InvoiceBuilderTest.php index 7c85cdf..eb9e1d9 100644 --- a/tests/Unit/Builders/InvoiceBuilderTest.php +++ b/tests/Unit/Builders/InvoiceBuilderTest.php @@ -171,7 +171,7 @@ public function testShouldCreateInvoice(string $gateway, array $data): void } // Verifica se a fatura foi criada no gateway com os dados corretos - $invoice = $invoice->get($invoice->id, $gateway); + $invoice = $invoice->refresh(); $this->assertNotEmpty($invoice->status); diff --git a/tests/Unit/MultiPaymentTest.php b/tests/Unit/MultiPaymentTest.php index eb09e95..7401995 100644 --- a/tests/Unit/MultiPaymentTest.php +++ b/tests/Unit/MultiPaymentTest.php @@ -31,6 +31,128 @@ public function testShouldGetInvoice() $this->assertEquals($invoiceFetched->id, $invoice->id); } + /** + * Test if can get the card by id + * + * @return void + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + */ + public function testShouldGetCard() + { + $gateway = 'iugu'; + $data = $this->creditCard(); + $customer = $this->createCustomer($gateway, $this->customerWithoutAddress()); + + $creditCard = MultiPayment::setGateway($gateway)->newCreditCard() + ->setDescription($data['description']) + ->setNumber($data['number']) + ->setCustomerId($customer->id) + ->setFirstName($data['firstName']) + ->setLastName($data['lastName']) + ->setMonth($data['month']) + ->setYear($data['year']) + ->setCvv($data['cvv']) + ->setDescription($data['description']) + ->setAsDefault($data['default']) + ->create(); + + $multiPayment = new \Potelo\MultiPayment\MultiPayment($gateway); + $card = $multiPayment->getCard($customer->id, $creditCard->id); + + $this->assertEquals($creditCard->id, $card->id); + $this->assertEquals(substr($data['number'], -4), $card->lastDigits); + $this->assertEquals($data['description'], $card->description); + $this->assertEquals($data['firstName'], $card->firstName); + $this->assertEquals($data['lastName'], $card->lastName); + $this->assertEquals($data['month'], $card->month); + $this->assertEquals($data['year'], $card->year); + } + + /** + * Test if can delete the card by id + * + * @return void + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + */ + public function testShouldDeleteCard() + { + $gateway = 'iugu'; + $data = $this->creditCard(); + $customer = $this->createCustomer($gateway, $this->customerWithoutAddress()); + + $creditCard = MultiPayment::setGateway($gateway)->newCreditCard() + ->setDescription($data['description']) + ->setNumber($data['number']) + ->setCustomerId($customer->id) + ->setFirstName($data['firstName']) + ->setLastName($data['lastName']) + ->setMonth($data['month']) + ->setYear($data['year']) + ->setCvv($data['cvv']) + ->setDescription($data['description']) + ->setAsDefault($data['default']) + ->create(); + + $multiPayment = new \Potelo\MultiPayment\MultiPayment($gateway); + $multiPayment->deleteCard($customer->id, $creditCard->id); + + $this->expectException(\Potelo\MultiPayment\Exceptions\GatewayException::class); + $this->expectExceptionMessage('payment_method: not found'); + $multiPayment->getCard($customer->id, $creditCard->id); + } + + /** + * Test if can set the card as default + * + * @return void + * @throws \Potelo\MultiPayment\Exceptions\GatewayException + */ + public function testShouldSetCardAsDefault() + { + $gateway = 'iugu'; + $data = $this->creditCard(); + $customer = $this->createCustomer($gateway, $this->customerWithoutAddress()); + + $creditCardOne = MultiPayment::setGateway($gateway)->newCreditCard() + ->setDescription($data['description']) + ->setNumber($data['number']) + ->setCustomerId($customer->id) + ->setFirstName($data['firstName']) + ->setLastName($data['lastName']) + ->setMonth($data['month']) + ->setYear($data['year']) + ->setCvv($data['cvv']) + ->setDescription($data['description']) + ->setAsDefault() + ->create(); + + $customer = $customer->refresh(); + $this->assertEquals($creditCardOne->id, $customer->defaultCard->id); + + $creditCardTwo = MultiPayment::setGateway($gateway)->newCreditCard() + ->setDescription($data['description']) + ->setNumber($data['number']) + ->setCustomerId($customer->id) + ->setFirstName($data['firstName']) + ->setLastName($data['lastName']) + ->setMonth($data['month']) + ->setYear($data['year']) + ->setCvv($data['cvv']) + ->setDescription($data['description']) + ->setAsDefault(false) + ->create(); + + $customer = $customer->refresh(); + $this->assertEquals($creditCardOne->id, $customer->defaultCard->id); + + $multiPayment = new \Potelo\MultiPayment\MultiPayment($gateway); + $multiPayment->setGateway($gateway); + $multiPayment->setDefaultCard($customer->id, $creditCardTwo->id); + + $customer = $customer->refresh(); + $this->assertEquals($creditCardTwo->id, $customer->defaultCard->id); + } + /** * Test if can duplicate the invoice * From 89c8af31350dc637af66b2b17ec24c2c84ba94dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Walker?= Date: Fri, 28 Nov 2025 21:44:04 -0300 Subject: [PATCH 02/10] feat: add method to retrieve a customer's credit card --- src/Traits/MultiPaymentTrait.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Traits/MultiPaymentTrait.php b/src/Traits/MultiPaymentTrait.php index 9b0a524..b212cd9 100644 --- a/src/Traits/MultiPaymentTrait.php +++ b/src/Traits/MultiPaymentTrait.php @@ -99,6 +99,15 @@ public function deleteCreditCard(string $gatewayName, string $cardId): void $payment->deleteCard($this->getGatewayCustomerId($gatewayName), $cardId); } + /** + * Get a credit card of the customer + */ + public function getCreditCard(string $gatewayName, string $cardId): CreditCard + { + $payment = new MultiPayment($gatewayName); + return $payment->getCard($this->getGatewayCustomerId($gatewayName), $cardId); + } + /** * Get the default credit card of the customer */ From a08da54303c0ebc8171be0e63c3eea6aa6bb526f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Oliveira?= Date: Fri, 28 Nov 2025 21:47:13 -0300 Subject: [PATCH 03/10] Update tests/Unit/MultiPaymentTest.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/Unit/MultiPaymentTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Unit/MultiPaymentTest.php b/tests/Unit/MultiPaymentTest.php index 7401995..26d0b60 100644 --- a/tests/Unit/MultiPaymentTest.php +++ b/tests/Unit/MultiPaymentTest.php @@ -114,7 +114,6 @@ public function testShouldSetCardAsDefault() $customer = $this->createCustomer($gateway, $this->customerWithoutAddress()); $creditCardOne = MultiPayment::setGateway($gateway)->newCreditCard() - ->setDescription($data['description']) ->setNumber($data['number']) ->setCustomerId($customer->id) ->setFirstName($data['firstName']) From 910b8ff1b6f64d2de9e40e08933aaebf15ff2853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Oliveira?= Date: Fri, 28 Nov 2025 21:47:28 -0300 Subject: [PATCH 04/10] Update tests/Unit/MultiPaymentTest.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/Unit/MultiPaymentTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Unit/MultiPaymentTest.php b/tests/Unit/MultiPaymentTest.php index 26d0b60..7030613 100644 --- a/tests/Unit/MultiPaymentTest.php +++ b/tests/Unit/MultiPaymentTest.php @@ -129,7 +129,6 @@ public function testShouldSetCardAsDefault() $this->assertEquals($creditCardOne->id, $customer->defaultCard->id); $creditCardTwo = MultiPayment::setGateway($gateway)->newCreditCard() - ->setDescription($data['description']) ->setNumber($data['number']) ->setCustomerId($customer->id) ->setFirstName($data['firstName']) From 3529373b77121af78fb772602536a096a6e4aae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Oliveira?= Date: Fri, 28 Nov 2025 21:48:05 -0300 Subject: [PATCH 05/10] Update src/Facades/MultiPayment.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Facades/MultiPayment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Facades/MultiPayment.php b/src/Facades/MultiPayment.php index d637cc1..55798a6 100644 --- a/src/Facades/MultiPayment.php +++ b/src/Facades/MultiPayment.php @@ -16,7 +16,7 @@ * @method static CustomerBuilder newCustomer() * @method static CreditCardBuilder newCreditCard() * @method static CreditCard getCard(string $customerId, string $creditCardId) - * @method static CreditCard deleteCard(string $customerId, string $creditCardId) + * @method static void deleteCard(string $customerId, string $creditCardId) * @method static \Potelo\MultiPayment\MultiPayment setGateway($gateway) * @method static Invoice chargeInvoiceWithCreditCard($invoice, ?string $creditCardToken = null, ?string $creditCardId = null) */ From 07187d5e37b7c3d8f86adf802acc9f666df8221e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Oliveira?= Date: Fri, 28 Nov 2025 21:48:31 -0300 Subject: [PATCH 06/10] Update tests/Unit/MultiPaymentTest.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/Unit/MultiPaymentTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Unit/MultiPaymentTest.php b/tests/Unit/MultiPaymentTest.php index 7030613..2b4136b 100644 --- a/tests/Unit/MultiPaymentTest.php +++ b/tests/Unit/MultiPaymentTest.php @@ -44,7 +44,6 @@ public function testShouldGetCard() $customer = $this->createCustomer($gateway, $this->customerWithoutAddress()); $creditCard = MultiPayment::setGateway($gateway)->newCreditCard() - ->setDescription($data['description']) ->setNumber($data['number']) ->setCustomerId($customer->id) ->setFirstName($data['firstName']) From 20bfdb1f6726310dad55b92635558b4d7874a8ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Oliveira?= Date: Fri, 28 Nov 2025 21:48:43 -0300 Subject: [PATCH 07/10] Update tests/Unit/MultiPaymentTest.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/Unit/MultiPaymentTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Unit/MultiPaymentTest.php b/tests/Unit/MultiPaymentTest.php index 2b4136b..16f8c0c 100644 --- a/tests/Unit/MultiPaymentTest.php +++ b/tests/Unit/MultiPaymentTest.php @@ -143,7 +143,6 @@ public function testShouldSetCardAsDefault() $this->assertEquals($creditCardOne->id, $customer->defaultCard->id); $multiPayment = new \Potelo\MultiPayment\MultiPayment($gateway); - $multiPayment->setGateway($gateway); $multiPayment->setDefaultCard($customer->id, $creditCardTwo->id); $customer = $customer->refresh(); From 014cd5e5e9d073dc314b0dfb22ef487d2d3c4718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Oliveira?= Date: Fri, 28 Nov 2025 21:49:32 -0300 Subject: [PATCH 08/10] Update src/Facades/MultiPayment.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Facades/MultiPayment.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Facades/MultiPayment.php b/src/Facades/MultiPayment.php index 55798a6..7a0f9ec 100644 --- a/src/Facades/MultiPayment.php +++ b/src/Facades/MultiPayment.php @@ -19,6 +19,7 @@ * @method static void deleteCard(string $customerId, string $creditCardId) * @method static \Potelo\MultiPayment\MultiPayment setGateway($gateway) * @method static Invoice chargeInvoiceWithCreditCard($invoice, ?string $creditCardToken = null, ?string $creditCardId = null) + * @method static \Potelo\MultiPayment\Models\Customer setDefaultCard(string $customerId, string $creditCardId) */ class MultiPayment extends Facade { From d686fee79ada84e0690d7a327ab426fb00f866ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Oliveira?= Date: Fri, 28 Nov 2025 21:49:54 -0300 Subject: [PATCH 09/10] Update tests/Unit/MultiPaymentTest.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/Unit/MultiPaymentTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Unit/MultiPaymentTest.php b/tests/Unit/MultiPaymentTest.php index 16f8c0c..72d19e8 100644 --- a/tests/Unit/MultiPaymentTest.php +++ b/tests/Unit/MultiPaymentTest.php @@ -80,7 +80,6 @@ public function testShouldDeleteCard() $customer = $this->createCustomer($gateway, $this->customerWithoutAddress()); $creditCard = MultiPayment::setGateway($gateway)->newCreditCard() - ->setDescription($data['description']) ->setNumber($data['number']) ->setCustomerId($customer->id) ->setFirstName($data['firstName']) From c61e118eeb48c58f9625f8907cce148e58bf61cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Walker?= Date: Fri, 28 Nov 2025 21:51:39 -0300 Subject: [PATCH 10/10] feat: remove unnecessary instantiation of CreditCard in IuguGateway --- src/Gateways/IuguGateway.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Gateways/IuguGateway.php b/src/Gateways/IuguGateway.php index 720c76a..e4f63c0 100644 --- a/src/Gateways/IuguGateway.php +++ b/src/Gateways/IuguGateway.php @@ -802,7 +802,6 @@ public function getCreditCard(CreditCard $creditCard): CreditCard throw new GatewayException('Error getting creditCard: ', $iuguCreditCard->errors); } - $creditCard = new CreditCard(); return $this->parseIuguCard($iuguCreditCard, $creditCard); }