diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 8ebd507..c4bfc1d 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] + php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} steps: diff --git a/README.md b/README.md index 5943546..1e5b54f 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,34 @@ if ( } ``` +### Using alternative payment methods + +To use an alternative payment method (like Google Pay), send a received token AS-IS or data from a decrypted token. + +```php +command()->setPaymentMethodType(Command::PAYMENT_METHOD_TYPE_GOOGLE_PAY); + +// option 1: send received token AS-IS +$operation->paymentMethod()->setToken(''); + +// option 2: send data from decrypted token +$operation->paymentMethod() + ->setPAN('') + ->setExpire('') + ->setCardHolderName('') // if available + ->setExternalTokenCryptogram('') // if available + ->setExternalTokenECI('') // if available + ->setExternalTokenTransStatus('') // available for Click to Pay + ->setExternalTokenDsTransId('') // available for Click to Pay + ->setExternalTokenAcsTransId('') // available for Click to Pay + ->setExternalTokenCardHolderAuthenticated($decryptedToken['paymentMethodDetails']['assuranceDetails']['cardHolderAuthenticated']); // for Google Pay +``` + ### Callback validation ```php diff --git a/src/Gateway/DataSets/Command.php b/src/Gateway/DataSets/Command.php index 703ea05..1278172 100644 --- a/src/Gateway/DataSets/Command.php +++ b/src/Gateway/DataSets/Command.php @@ -31,6 +31,11 @@ class Command extends DataSet implements DataSetInterface const DATA_SOURCE_USE_GATEWAY_SAVED_MERCHANT_INITIATED = 5; const DATA_SOURCE_USE_MERCHANT_SAVED_MERCHANT_INITIATED = 6; + const PAYMENT_METHOD_TYPE_CARD = 'cc'; + const PAYMENT_METHOD_TYPE_GOOGLE_PAY = 'google_pay'; + const PAYMENT_METHOD_TYPE_APPLE_PAY = 'apple_pay'; + const PAYMENT_METHOD_TYPE_CLICK2PAY = 'click2pay'; + /** * @param string $gatewayTransactionID * @return Command @@ -96,4 +101,15 @@ public function setPaymentMethodDataToken(string $paymentMethodDataToken): self return $this; } + + /** + * @param string $paymentMethodType + * @return Command + */ + public function setPaymentMethodType(string $paymentMethodType): self + { + $this->data[ self::COMMAND_DATA_PAYMENT_METHOD_TYPE ] = $paymentMethodType; + + return $this; + } } diff --git a/src/Gateway/DataSets/DataSet.php b/src/Gateway/DataSets/DataSet.php index 17f197e..d552a67 100644 --- a/src/Gateway/DataSets/DataSet.php +++ b/src/Gateway/DataSets/DataSet.php @@ -28,6 +28,7 @@ abstract class DataSet const COMMAND_DATA_CARD_VERIFICATION = 'data.command-data.card-verification'; const COMMAND_DATA_PAYMENT_METHOD_DATA_SOURCE = 'data.command-data.payment-method-data-source'; const COMMAND_DATA_PAYMENT_METHOD_DATA_TOKEN = 'data.command-data.payment-method-data-token'; + const COMMAND_DATA_PAYMENT_METHOD_TYPE = 'data.command-data.payment-method-type'; // customer data const GENERAL_DATA_CUSTOMER_DATA_EMAIL = 'data.general-data.customer-data.email'; @@ -69,12 +70,20 @@ abstract class DataSet const PAYMENT_METHOD_DATA_EXPIRE = 'data.payment-method-data.exp-mm-yy'; const PAYMENT_METHOD_DATA_CVV = 'data.payment-method-data.cvv'; const PAYMENT_METHOD_DATA_CARDHOLDER_NAME = 'data.payment-method-data.cardholder-name'; + const PAYMENT_METHOD_DATA_TOKEN = 'data.payment-method-data.token'; const PAYMENT_METHOD_DATA_EXTERNAL_MPI_DATA = 'data.payment-method-data.external-mpi-data'; const PAYMENT_METHOD_DATA_EXTERNAL_MPI_PROTOCOL = 'data.payment-method-data.external-mpi-data.protocolVersion'; const PAYMENT_METHOD_DATA_EXTERNAL_MPI_DS_TRANS_ID = 'data.payment-method-data.external-mpi-data.dsTransID'; const PAYMENT_METHOD_DATA_EXTERNAL_MPI_XID = 'data.payment-method-data.external-mpi-data.xid'; const PAYMENT_METHOD_DATA_EXTERNAL_MPI_CAVV = 'data.payment-method-data.external-mpi-data.cavv'; const PAYMENT_METHOD_DATA_EXTERNAL_MPI_TRANS_STATUS = 'data.payment-method-data.external-mpi-data.transStatus'; + const PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DATA = 'data.payment-method-data.external-token-data'; + const PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM = 'data.payment-method-data.external-token-data.cryptogram'; + const PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI = 'data.payment-method-data.external-token-data.eci'; + const PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS = 'data.payment-method-data.external-token-data.transStatus'; + const PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID = 'data.payment-method-data.external-token-data.dsTransID'; + const PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID = 'data.payment-method-data.external-token-data.acsTransID'; + const PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED = 'data.payment-method-data.external-token-data.cardHolderAuthenticated'; // money data const MONEY_DATA_AMOUNT = 'data.money-data.amount'; diff --git a/src/Gateway/DataSets/PaymentMethod.php b/src/Gateway/DataSets/PaymentMethod.php index 091bf6f..f648754 100644 --- a/src/Gateway/DataSets/PaymentMethod.php +++ b/src/Gateway/DataSets/PaymentMethod.php @@ -64,6 +64,17 @@ public function setCardHolderName(string $cardHolderName): self return $this; } + /** + * @param string $token + * @return PaymentMethod + */ + public function setToken(string $token): self + { + $this->data[self::PAYMENT_METHOD_DATA_TOKEN] = $token; + + return $this; + } + /** * @param string $protocolVersion * @return PaymentMethod @@ -118,4 +129,70 @@ public function setExternalMpiTransStatus(string $transStatus): self return $this; } + + /** + * @param string $cryptogram + * @return PaymentMethod + */ + public function setExternalTokenCryptogram(string $cryptogram): self + { + $this->data[self::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM] = $cryptogram; + + return $this; + } + + /** + * @param string $eci + * @return PaymentMethod + */ + public function setExternalTokenECI(string $eci): self + { + $this->data[self::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI] = $eci; + + return $this; + } + + /** + * @param string $transStatus + * @return PaymentMethod + */ + public function setExternalTokenTransStatus(string $transStatus): self + { + $this->data[self::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS] = $transStatus; + + return $this; + } + + /** + * @param string $dsTransId + * @return PaymentMethod + */ + public function setExternalTokenDsTransId(string $dsTransId): self + { + $this->data[self::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID] = $dsTransId; + + return $this; + } + + /** + * @param string $acsTransId + * @return PaymentMethod + */ + public function setExternalTokenAcsTransId(string $acsTransId): self + { + $this->data[self::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID] = $acsTransId; + + return $this; + } + + /** + * @param bool $value + * @return PaymentMethod + */ + public function setExternalTokenCardHolderAuthenticated(bool $value): self + { + $this->data[self::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED] = $value; + + return $this; + } } diff --git a/src/Gateway/Exceptions/DigestMismatchException.php b/src/Gateway/Exceptions/DigestMismatchException.php index 6c9456b..4e6b8b8 100644 --- a/src/Gateway/Exceptions/DigestMismatchException.php +++ b/src/Gateway/Exceptions/DigestMismatchException.php @@ -21,7 +21,7 @@ */ class DigestMismatchException extends ResponseException { - public function __construct($message = "Digest mismatch", $code = 0, Throwable $previous = null) + public function __construct($message = "Digest mismatch", $code = 0, ?Throwable $previous = null) { parent::__construct($message, $code, $previous); } diff --git a/src/Gateway/Exceptions/DigestMissingException.php b/src/Gateway/Exceptions/DigestMissingException.php index e2dbd55..bccb43d 100644 --- a/src/Gateway/Exceptions/DigestMissingException.php +++ b/src/Gateway/Exceptions/DigestMissingException.php @@ -20,7 +20,7 @@ */ class DigestMissingException extends ResponseException { - public function __construct($message = "Digest is missing", $code = 0, Throwable $previous = null) + public function __construct($message = "Digest is missing", $code = 0, ?Throwable $previous = null) { parent::__construct($message, $code, $previous); } diff --git a/src/Gateway/Http/Crypto/ResponseDigest.php b/src/Gateway/Http/Crypto/ResponseDigest.php index d4fd27a..f83426d 100644 --- a/src/Gateway/Http/Crypto/ResponseDigest.php +++ b/src/Gateway/Http/Crypto/ResponseDigest.php @@ -28,7 +28,7 @@ class ResponseDigest extends Digest * @throws DigestMissingException * @throws DigestMismatchException */ - public function __construct(string $authorizationHeader = null) + public function __construct(?string $authorizationHeader = null) { if (empty($authorizationHeader)) { throw new DigestMissingException(); diff --git a/src/Gateway/Http/Response.php b/src/Gateway/Http/Response.php index ca23892..0f1a1bf 100644 --- a/src/Gateway/Http/Response.php +++ b/src/Gateway/Http/Response.php @@ -169,7 +169,7 @@ public function getDigest() /** * @param ResponseDigest|null $digest */ - public function setDigest(ResponseDigest $digest = null) + public function setDigest(?ResponseDigest $digest = null) { $this->digest = $digest; } diff --git a/src/Gateway/Responses/CallbackResult.php b/src/Gateway/Responses/CallbackResult.php index a65402f..e200c5e 100644 --- a/src/Gateway/Responses/CallbackResult.php +++ b/src/Gateway/Responses/CallbackResult.php @@ -13,7 +13,7 @@ class CallbackResult extends PaymentResponse { - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { parent::__construct($rawDecoded['result-data'] ?? null); } diff --git a/src/Gateway/Responses/CsvResponse.php b/src/Gateway/Responses/CsvResponse.php index cbc93aa..09dd847 100644 --- a/src/Gateway/Responses/CsvResponse.php +++ b/src/Gateway/Responses/CsvResponse.php @@ -39,7 +39,7 @@ public function __construct(string $data) throw new ResponseException('Cannot parse CSV data: no headers line'); } - $this->headers = str_getcsv($rawHeaders); + $this->headers = str_getcsv($rawHeaders, ",", "\"", "\\"); $this->next(); } @@ -83,7 +83,7 @@ public function next() return; } - $data = str_getcsv(trim($rawLine, "\n")); + $data = str_getcsv(trim($rawLine, "\n"), ",", "\"", "\\"); $this->current = array_combine($this->headers, $data); $this->status = true; } diff --git a/src/Gateway/Responses/EnrollmentResponse.php b/src/Gateway/Responses/EnrollmentResponse.php index 79371b5..7d15607 100644 --- a/src/Gateway/Responses/EnrollmentResponse.php +++ b/src/Gateway/Responses/EnrollmentResponse.php @@ -15,7 +15,7 @@ class EnrollmentResponse extends GenericResponse { public $enrollment; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { parent::__construct($rawDecoded); $this->enrollment = ($rawDecoded['enrollment'] ?? 'n') === 'y'; diff --git a/src/Gateway/Responses/GenericResponse.php b/src/Gateway/Responses/GenericResponse.php index 19e25f3..0a413b5 100644 --- a/src/Gateway/Responses/GenericResponse.php +++ b/src/Gateway/Responses/GenericResponse.php @@ -17,7 +17,7 @@ class GenericResponse { public $error; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->error = !empty($rawDecoded['error']) ? new Error($rawDecoded['error']) : null; } diff --git a/src/Gateway/Responses/HistoryResponse.php b/src/Gateway/Responses/HistoryResponse.php index a1742f2..9807877 100644 --- a/src/Gateway/Responses/HistoryResponse.php +++ b/src/Gateway/Responses/HistoryResponse.php @@ -17,7 +17,7 @@ class HistoryResponse extends GenericResponse { public $transactions = []; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { parent::__construct($rawDecoded); if (!empty($rawDecoded['transactions']) && is_array($rawDecoded['transactions'])) { diff --git a/src/Gateway/Responses/LimitsResponse.php b/src/Gateway/Responses/LimitsResponse.php index 90928e7..91d4f44 100644 --- a/src/Gateway/Responses/LimitsResponse.php +++ b/src/Gateway/Responses/LimitsResponse.php @@ -17,7 +17,7 @@ class LimitsResponse extends GenericResponse { public $limits; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { parent::__construct($rawDecoded); $this->limits = new ObjectLimits($rawDecoded); diff --git a/src/Gateway/Responses/Parts/Info/HistoryEvent.php b/src/Gateway/Responses/Parts/Info/HistoryEvent.php index 622cd8e..32b8c6b 100644 --- a/src/Gateway/Responses/Parts/Info/HistoryEvent.php +++ b/src/Gateway/Responses/Parts/Info/HistoryEvent.php @@ -22,7 +22,7 @@ class HistoryEvent public $statusTextNew; public $statusTextOld; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->dateUpdated = !empty($rawDecoded['date-updated']) ? DateTime::createFromFormat('Y-m-d H:i:s', $rawDecoded['date-updated'], new DateTimeZone('UTC')) : null; $this->statusCodeNew = intval($rawDecoded['status-code-new'] ?? null); diff --git a/src/Gateway/Responses/Parts/Info/InitialRecurringTransaction.php b/src/Gateway/Responses/Parts/Info/InitialRecurringTransaction.php index 9fcf17c..86ae4b1 100644 --- a/src/Gateway/Responses/Parts/Info/InitialRecurringTransaction.php +++ b/src/Gateway/Responses/Parts/Info/InitialRecurringTransaction.php @@ -19,7 +19,7 @@ class InitialRecurringTransaction public $error; public $subsequent = []; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->gatewayTransactionId = strval($rawDecoded['gateway-transaction-id'] ?? null); $this->error = !empty($rawDecoded['error']) ? new Error($rawDecoded['error']) : null; diff --git a/src/Gateway/Responses/Parts/Info/Limit.php b/src/Gateway/Responses/Parts/Info/Limit.php index 6b168db..44a3b96 100644 --- a/src/Gateway/Responses/Parts/Info/Limit.php +++ b/src/Gateway/Responses/Parts/Info/Limit.php @@ -20,7 +20,7 @@ class Limit public $paymentMethodType; public $value; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->counterType = strval($rawDecoded['counter-type'] ?? null); $this->currency = strval($rawDecoded['currency'] ?? null); diff --git a/src/Gateway/Responses/Parts/Info/ObjectLimits.php b/src/Gateway/Responses/Parts/Info/ObjectLimits.php index 4d47cb0..f4afb68 100644 --- a/src/Gateway/Responses/Parts/Info/ObjectLimits.php +++ b/src/Gateway/Responses/Parts/Info/ObjectLimits.php @@ -19,7 +19,7 @@ class ObjectLimits public $limits = []; public $children = []; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->type = strval($rawDecoded['type'] ?? null); $this->title = strval($rawDecoded['title'] ?? null); diff --git a/src/Gateway/Responses/Parts/Info/RefundedTransaction.php b/src/Gateway/Responses/Parts/Info/RefundedTransaction.php index e55d605..490d9bd 100644 --- a/src/Gateway/Responses/Parts/Info/RefundedTransaction.php +++ b/src/Gateway/Responses/Parts/Info/RefundedTransaction.php @@ -19,7 +19,7 @@ class RefundedTransaction public $error; public $refunds = []; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->gatewayTransactionId = strval($rawDecoded['gateway-transaction-id'] ?? null); $this->error = !empty($rawDecoded['error']) ? new Error($rawDecoded['error']) : null; diff --git a/src/Gateway/Responses/Parts/Info/TransactionHistory.php b/src/Gateway/Responses/Parts/Info/TransactionHistory.php index 1b660b9..5f6a0de 100644 --- a/src/Gateway/Responses/Parts/Info/TransactionHistory.php +++ b/src/Gateway/Responses/Parts/Info/TransactionHistory.php @@ -19,7 +19,7 @@ class TransactionHistory public $error; public $history = []; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->gatewayTransactionId = strval($rawDecoded['gateway-transaction-id'] ?? null); $this->error = !empty($rawDecoded['error']) ? new Error($rawDecoded['error']) : null; diff --git a/src/Gateway/Responses/Parts/Info/TransactionInfo.php b/src/Gateway/Responses/Parts/Info/TransactionInfo.php index 59a1991..8ab6fb6 100644 --- a/src/Gateway/Responses/Parts/Info/TransactionInfo.php +++ b/src/Gateway/Responses/Parts/Info/TransactionInfo.php @@ -32,7 +32,7 @@ class TransactionInfo public $statusText; public $statusTextGeneral; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->accountGuid = strval($rawDecoded['account-guid'] ?? null); $this->acqTerminalId = strval($rawDecoded['acq-terminal-id'] ?? null); diff --git a/src/Gateway/Responses/Parts/Info/TransactionResult.php b/src/Gateway/Responses/Parts/Info/TransactionResult.php index aea7838..93b8d79 100644 --- a/src/Gateway/Responses/Parts/Info/TransactionResult.php +++ b/src/Gateway/Responses/Parts/Info/TransactionResult.php @@ -24,7 +24,7 @@ class TransactionResult public $error; public $resultData; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->dateCreated = !empty($rawDecoded['date-created']) ? DateTime::createFromFormat('Y-m-d H:i:s', $rawDecoded['date-created'], new DateTimeZone('UTC')) : null; $this->dateFinished = !empty($rawDecoded['date-finished']) ? DateTime::createFromFormat('Y-m-d H:i:s', $rawDecoded['date-finished'], new DateTimeZone('UTC')) : null; diff --git a/src/Gateway/Responses/Parts/Info/TransactionStatus.php b/src/Gateway/Responses/Parts/Info/TransactionStatus.php index 38cb289..522977a 100644 --- a/src/Gateway/Responses/Parts/Info/TransactionStatus.php +++ b/src/Gateway/Responses/Parts/Info/TransactionStatus.php @@ -26,7 +26,7 @@ class TransactionStatus public $cardFamily; public $cardMask; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->error = !empty($rawDecoded['error']) ? new Error($rawDecoded['error']) : null; $this->gatewayTransactionId = strval($rawDecoded['gateway-transaction-id'] ?? null); diff --git a/src/Gateway/Responses/Parts/Payment/AcquirerDetails.php b/src/Gateway/Responses/Parts/Payment/AcquirerDetails.php index b188724..92d9dc3 100644 --- a/src/Gateway/Responses/Parts/Payment/AcquirerDetails.php +++ b/src/Gateway/Responses/Parts/Payment/AcquirerDetails.php @@ -21,7 +21,7 @@ class AcquirerDetails public $terminalMid; public $transactionId; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->dynamicDescriptor = strval($rawDecoded['dynamic-descriptor'] ?? null); $this->eciSli = strval($rawDecoded['eci-sli'] ?? null); diff --git a/src/Gateway/Responses/Parts/Payment/Error.php b/src/Gateway/Responses/Parts/Payment/Error.php index 7933e33..e7516d9 100644 --- a/src/Gateway/Responses/Parts/Payment/Error.php +++ b/src/Gateway/Responses/Parts/Payment/Error.php @@ -16,7 +16,7 @@ class Error public $code; public $message; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->code = intval($rawDecoded['code'] ?? null); $this->message = strval($rawDecoded['message'] ?? null); diff --git a/src/Gateway/Responses/Parts/Payment/GW.php b/src/Gateway/Responses/Parts/Payment/GW.php index 4e44cb7..aae9cfc 100644 --- a/src/Gateway/Responses/Parts/Payment/GW.php +++ b/src/Gateway/Responses/Parts/Payment/GW.php @@ -21,7 +21,7 @@ class GW public $statusCode; public $statusText; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { $this->gatewayTransactionId = strval($rawDecoded['gateway-transaction-id'] ?? null); $this->merchantTransactionId = strval($rawDecoded['merchant-transaction-id'] ?? null); diff --git a/src/Gateway/Responses/PaymentResponse.php b/src/Gateway/Responses/PaymentResponse.php index 913d788..e390645 100644 --- a/src/Gateway/Responses/PaymentResponse.php +++ b/src/Gateway/Responses/PaymentResponse.php @@ -20,7 +20,7 @@ class PaymentResponse extends GenericResponse public $gw; public $warnings; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { parent::__construct($rawDecoded); $this->acquirerDetails = !empty($rawDecoded['acquirer-details']) ? new AcquirerDetails($rawDecoded['acquirer-details']) : null; diff --git a/src/Gateway/Responses/RecurringTransactionsResponse.php b/src/Gateway/Responses/RecurringTransactionsResponse.php index 9dfd59b..6a2bdb9 100644 --- a/src/Gateway/Responses/RecurringTransactionsResponse.php +++ b/src/Gateway/Responses/RecurringTransactionsResponse.php @@ -17,7 +17,7 @@ class RecurringTransactionsResponse extends GenericResponse { public $transactions = []; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { parent::__construct($rawDecoded); if (!empty($rawDecoded['transactions']) && is_array($rawDecoded['transactions'])) { diff --git a/src/Gateway/Responses/RefundsResponse.php b/src/Gateway/Responses/RefundsResponse.php index 5a8fba5..dfa7db9 100644 --- a/src/Gateway/Responses/RefundsResponse.php +++ b/src/Gateway/Responses/RefundsResponse.php @@ -17,7 +17,7 @@ class RefundsResponse extends GenericResponse { public $transactions = []; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { parent::__construct($rawDecoded); if (!empty($rawDecoded['transactions']) && is_array($rawDecoded['transactions'])) { diff --git a/src/Gateway/Responses/ResultResponse.php b/src/Gateway/Responses/ResultResponse.php index cdbb02a..0bc4bfc 100644 --- a/src/Gateway/Responses/ResultResponse.php +++ b/src/Gateway/Responses/ResultResponse.php @@ -17,7 +17,7 @@ class ResultResponse extends GenericResponse { public $transactions = []; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { parent::__construct($rawDecoded); if (!empty($rawDecoded['transactions']) && is_array($rawDecoded['transactions'])) { diff --git a/src/Gateway/Responses/StatusResponse.php b/src/Gateway/Responses/StatusResponse.php index 3b9c166..bff906a 100644 --- a/src/Gateway/Responses/StatusResponse.php +++ b/src/Gateway/Responses/StatusResponse.php @@ -17,7 +17,7 @@ class StatusResponse extends GenericResponse { public $transactions = []; - public function __construct(array $rawDecoded = null) + public function __construct(?array $rawDecoded = null) { parent::__construct($rawDecoded); if (!empty($rawDecoded['transactions']) && is_array($rawDecoded['transactions'])) { diff --git a/src/Gateway/Validator/Validator.php b/src/Gateway/Validator/Validator.php index 0976c84..858df52 100644 --- a/src/Gateway/Validator/Validator.php +++ b/src/Gateway/Validator/Validator.php @@ -11,6 +11,7 @@ namespace TransactPro\Gateway\Validator; +use TransactPro\Gateway\DataSets\Command; use TransactPro\Gateway\DataSets\DataSet; use TransactPro\Gateway\Exceptions\ValidatorException; @@ -37,6 +38,7 @@ class Validator DataSet::COMMAND_DATA_CARD_VERIFICATION => 'integer', DataSet::COMMAND_DATA_PAYMENT_METHOD_DATA_SOURCE => 'integer', DataSet::COMMAND_DATA_PAYMENT_METHOD_DATA_TOKEN => 'string', + DataSet::COMMAND_DATA_PAYMENT_METHOD_TYPE => 'string', // general data DataSet::GENERAL_DATA_CUSTOMER_DATA_EMAIL => 'string', @@ -78,12 +80,17 @@ class Validator DataSet::PAYMENT_METHOD_DATA_EXPIRE => 'string', DataSet::PAYMENT_METHOD_DATA_CVV => 'string', DataSet::PAYMENT_METHOD_DATA_CARDHOLDER_NAME => 'string', - DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_DATA => 'string', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_PROTOCOL => 'string', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_DS_TRANS_ID => 'string', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_XID => 'string', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_CAVV => 'string', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_TRANS_STATUS => 'string', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM => 'string', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI => 'string', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS => 'string', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID => 'string', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID => 'string', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED => 'boolean', // money data DataSet::MONEY_DATA_AMOUNT => 'integer', @@ -137,18 +144,20 @@ public function validate(array $mandatory, array $data): bool for ($i = 0, $c = count($mandatory); $i < $c; $i++) { $key = $mandatory[$i]; - if (!isset($data[$key])) { + if (!$this->validateData($data, $key)) { $errors[] = "No value by \"$key\" is found."; continue; } - $value = $data[$key]; - $realType = gettype($value); - $expectedType = $this->fieldTypes[$key]; + if (isset($data[$key])) { + $value = $data[$key]; + $realType = gettype($value); + $expectedType = $this->fieldTypes[$key]; - if ($realType !== $expectedType) { - $errors[] = "Type of \"$value\" should be \"$expectedType\", but is \"$realType\""; - continue; + if ($realType !== $expectedType) { + $errors[] = "Type of \"$value\" should be \"$expectedType\", but is \"$realType\""; + continue; + } } } @@ -158,4 +167,31 @@ public function validate(array $mandatory, array $data): bool return true; } + + private function validateData(array $data, string $key): bool + { + if (isset($data[$key])) { + return true; + } + + $paymentMethodType = $data[DataSet::COMMAND_DATA_PAYMENT_METHOD_TYPE] ?? Command::PAYMENT_METHOD_TYPE_CARD; + if ($paymentMethodType !== Command::PAYMENT_METHOD_TYPE_CARD) { + static $cardDataKeys = [ + DataSet::PAYMENT_METHOD_DATA_PAN, + DataSet::PAYMENT_METHOD_DATA_EXPIRE, + DataSet::PAYMENT_METHOD_DATA_CVV, + ]; + + $tokenPassed = isset($data[DataSet::PAYMENT_METHOD_DATA_TOKEN]); + if ($tokenPassed) { + // encrypted token is passed intead of plain card data + return in_array($key, $cardDataKeys); + } else { + // decrypted token doesn't contain CVV + return $key === DataSet::PAYMENT_METHOD_DATA_CVV; + } + } + + return false; + } } diff --git a/tests/Gateway/DataSets/CommandTest.php b/tests/Gateway/DataSets/CommandTest.php index bb518dc..c180e01 100644 --- a/tests/Gateway/DataSets/CommandTest.php +++ b/tests/Gateway/DataSets/CommandTest.php @@ -22,6 +22,7 @@ public function testSuccess(): void DataSet::COMMAND_DATA_GATEWAY_TRANSACTION_ID => 'b', DataSet::COMMAND_DATA_TERMINAL_MID => 'c', DataSet::COMMAND_DATA_CARD_VERIFICATION => 123, + DataSet::COMMAND_DATA_PAYMENT_METHOD_TYPE => Command::PAYMENT_METHOD_TYPE_GOOGLE_PAY, ]; $command = new Command(); @@ -29,6 +30,7 @@ public function testSuccess(): void ->setGatewayTransactionID('b') ->setTerminalMID('c') ->setCardVerificationMode(123) + ->setPaymentMethodType(Command::PAYMENT_METHOD_TYPE_GOOGLE_PAY) ->getRaw(); $this->assertEquals($expected, $raw); diff --git a/tests/Gateway/DataSets/PaymentMethodTest.php b/tests/Gateway/DataSets/PaymentMethodTest.php index 16ac99d..549a5bb 100644 --- a/tests/Gateway/DataSets/PaymentMethodTest.php +++ b/tests/Gateway/DataSets/PaymentMethodTest.php @@ -22,11 +22,18 @@ public function testSuccess(): void DataSet::PAYMENT_METHOD_DATA_CVV => '123', DataSet::PAYMENT_METHOD_DATA_EXPIRE => '12/21', DataSet::PAYMENT_METHOD_DATA_CARDHOLDER_NAME => 'John Doe', + DataSet::PAYMENT_METHOD_DATA_TOKEN => 'dummy-token', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_PROTOCOL => '2.2.0', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_DS_TRANS_ID => '26221368-1c3d-4f3c-ba34-2efb76644c32', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_XID => 'b+f8duAy8jNTQ0DB4U3mSmPyp8s=', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_CAVV => 'kBMI/uGZvlKCygBkcQIlLJeBTPLG', DataSet::PAYMENT_METHOD_DATA_EXTERNAL_MPI_TRANS_STATUS => 'Y', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_CRYPTOGRAM => 'qqq', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ECI => '07', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_TRANS_STATUS => 'Y', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_DS_TRANS_ID => '123-qwe-asd', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_ACS_TRANS_ID => 'zxc-asd-qwe', + DataSet::PAYMENT_METHOD_DATA_EXTERNAL_TOKEN_AUTHENTICATED => true, ]; $paymentMethod = new PaymentMethod(); @@ -35,11 +42,18 @@ public function testSuccess(): void ->setCVV('123') ->setExpire('12/21') ->setCardHolderName('John Doe') + ->setToken('dummy-token') ->setExternalMpiProtocolVersion('2.2.0') ->setExternalMpiDsTransID('26221368-1c3d-4f3c-ba34-2efb76644c32') ->setExternalMpiXID('b+f8duAy8jNTQ0DB4U3mSmPyp8s=') ->setExternalMpiCAVV('kBMI/uGZvlKCygBkcQIlLJeBTPLG') ->setExternalMpiTransStatus('Y') + ->setExternalTokenCryptogram('qqq') + ->setExternalTokenECI('07') + ->setExternalTokenTransStatus('Y') + ->setExternalTokenDsTransId('123-qwe-asd') + ->setExternalTokenAcsTransId('zxc-asd-qwe') + ->setExternalTokenCardHolderAuthenticated(true) ->getRaw(); $this->assertEquals($expected, $raw);