diff --git a/CHANGELOG.md b/CHANGELOG.md index a97d714..0733b7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,15 @@ # Changelog -## Latest Version - v12.0.5 (06/18/24) +## Latest Version - v12.0.7 (07/16/24) +### Enhancements: +- [GP-API] Adds avs data to "/transaction" request for digital wallet +- [GP-API] Adds brand reference and stage time to the DisputeSummary + +### Bug Fixes: +- [PAX] Correction to tip/gratuity handling in the request to device + +## Latest Version - v12.0.6 (06/18/24) ### Enhancements: - [GP-ECOM] Add Multi-Capture diff --git a/composer.json b/composer.json index e52a916..ab57530 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,6 @@ }, "require-dev": { "phpunit/phpunit": "^7.5 || ^8.5 || ~9.4", - "brianium/paratest": "4.0.0 || ^v6.6", "squizlabs/php_codesniffer": "2.*", "phpstan/phpstan": "^0.12" }, @@ -39,7 +38,6 @@ "scripts": { "docs": "@php sami.phar update docs-config.php", "pretest": "@composer test:lint", - "test": "@composer pretest && paratest --coverage-html=coverage.html --colors -f --stop-on-failure", "test:lint": "phpcs" }, "config": { diff --git a/metadata.xml b/metadata.xml index 6948c85..a4d7360 100644 --- a/metadata.xml +++ b/metadata.xml @@ -1,3 +1,3 @@ - 12.0.6 + 12.0.7 \ No newline at end of file diff --git a/src/Builders/RequestBuilder/GpApi/GpApiAuthorizationRequestBuilder.php b/src/Builders/RequestBuilder/GpApi/GpApiAuthorizationRequestBuilder.php index 9b918bd..fca5222 100644 --- a/src/Builders/RequestBuilder/GpApi/GpApiAuthorizationRequestBuilder.php +++ b/src/Builders/RequestBuilder/GpApi/GpApiAuthorizationRequestBuilder.php @@ -576,7 +576,6 @@ private function createPaymentMethodParam($builder, $config) /* digital wallet */ switch ($builder->transactionModifier) { case TransactionModifier::ENCRYPTED_MOBILE: - $paymentToken = null; switch ($paymentMethodContainer->mobileType){ case EncyptedMobileType::CLICK_TO_PAY: $paymentToken = ['data' => $paymentMethodContainer->token]; @@ -601,6 +600,10 @@ private function createPaymentMethodParam($builder, $config) $digitalWallet['cryptogram'] = $paymentMethodContainer->cryptogram; $digitalWallet['eci'] = !empty($paymentMethodContainer->eci) ? $paymentMethodContainer->eci : $this->getEciCode($paymentMethodContainer); + $digitalWallet['avs_address'] = + (!empty($builder->billingAddress) ? $builder->billingAddress->streetAddress1 : ''); + $digitalWallet['avs_postal_code'] = + (!empty($builder->billingAddress) ? $builder->billingAddress->postalCode : ''); break; default: break; diff --git a/src/Entities/Reporting/DisputeSummary.php b/src/Entities/Reporting/DisputeSummary.php index 6f19d86..02ba04c 100644 --- a/src/Entities/Reporting/DisputeSummary.php +++ b/src/Entities/Reporting/DisputeSummary.php @@ -14,22 +14,9 @@ class DisputeSummary * @var string */ public $merchantHierarchy; - /** - * @var string - */ - public $merchantName; - /** - * @var string - */ - public $merchantDbaName; - /** - * @var string - */ - public $merchantNumber; - /** - * @var string - */ - public $merchantCategory; + + public ?string $merchantName; + /** * @var \DateTime */ @@ -38,14 +25,7 @@ class DisputeSummary * @var string */ public $depositReference; - /** - * @var string - */ - public $depositType; - /** - * @var string - */ - public $type; + /** * @var integer */ @@ -54,26 +34,16 @@ class DisputeSummary * @var string */ public $caseCurrency; - /** - * @var string - */ - public $caseStage; + + /** This field indicates the distinct step a dispute is at, within the dispute lifecycle. */ + public ?string $caseStage; + + /** Time the current Dispute stage was created. */ + public ?\DateTime $disputeStageTime; /** * @var string */ public $caseStatus; - /** - * @var string - */ - public $caseDescription; - /** - * @var string - */ - public $transactionOrderId; - /** - * @var \DateTime - */ - public $transactionLocalTime; /** * @var \DateTime */ @@ -90,18 +60,9 @@ class DisputeSummary * @var string */ public $transactionCurrency; - /** - * @var string - */ - public $caseNumber; - /** - * @var \DateTime - */ - public $caseTime; - /** - * @var string - */ - public $caseId; + + /** Unique identifier for the Dispute on the Global Payments system. */ + public string $caseId; /** * @var \DateTime */ @@ -110,10 +71,7 @@ class DisputeSummary * @var string */ public $caseMerchantId; - /** - * @var string - */ - public $caseTerminalId; + /** * @var string */ @@ -122,10 +80,7 @@ class DisputeSummary * @var string */ public $transactionReferenceNumber; - /** - * @var string - */ - public $transactionSRD; + /** * @var string */ @@ -150,38 +105,20 @@ class DisputeSummary * @var string */ public $result; - /** - * @var string - */ - public $issuerComment; - /** - * @var string - */ - public $issuerCaseNumber; - /** - * @var integer - */ - public $disputeAmount; - /** - * @var string - */ - public $disputeCurrency; + public array $issuerComment = []; + public array $issuerCaseNumber = []; + /** * @var integer */ public $disputeCustomerAmount; - /** - * @var string - */ - public $disputeCustomerCurrency; + + public ?string $disputeCustomerCurrency; /** * @var \DateTime */ public $respondByDate; - /** - * @var string - */ - public $caseOriginalReference; + /** * @var integer */ @@ -198,6 +135,8 @@ class DisputeSummary /** @var array */ public $documents; + public ?string $transactionBrandReference; + /** * @return ManagementBuilder */ diff --git a/src/Gateways/GpApiConnector.php b/src/Gateways/GpApiConnector.php index 5171f42..9130dff 100644 --- a/src/Gateways/GpApiConnector.php +++ b/src/Gateways/GpApiConnector.php @@ -315,7 +315,7 @@ public function doTransaction( ); } catch (GatewayException $exception) { if ( - strpos($exception->getMessage(), 'NOT_AUTHENTICATED') !== false && + in_array($exception->responseCode, ['NOT_AUTHENTICATED', '401']) && !empty($this->gpApiConfig->appKey) && !empty($this->gpApiConfig->appKey) ) { diff --git a/src/Mapping/GpApiMapping.php b/src/Mapping/GpApiMapping.php index dbe5f2f..433d144 100644 --- a/src/Mapping/GpApiMapping.php +++ b/src/Mapping/GpApiMapping.php @@ -597,10 +597,11 @@ public static function mapDisputeSummary($response) { $summary = new DisputeSummary(); $summary->caseId = $response->id; - $summary->caseIdTime = !empty($response->time_created) ? new \DateTime($response->time_created) : - (!empty($response->stage_time_created) ? new \DateTime($response->stage_time_created) : ''); + $summary->caseIdTime = !empty($response->time_created) ? new \DateTime($response->time_created) : null; $summary->caseStatus = $response->status; $summary->caseStage = $response->stage; + $summary->disputeStageTime = + (!empty($response->stage_time_created) ? new \DateTime($response->stage_time_created) : null); $summary->caseAmount = StringUtils::toAmount($response->amount); $summary->caseCurrency = $response->currency; if (isset($response->system)) { @@ -610,14 +611,11 @@ public static function mapDisputeSummary($response) $summary->merchantName = !empty($system->name) ? $system->name : null; } - if ( - isset($response->payment_method) && - isset($response->payment_method->card) - ) { + if (!empty($response->payment_method->card)) { $card = $response->payment_method->card; - $summary->transactionMaskedCardNumber = $card->number; + $summary->transactionMaskedCardNumber = $card->number ?? null; } - if (isset($response->transaction)) { + if (!empty($response->transaction)) { $summary->transactionTime = $response->transaction->time_created; $summary->transactionType = $response->transaction->type; $summary->transactionAmount = StringUtils::toAmount($response->transaction->amount); @@ -625,9 +623,9 @@ public static function mapDisputeSummary($response) $summary->transactionReferenceNumber = $response->transaction->reference; if (isset($response->transaction->payment_method->card)) { $card = $response->transaction->payment_method->card; - $summary->transactionMaskedCardNumber = !empty($card->masked_number_first6last4) ? - $card->masked_number_first6last4 : ''; + $summary->transactionMaskedCardNumber = $card->masked_number_first6last4 ?? null; $summary->transactionAuthCode = $card->authcode; + $summary->transactionBrandReference = $card->brand_reference ?? null; } } if (!empty($response->documents)) { @@ -654,6 +652,19 @@ public static function mapDisputeSummary($response) $summary->lastAdjustmentFunding = $response->last_adjustment_funding; $summary->depositDate = !empty($response->deposit_time_created) ? $response->deposit_time_created : null; $summary->depositReference = !empty($response->deposit_id) ? $response->deposit_id : null; + $summary->disputeCustomerAmount = !empty($response->payer_amount) ? + StringUtils::toAmount($response->payer_amount) : null; + $summary->disputeCustomerCurrency = $response->payer_currency ?? null; + if (!empty($response->payment_method_provider)) { + foreach ($response->payment_method_provider as $providerPaymentMethod) { + if (!empty($providerPaymentMethod->comment)) { + $summary->issuerComment[] = $providerPaymentMethod->comment; + } + if (!empty($providerPaymentMethod->reference)) { + $summary->issuerCaseNumber[] = $providerPaymentMethod->reference; + } + } + } return $summary; } diff --git a/src/Terminals/PAX/PaxController.php b/src/Terminals/PAX/PaxController.php index 73804dd..9ff7e72 100644 --- a/src/Terminals/PAX/PaxController.php +++ b/src/Terminals/PAX/PaxController.php @@ -226,6 +226,10 @@ public function processTransaction(TerminalAuthBuilder $builder) : TerminalRespo if ($builder->signatureCapture !== null) { $extData->details[PaxExtData::SIGNATURE_CAPTURE] = $builder->signatureCapture; } + + if (empty($builder->gratuity)) { + $extData->details[PaxExtData::TIP_REQUEST] = 1; + } $transactionType = $this->mapTransactionType($builder->transactionType, $builder->requestMultiUseToken); switch ($builder->paymentMethodType) { diff --git a/test/Integration/Gateways/GpApiConnector/GpApiDigitalWalletTest.php b/test/Integration/Gateways/GpApiConnector/GpApiDigitalWalletTest.php index 28e6d98..bec67df 100644 --- a/test/Integration/Gateways/GpApiConnector/GpApiDigitalWalletTest.php +++ b/test/Integration/Gateways/GpApiConnector/GpApiDigitalWalletTest.php @@ -2,6 +2,8 @@ namespace Gateways\GpApiConnector; +use GlobalPayments\Api\Entities\Address; +use GlobalPayments\Api\Entities\Enums\AddressType; use GlobalPayments\Api\Entities\Enums\Channel; use GlobalPayments\Api\Entities\Enums\EncyptedMobileType; use GlobalPayments\Api\Entities\Enums\TransactionModifier; @@ -167,6 +169,10 @@ public function testPayWithApplePayEncrypted() public function testPayWithDecryptedFlow() { $encryptedProviders = [EncyptedMobileType::GOOGLE_PAY, EncyptedMobileType::APPLE_PAY]; + $address = new Address(); + $address->streetAddress1 = "123 Main St."; + $address->postalCode = "12345"; + foreach ($encryptedProviders as $encryptedProvider) { $this->card->token = '5167300431085507'; $this->card->mobileType = $encryptedProvider; @@ -177,6 +183,7 @@ public function testPayWithDecryptedFlow() $response = $this->card->charge($this->amount) ->withCurrency($this->currency) ->withModifier(TransactionModifier::DECRYPTED_MOBILE) + ->withAddress($address) ->execute(); $this->assertTransactionResponse($response, TransactionStatus::CAPTURED); diff --git a/test/Integration/Gateways/GpApiConnector/ReportingDisputesTest.php b/test/Integration/Gateways/GpApiConnector/ReportingDisputesTest.php index 5f78def..bd7a0f1 100644 --- a/test/Integration/Gateways/GpApiConnector/ReportingDisputesTest.php +++ b/test/Integration/Gateways/GpApiConnector/ReportingDisputesTest.php @@ -51,11 +51,14 @@ public static function tearDownAfterClass(): void public function testReportDisputeDetail() { $disputeId = 'DIS_SAND_abcd1234'; + /** @var DisputeSummary $response */ $response = ReportingService::disputeDetail($disputeId) ->execute(); $this->assertNotNull($response); $this->assertInstanceOf(DisputeSummary::class, $response); $this->assertEquals($disputeId, $response->caseId); + $this->assertNotNull($response->transactionBrandReference); + $this->assertNotNull($response->disputeStageTime); $this->arn = $response->transactionARN; } @@ -648,8 +651,9 @@ public function testReportFindSettlementDisputes_FilterBy_FromAndToStageTimeCrea $this->assertNotNull($summary); $this->assertInstanceOf(PagedResult::class, $summary); + /** @var DisputeSummary $dispute */ foreach ($summary->result as $dispute) { - $this->assertTrue($dispute->caseTime <= $this->endDate); + $this->assertTrue($dispute->caseIdTime <= $this->endDate); } } diff --git a/test/Integration/Gateways/Terminals/PAX/PaxCreditTests.php b/test/Integration/Gateways/Terminals/PAX/PaxCreditTests.php index 2e165a5..8d4a30c 100644 --- a/test/Integration/Gateways/Terminals/PAX/PaxCreditTests.php +++ b/test/Integration/Gateways/Terminals/PAX/PaxCreditTests.php @@ -427,4 +427,47 @@ public function testPartialAuthHandling() : void $this->assertEquals(55, $response->amountDue); $this->assertEquals(100, $response->transactionAmount); } + + /** + * This test should demonstrates that the device IS prompting for a tip + * when a gratuity amount isn't provided to the builder. This assumes that + * the device has been configured for gratuity, which is something that is + * set at the terminal file level. + * + * **Requires end-user confirmation** + * + * @return void + * @throws InvalidArgumentException + * @throws ExpectationFailedException + */ + public function testTipPropmpt() : void + { + $response = $this->device->sale("12.34") + ->execute(); + + $this->assertNotNull($response); + $this->assertEquals("00", $response->responseCode); + } + + /** + * This test should demonstrate that a the device is NOT prompting for a + * tip when a gratuity amount IS provided to the builder. This assumes that + * the device is configured for gratuity which is something that is set at + * the terminal file level. + * + * **Requires end-user confirmation** + * + * @return void + * @throws InvalidArgumentException + * @throws ExpectationFailedException + */ + public function testTipNoPropmpt() : void + { + $response = $this->device->sale("15.34") + ->withGratuity("3.00") // this makes for an $18.34 sale on device + ->execute(); + + $this->assertNotNull($response); + $this->assertEquals("00", $response->responseCode); + } }