diff --git a/src/Constants/IdStrings.php b/src/Constants/IdStrings.php index 662c9ea96..4634335b5 100755 --- a/src/Constants/IdStrings.php +++ b/src/Constants/IdStrings.php @@ -16,6 +16,7 @@ class IdStrings public const CHARGEBACK = 'cbk'; public const PAYOUT = 'out'; public const PREAUTHORIZE = 'preaut'; + public const SCA = 'sca'; public const SHIPMENT = 'shp'; // Payment Types diff --git a/src/Constants/TransactionTypes.php b/src/Constants/TransactionTypes.php index 3a4721b18..111bed024 100755 --- a/src/Constants/TransactionTypes.php +++ b/src/Constants/TransactionTypes.php @@ -18,4 +18,5 @@ class TransactionTypes public const SHIPMENT = 'shipment'; public const PAYOUT = 'payout'; public const CHARGEBACK = 'chargeback'; + public const SCA = 'sca'; } diff --git a/src/Interfaces/PaymentServiceInterface.php b/src/Interfaces/PaymentServiceInterface.php index d4613979e..ad05a505e 100644 --- a/src/Interfaces/PaymentServiceInterface.php +++ b/src/Interfaces/PaymentServiceInterface.php @@ -24,6 +24,7 @@ use UnzerSDK\Resources\TransactionTypes\Authorization; use UnzerSDK\Resources\TransactionTypes\Charge; use UnzerSDK\Resources\TransactionTypes\Payout; +use UnzerSDK\Resources\TransactionTypes\Sca; use UnzerSDK\Resources\TransactionTypes\Shipment; interface PaymentServiceInterface @@ -389,4 +390,20 @@ public function fetchInstallmentPlans( * @return PaylaterInstallmentPlans */ public function fetchPaylaterInstallmentPlans(InstallmentPlansQuery $plansRequest): PaylaterInstallmentPlans; + + /** + * Perform an SCA transaction. + * + * @param Sca $sca The SCA object. + * @param BasePaymentType|string $paymentType The payment type object or ID. + * @param Customer|string|null $customer The customer object or ID. + * @param Metadata|null $metadata The metadata object. + * @param Basket|null $basket The basket object. + * + * @return Sca The resulting SCA object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function performSca(Sca $sca, $paymentType, $customer = null, ?Metadata $metadata = null, ?Basket $basket = null): Sca; } diff --git a/src/Interfaces/ResourceServiceInterface.php b/src/Interfaces/ResourceServiceInterface.php index 6f26b0ad1..22c752c32 100644 --- a/src/Interfaces/ResourceServiceInterface.php +++ b/src/Interfaces/ResourceServiceInterface.php @@ -25,6 +25,7 @@ use UnzerSDK\Resources\TransactionTypes\Charge; use UnzerSDK\Resources\TransactionTypes\Chargeback; use UnzerSDK\Resources\TransactionTypes\Payout; +use UnzerSDK\Resources\TransactionTypes\Sca; use UnzerSDK\Resources\TransactionTypes\Shipment; interface ResourceServiceInterface @@ -452,4 +453,29 @@ public function fetchShipment($payment, string $shipmentId): Shipment; * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. */ public function fetchConfig(BasePaymentType $paymentType, ?Config $config = null): Config; + + /** + * Fetches an SCA transaction. + * + * @param Sca $sca The SCA object to fetch. + * + * @return Sca The fetched SCA object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function fetchSca(Sca $sca): Sca; + + /** + * Fetches an SCA transaction by payment ID and SCA ID. + * + * @param Payment|string $payment The payment object or payment ID. + * @param string $scaId The SCA transaction ID. + * + * @return Sca The fetched SCA object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function fetchScaById($payment, string $scaId): Sca; } diff --git a/src/Resources/Payment.php b/src/Resources/Payment.php index 4a75656b4..65dd979bf 100755 --- a/src/Resources/Payment.php +++ b/src/Resources/Payment.php @@ -19,6 +19,7 @@ use UnzerSDK\Resources\TransactionTypes\Chargeback; use UnzerSDK\Resources\TransactionTypes\Payout; use UnzerSDK\Resources\TransactionTypes\PreAuthorization; +use UnzerSDK\Resources\TransactionTypes\Sca; use UnzerSDK\Resources\TransactionTypes\Shipment; use UnzerSDK\Services\IdService; use UnzerSDK\Traits\HasInvoiceId; @@ -58,6 +59,9 @@ class Payment extends AbstractUnzerResource /** @var Chargeback[] $chargebacks */ private $chargebacks = []; + /** @var Sca|null $sca */ + private $sca; + /** * Associative array using the ID of the cancellations as the key. @@ -337,6 +341,32 @@ public function getChargeByIndex(int $index, bool $lazy = false) return $resource; } + /** + * Returns the SCA transaction of this Payment. + */ + public function getSca(bool $lazy = false): ?Sca + { + $sca = $this->sca; + if (!$lazy && $sca !== null) { + return $this->getResource($sca); + } + return $sca; + } + + /** + * Adds an SCA object to this Payment. + * + * @param Sca $sca + * + * @return $this + */ + public function setSca(Sca $sca): self + { + $sca->setPayment($this); + $this->sca = $sca; + return $this; + } + /** * Reference this payment object to the passed Customer resource. * The Customer resource can be passed as Customer object or the Id of a Customer resource. @@ -924,6 +954,9 @@ private function updateResponseTransactions(array $transactions = []): void case TransactionTypes::CHARGEBACK: $this->updateChargebackTransaction($transaction); break; + case TransactionTypes::SCA: + $this->updateScaTransaction($transaction); + break; default: // skip break; @@ -1191,4 +1224,25 @@ private function updateChargebackTransaction(stdClass $transaction): void $chargeback->handleResponse($transaction); } + + /** + * This updates the local SCA object referenced by this Payment with the given SCA transaction + * from the Payment response. + * + * @param stdClass $transaction The transaction from the Payment response containing the SCA data. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + private function updateScaTransaction(stdClass $transaction): void + { + $transactionId = IdService::getResourceIdFromUrl($transaction->url, IdStrings::SCA); + $sca = $this->sca; + if (!$sca instanceof Sca) { + $sca = (new Sca())->setPayment($this)->setId($transactionId); + $this->setSca($sca); + } + + $sca->handleResponse($transaction); + } } diff --git a/src/Resources/TransactionTypes/AbstractTransactionType.php b/src/Resources/TransactionTypes/AbstractTransactionType.php index 70bd0802e..05093caf9 100755 --- a/src/Resources/TransactionTypes/AbstractTransactionType.php +++ b/src/Resources/TransactionTypes/AbstractTransactionType.php @@ -39,6 +39,9 @@ abstract class AbstractTransactionType extends AbstractUnzerResource use HasAdditionalTransactionData; use HasDate; + /** @var float $amount */ + protected $amount; + /** @var Payment $payment */ private $payment; @@ -242,4 +245,23 @@ protected function handleWeroTransactionData(stdClass $additionalTransactionData $this->setWeroTransactionData($weroTransactionData); } } + + /** + * @param float|null $amount + * + * @return Charge + */ + public function setAmount(?float $amount): self + { + $this->amount = $amount !== null ? round($amount, 4) : null; + return $this; + } + + /** + * @return float|null + */ + public function getAmount(): ?float + { + return $this->amount; + } } diff --git a/src/Resources/TransactionTypes/Authorization.php b/src/Resources/TransactionTypes/Authorization.php index 6936b8b84..4df8d113c 100755 --- a/src/Resources/TransactionTypes/Authorization.php +++ b/src/Resources/TransactionTypes/Authorization.php @@ -62,25 +62,6 @@ public function __construct(?float $amount = null, ?string $currency = null, ?st $this->setReturnUrl($returnUrl); } - /** - * @return float|null - */ - public function getAmount(): ?float - { - return $this->amount; - } - - /** - * @param float|null $amount - * - * @return self - */ - public function setAmount(?float $amount): self - { - $this->amount = $amount !== null ? round($amount, 4) : null; - return $this; - } - /** * @return float|null */ diff --git a/src/Resources/TransactionTypes/Cancellation.php b/src/Resources/TransactionTypes/Cancellation.php index ad41d1c22..51f0d86e0 100755 --- a/src/Resources/TransactionTypes/Cancellation.php +++ b/src/Resources/TransactionTypes/Cancellation.php @@ -16,13 +16,6 @@ */ class Cancellation extends AbstractTransactionType { - /** - * The cancellation amount will be transferred as grossAmount in case of Installment Secured payment type. - * - * @var float $amount - */ - protected $amount; - /** @var string $reasonCode */ protected $reasonCode; @@ -53,29 +46,6 @@ public function __construct(?float $amount = null) $this->setAmount($amount); } - /** - * Returns the cancellationAmount (equals grossAmount in case of Installment Secured). - * - * @return float|null - */ - public function getAmount(): ?float - { - return $this->amount; - } - - /** - * Sets the cancellationAmount (equals grossAmount in case of Installment Secured). - * - * @param float|null $amount - * - * @return Cancellation - */ - public function setAmount(?float $amount): self - { - $this->amount = $amount !== null ? round($amount, 4) : null; - return $this; - } - /** * Returns the reason code of the cancellation if set. * diff --git a/src/Resources/TransactionTypes/Charge.php b/src/Resources/TransactionTypes/Charge.php index 278cc9c3b..338bca79d 100755 --- a/src/Resources/TransactionTypes/Charge.php +++ b/src/Resources/TransactionTypes/Charge.php @@ -25,9 +25,6 @@ class Charge extends AbstractTransactionType use HasDescriptor; use HasChargebacks; - /** @var float $amount */ - protected $amount; - /** @var string $currency */ protected $currency; @@ -54,25 +51,6 @@ public function __construct(?float $amount = null, ?string $currency = null, ?st $this->setReturnUrl($returnUrl); } - /** - * @return float|null - */ - public function getAmount(): ?float - { - return $this->amount; - } - - /** - * @param float|null $amount - * - * @return self - */ - public function setAmount(?float $amount): self - { - $this->amount = $amount !== null ? round($amount, 4) : null; - return $this; - } - /** * @return float|null */ diff --git a/src/Resources/TransactionTypes/Chargeback.php b/src/Resources/TransactionTypes/Chargeback.php index 2ef22d038..607ebe7a5 100644 --- a/src/Resources/TransactionTypes/Chargeback.php +++ b/src/Resources/TransactionTypes/Chargeback.php @@ -12,9 +12,6 @@ */ class Chargeback extends AbstractTransactionType { - /** @var float $amount */ - protected $amount; - /** @var string $currency */ protected $currency; @@ -29,27 +26,6 @@ public function __construct(?float $amount = null) $this->setAmount($amount); } - /** - * @return float|null - */ - public function getAmount(): ?float - { - return $this->amount; - } - - /** - * Sets the cancellationAmount (equals grossAmount in case of Installment Secured). - * - * @param float|null $amount - * - * @return Cancellation - */ - public function setAmount(?float $amount): self - { - $this->amount = $amount !== null ? round($amount, 4) : null; - return $this; - } - /** * @return string */ diff --git a/src/Resources/TransactionTypes/Payout.php b/src/Resources/TransactionTypes/Payout.php index 15f0767ca..2f39d5a0d 100644 --- a/src/Resources/TransactionTypes/Payout.php +++ b/src/Resources/TransactionTypes/Payout.php @@ -12,9 +12,6 @@ */ class Payout extends AbstractTransactionType { - /** @var float|null $amount */ - protected $amount; - /** @var string|null $currency */ protected $currency; @@ -31,25 +28,6 @@ public function __construct(?float $amount = null, ?string $currency = null, $re $this->setReturnUrl($returnUrl); } - /** - * @return float|null - */ - public function getAmount(): ?float - { - return $this->amount; - } - - /** - * @param float|null $amount - * - * @return self - */ - public function setAmount(?float $amount): self - { - $this->amount = $amount !== null ? round($amount, 4) : null; - return $this; - } - /** * @return string|null */ diff --git a/src/Resources/TransactionTypes/Sca.php b/src/Resources/TransactionTypes/Sca.php new file mode 100644 index 000000000..7c6b2a180 --- /dev/null +++ b/src/Resources/TransactionTypes/Sca.php @@ -0,0 +1,131 @@ +setAmount($amount); + $this->setCurrency($currency); + $this->setReturnUrl($returnUrl); + } + + /** + * @return string|null + */ + public function getCurrency(): ?string + { + return $this->currency; + } + + /** + * @param string|null $currency + * + * @return self + */ + public function setCurrency(?string $currency): self + { + $this->currency = $currency; + return $this; + } + + /** + * @return string|null + */ + public function getReturnUrl(): ?string + { + return $this->returnUrl; + } + + /** + * @param string|null $returnUrl + * + * @return self + */ + public function setReturnUrl(?string $returnUrl): self + { + $this->returnUrl = $returnUrl; + return $this; + } + + /** + * @return string|null + */ + public function getPaymentReference(): ?string + { + return $this->paymentReference; + } + + /** + * @param string|null $referenceText + * + * @return Sca + */ + public function setPaymentReference(?string $referenceText): Sca + { + $this->paymentReference = $referenceText; + return $this; + } + + /** + * @return bool|null + */ + public function isCard3ds(): ?bool + { + return $this->card3ds; + } + + /** + * @param bool|null $card3ds + * + * @return Sca + */ + public function setCard3ds(?bool $card3ds): Sca + { + $this->card3ds = $card3ds; + return $this; + } + + /** + * {@inheritDoc} + */ + protected function getResourcePath(string $httpMethod = HttpAdapterInterface::REQUEST_GET): string + { + return 'sca'; + } +} diff --git a/src/Resources/TransactionTypes/Shipment.php b/src/Resources/TransactionTypes/Shipment.php index 55dfbc7d5..d29e09cc8 100755 --- a/src/Resources/TransactionTypes/Shipment.php +++ b/src/Resources/TransactionTypes/Shipment.php @@ -12,28 +12,6 @@ */ class Shipment extends AbstractTransactionType { - /** @var float|null $amount */ - protected $amount; - - /** - * @return float|null - */ - public function getAmount(): ?float - { - return $this->amount; - } - - /** - * @param float|null $amount - * - * @return Shipment - */ - public function setAmount(?float $amount): self - { - $this->amount = $amount !== null ? round($amount, 4) : null; - return $this; - } - /** * {@inheritDoc} */ diff --git a/src/Services/PaymentService.php b/src/Services/PaymentService.php index 8eefc3597..de6954115 100755 --- a/src/Services/PaymentService.php +++ b/src/Services/PaymentService.php @@ -22,6 +22,7 @@ use UnzerSDK\Resources\TransactionTypes\Authorization; use UnzerSDK\Resources\TransactionTypes\Charge; use UnzerSDK\Resources\TransactionTypes\Payout; +use UnzerSDK\Resources\TransactionTypes\Sca; use UnzerSDK\Resources\TransactionTypes\Shipment; use UnzerSDK\Unzer; @@ -392,4 +393,32 @@ private function createPayment($paymentType): AbstractUnzerResource { return (new Payment($this->unzer))->setPaymentType($paymentType); } + + /** + * Perform an SCA transaction. + * + * @param Sca $sca The SCA object to be executed. + * @param BasePaymentType|string $paymentType The payment type object or its ID. + * @param Customer|string|null $customer The customer object or ID. + * @param Metadata|null $metadata The metadata object. + * @param Basket|null $basket The basket object. + * + * @return Sca The resulting SCA object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function performSca(Sca $sca, $paymentType, $customer = null, ?Metadata $metadata = null, ?Basket $basket = null): Sca + { + $payment = $this->createPayment($paymentType); + $paymentType = $payment->getPaymentType(); + + /** @var Sca $sca */ + $sca->setSpecialParams($paymentType->getTransactionParams() ?? []); + $payment->setSca($sca)->setCustomer($customer)->setMetadata($metadata)->setBasket($basket); + + $this->getResourceService()->createResource($sca); + + return $sca; + } } diff --git a/src/Services/ResourceService.php b/src/Services/ResourceService.php index 16821523e..90b095782 100755 --- a/src/Services/ResourceService.php +++ b/src/Services/ResourceService.php @@ -59,6 +59,7 @@ use UnzerSDK\Resources\TransactionTypes\Charge; use UnzerSDK\Resources\TransactionTypes\Chargeback; use UnzerSDK\Resources\TransactionTypes\Payout; +use UnzerSDK\Resources\TransactionTypes\Sca; use UnzerSDK\Resources\TransactionTypes\Shipment; use UnzerSDK\Resources\V2\Customer as CustomerV2; use UnzerSDK\Resources\V2\Paypage as PaypageV2; @@ -222,6 +223,12 @@ public function fetchResourceByUrl($url) case $resourceType === IdStrings::PAYOUT: $resource = $unzer->fetchPayout(IdService::getResourceIdFromUrl($url, IdStrings::PAYMENT)); break; + case $resourceType === IdStrings::SCA: + $resource = $unzer->fetchScaById( + IdService::getResourceIdFromUrl($url, IdStrings::PAYMENT), + $resourceId + ); + break; case $resourceType === IdStrings::PAYMENT: $resource = $unzer->fetchPayment($resourceId); break; @@ -980,4 +987,44 @@ public static function getTypeInstanceFromIdString($typeId): BasePaymentType } return $paymentType; } + + /** + * Fetches an SCA transaction. + * + * @param Sca $sca The SCA object to fetch. + * + * @return Sca The fetched SCA object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function fetchSca(Sca $sca): Sca + { + $this->fetchResource($sca); + return $sca; + } + + /** + * Fetches an SCA transaction by payment ID and SCA ID. + * + * @param Payment|string $payment The payment object or payment ID. + * @param string $scaId The SCA transaction ID. + * + * @return Sca The fetched SCA object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function fetchScaById($payment, string $scaId): Sca + { + $paymentObject = $this->fetchPayment($payment); + $sca = $paymentObject->getSca($scaId, true); + + if (!$sca instanceof Sca) { + throw new RuntimeException('The SCA object could not be found.'); + } + + $this->fetchResource($sca); + return $sca; + } } diff --git a/src/Unzer.php b/src/Unzer.php index a0bb6570c..e0f8b45d1 100644 --- a/src/Unzer.php +++ b/src/Unzer.php @@ -33,6 +33,7 @@ use UnzerSDK\Resources\TransactionTypes\Charge; use UnzerSDK\Resources\TransactionTypes\Chargeback; use UnzerSDK\Resources\TransactionTypes\Payout; +use UnzerSDK\Resources\TransactionTypes\Sca; use UnzerSDK\Resources\TransactionTypes\Shipment; use UnzerSDK\Resources\V2\Paypage as PaypageV2; use UnzerSDK\Resources\Webhook; @@ -812,6 +813,126 @@ public function performChargeOnPayment($payment, Charge $charge): Charge return $this->paymentService->performChargeOnPayment($payment, $charge); } + /** + * Perform an SCA transaction. + * + * @param Sca $sca The SCA object. + * @param BasePaymentType|string $paymentType The payment type object or ID. + * @param Customer|string|null $customer The customer object or ID. + * @param Metadata|null $metadata The metadata object. + * @param Basket|null $basket The basket object. + * + * @return Sca The resulting SCA object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function performSca(Sca $sca, $paymentType, $customer = null, ?Metadata $metadata = null, ?Basket $basket = null): Sca + { + return $this->paymentService->performSca($sca, $paymentType, $customer, $metadata, $basket); + } + + /** + * Fetch an SCA transaction. + * + * @param Sca $sca The SCA object to fetch. + * + * @return Sca The fetched SCA object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function fetchSca(Sca $sca): Sca + { + return $this->resourceService->fetchSca($sca); + } + + /** + * Fetch an SCA transaction by payment ID and SCA ID. + * + * @param Payment|string $payment The payment object or payment ID. + * @param string $scaId The SCA transaction ID. + * + * @return Sca The fetched SCA object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function fetchScaById($payment, string $scaId): Sca + { + return $this->resourceService->fetchScaById($payment, $scaId); + } + + /** + * Charge an SCA transaction. + * + * @param Payment|string $payment The payment object or payment ID. + * @param string $scaId The SCA transaction ID. + * @param float|null $amount The amount to charge. + * @param string|null $orderId The order ID. + * @param string|null $invoiceId The invoice ID. + * + * @return Charge The resulting Charge object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function chargeScaTransaction($payment, string $scaId, ?float $amount = null, ?string $orderId = null, ?string $invoiceId = null): Charge + { + $charge = new Charge($amount); + $charge->setOrderId($orderId)->setInvoiceId($invoiceId); + + $paymentObject = $this->resourceService->getPaymentResource($payment); + $sca = $paymentObject->getSca($scaId, true); + + if (!$sca instanceof Sca) { + throw new RuntimeException('SCA transaction not found.'); + } + + $sca->addCharge($charge); + $charge->setPayment($paymentObject); + $charge->setParentResource($sca); + + $this->resourceService->createResource($charge); + + return $charge; + } + + /** + * Authorize an SCA transaction. + * + * @param Payment|string $payment The payment object or payment ID. + * @param string $scaId The SCA transaction ID. + * @param float|null $amount The amount to authorize. + * @param string|null $orderId The order ID. + * @param string|null $invoiceId The invoice ID. + * + * @return Authorization The resulting Authorization object. + * + * @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK. + */ + public function authorizeScaTransaction($payment, string $scaId, ?float $amount = null, ?string $orderId = null, ?string $invoiceId = null): Authorization + { + $authorization = new Authorization($amount, null, null); + $authorization->setOrderId($orderId)->setInvoiceId($invoiceId); + + $paymentObject = $this->resourceService->getPaymentResource($payment); + $sca = $paymentObject->getSca($scaId, true); + + if (!$sca instanceof Sca) { + throw new RuntimeException('SCA transaction not found.'); + } + + $sca->addAuthorization($authorization); + $authorization->setPayment($paymentObject); + $authorization->setParentResource($sca); + + $this->resourceService->createResource($authorization); + + return $authorization; + } + /** * {@inheritDoc} */ diff --git a/test/Fixtures/jsonData/scaPaymentResponse.json b/test/Fixtures/jsonData/scaPaymentResponse.json new file mode 100644 index 000000000..f897f5a69 --- /dev/null +++ b/test/Fixtures/jsonData/scaPaymentResponse.json @@ -0,0 +1,33 @@ +{ + "id": "s-sca-1", + "isSuccess": true, + "isPending": false, + "isResumed": false, + "isError": false, + "card3ds": true, + "redirectUrl": "", + "message": { + "code": "COR.000.100.200", + "merchant": "Transaction succeeded", + "customer": "Your payments have been successfully processed." + }, + "amount": "119.0000", + "currency": "EUR", + "date": "2023-02-28 08:00:00", + "resources": { + "customerId": "", + "paymentId": "s-pay-123", + "basketId": "", + "metadataId": "", + "payPageId": "", + "traceId": "trace-123", + "typeId": "s-crd-123" + }, + "paymentReference": "", + "processing": { + "uniqueId": "31HA0xyz", + "shortId": "1234.1234.1234", + "3dsEci": "3DS_SUCCESS", + "traceId": "trace-123" + } +} \ No newline at end of file diff --git a/test/Fixtures/jsonData/scaResponse.json b/test/Fixtures/jsonData/scaResponse.json new file mode 100644 index 000000000..f897f5a69 --- /dev/null +++ b/test/Fixtures/jsonData/scaResponse.json @@ -0,0 +1,33 @@ +{ + "id": "s-sca-1", + "isSuccess": true, + "isPending": false, + "isResumed": false, + "isError": false, + "card3ds": true, + "redirectUrl": "", + "message": { + "code": "COR.000.100.200", + "merchant": "Transaction succeeded", + "customer": "Your payments have been successfully processed." + }, + "amount": "119.0000", + "currency": "EUR", + "date": "2023-02-28 08:00:00", + "resources": { + "customerId": "", + "paymentId": "s-pay-123", + "basketId": "", + "metadataId": "", + "payPageId": "", + "traceId": "trace-123", + "typeId": "s-crd-123" + }, + "paymentReference": "", + "processing": { + "uniqueId": "31HA0xyz", + "shortId": "1234.1234.1234", + "3dsEci": "3DS_SUCCESS", + "traceId": "trace-123" + } +} \ No newline at end of file diff --git a/test/integration/TransactionTypes/ScaTest.php b/test/integration/TransactionTypes/ScaTest.php new file mode 100644 index 000000000..664967c6e --- /dev/null +++ b/test/integration/TransactionTypes/ScaTest.php @@ -0,0 +1,189 @@ +unzer->createPaymentType($this->createCardObject()); + $sca = new Sca(100.0, 'EUR', self::RETURN_URL); + $sca = $this->unzer->performSca($sca, $paymentType->getId()); + + $this->assertTransactionResourceHasBeenCreated($sca); + $this->assertInstanceOf(Payment::class, $sca->getPayment()); + $this->assertNotEmpty($sca->getPayment()->getId()); + $this->assertEquals(self::RETURN_URL, $sca->getReturnUrl()); + + return $sca; + } + + /** + * Verify SCA transaction can be performed with payment type object. + * + * @test + */ + public function scaShouldWorkWithTypeObject(): void + { + /** @var Card $paymentType */ + $paymentType = $this->unzer->createPaymentType($this->createCardObject()); + $sca = new Sca(100.0, 'EUR', self::RETURN_URL); + $sca = $this->unzer->performSca($sca, $paymentType); + + $this->assertTransactionResourceHasBeenCreated($sca); + $this->assertInstanceOf(Payment::class, $sca->getPayment()); + $this->assertNotEmpty($sca->getPayment()->getId()); + $this->assertEquals(self::RETURN_URL, $sca->getReturnUrl()); + } + + /** + * Verify SCA transaction accepts all parameters. + * + * @test + */ + public function scaShouldAcceptAllParameters(): Sca + { + // prepare test data + /** @var Card $paymentType */ + $paymentType = $this->unzer->createPaymentType($this->createCardObject()); + $customer = $this->getMinimalCustomer(); + $metadata = (new Metadata())->addMetadata('key', 'value'); + $basket = $this->createBasket(); + $paymentReference = 'scaPaymentReference'; + + // perform request + $sca = new Sca(119.0, 'EUR', self::RETURN_URL); + $sca->setRecurrenceType(RecurrenceTypes::ONE_CLICK, $paymentType); + $sca->setPaymentReference($paymentReference); + $sca->setCard3ds(true); + + $sca = $this->unzer->performSca($sca, $paymentType, $customer, $metadata, $basket); + + // verify the data sent and received match + $payment = $sca->getPayment(); + $this->assertSame($paymentType, $payment->getPaymentType()); + $this->assertEquals(119.0, $sca->getAmount()); + $this->assertEquals('EUR', $sca->getCurrency()); + $this->assertEquals(self::RETURN_URL, $sca->getReturnUrl()); + $this->assertSame($customer, $payment->getCustomer()); + $this->assertSame($metadata, $payment->getMetadata()); + $this->assertSame($basket, $payment->getBasket()); + $this->assertEquals($paymentReference, $sca->getPaymentReference()); + } + + /** + * Verify SCA transaction can be fetched. + * + * @test + * @depends scaShouldWorkWithTypeId + */ + public function scaTransactionCanBeFetched($sca): void + { + $this->assertTransactionResourceHasBeenCreated($sca); + + // Fetch the SCA transaction + $fetchedSca = $this->unzer->fetchScaById($sca->getPaymentId(), $sca->getId()); + + // Verify the fetched transaction matches the initial transaction + $this->assertEquals($sca->getId(), $fetchedSca->getId()); + $this->assertEquals($sca->getPaymentId(), $fetchedSca->getPaymentId()); + $this->assertEquals($sca->getAmount(), $fetchedSca->getAmount()); + $this->assertEquals($sca->getCurrency(), $fetchedSca->getCurrency()); + } + + /** + * Verify SCA transaction can be fetched using Sca object. + * + * @test + */ + public function scaTransactionCanBeFetchedUsingSca(): void + { + // Create SCA transaction + /** @var Card $paymentType */ + $paymentType = $this->unzer->createPaymentType($this->createCardObject()); + $sca = new Sca(100.0, 'EUR', self::RETURN_URL); + $sca = $this->unzer->performSca($sca, $paymentType); + + $this->assertTransactionResourceHasBeenCreated($sca); + + // Fetch the SCA transaction using the Sca object + $fetchedSca = $this->unzer->fetchSca($sca); + + // Verify the fetched transaction matches the initial transaction + $this->assertEquals($sca->getId(), $fetchedSca->getId()); + $this->assertEquals($sca->getAmount(), $fetchedSca->getAmount()); + $this->assertEquals($sca->getCurrency(), $fetchedSca->getCurrency()); + } + + /** + * Verify charge can be performed on SCA transaction. + * + * @test + */ + public function chargeCanBePerformedOnScaTransaction(): void + { + // Create SCA transaction + /** @var Card $paymentType */ + $paymentType = $this->unzer->createPaymentType($this->createCardObject()); + $sca = new Sca(100.0, 'EUR', self::RETURN_URL); + $sca = $this->unzer->performSca($sca, $paymentType); + + $this->assertTransactionResourceHasBeenCreated($sca); + + // Perform charge on SCA transaction + $charge = $this->unzer->chargeScaTransaction($sca->getPayment(), $sca->getId(), 50.0); + + $this->assertTransactionResourceHasBeenCreated($charge); + $this->assertInstanceOf(Charge::class, $charge); + $this->assertEquals(50.0, $charge->getAmount()); + $this->assertEquals($sca->getPayment()->getId(), $charge->getPayment()->getId()); + } + + /** + * Verify authorize can be performed on SCA transaction. + * + * @test + */ + public function authorizeCanBePerformedOnScaTransaction(): void + { + // Create SCA transaction + /** @var Card $paymentType */ + $paymentType = $this->unzer->createPaymentType($this->createCardObject()); + $sca = new Sca(100.0, 'EUR', self::RETURN_URL); + $sca = $this->unzer->performSca($sca, $paymentType); + + $this->assertTransactionResourceHasBeenCreated($sca); + + // Perform authorize on SCA transaction + $authorization = $this->unzer->authorizeScaTransaction($sca->getPayment(), $sca->getId(), 75.0); + + $this->assertTransactionResourceHasBeenCreated($authorization); + $this->assertInstanceOf(Authorization::class, $authorization); + $this->assertEquals(75.0, $authorization->getAmount()); + $this->assertEquals($sca->getPayment()->getId(), $authorization->getPayment()->getId()); + } +} diff --git a/test/unit/Resources/TransactionTypes/ScaTest.php b/test/unit/Resources/TransactionTypes/ScaTest.php new file mode 100644 index 000000000..b587880c3 --- /dev/null +++ b/test/unit/Resources/TransactionTypes/ScaTest.php @@ -0,0 +1,42 @@ +assertEquals(99.99, $sca->getAmount()); + $this->assertEquals("EUR", $sca->getCurrency()); + $this->assertEquals("https://return-url.com", $sca->getReturnUrl()); + } + + /** + * @test + */ + public function getterSetterTest() + { + $sca = new Sca(99.99, "EUR", "https://return-url.com"); + $sca->setPaymentReference('reference') + ->setCard3ds(false); + $this->assertEquals(99.99, $sca->getAmount()); + $this->assertEquals("EUR", $sca->getCurrency()); + $this->assertEquals("https://return-url.com", $sca->getReturnUrl()); + } +} diff --git a/test/unit/Services/ResourceServiceTest.php b/test/unit/Services/ResourceServiceTest.php index 1bcc7086b..6cb6de5cd 100755 --- a/test/unit/Services/ResourceServiceTest.php +++ b/test/unit/Services/ResourceServiceTest.php @@ -1391,7 +1391,8 @@ public function fetchResourceByUrlShouldFetchTheDesiredResourceDP(): array 'v2Customer' => ['fetchCustomer', ['s-cst-123e4567-e89b-12d3-a456-426614174000'], 'https://api.unzer.com/v2/customers/s-cst-123e4567-e89b-12d3-a456-426614174000'], 'v1Basket' => ['fetchBasket', ['s-bsk-1254'], 'https://api.unzer.com/v1/baskets/s-bsk-1254/'], 'v2Basket' => ['fetchBasket', ['s-bsk-1254'], 'https://api.unzer.com/v2/baskets/s-bsk-1254/'], - 'Payout' => ['fetchPayout', ['s-pay-100746'], 'https://api.unzer.com/v1/payments/s-pay-100746/payout/s-out-1/'] + 'Payout' => ['fetchPayout', ['s-pay-100746'], 'https://api.unzer.com/v1/payments/s-pay-100746/payout/s-out-1/'], + 'Sca' => ['fetchScaById', ['s-pay-100746', 's-sca-1'], 'https://api.unzer.com/v1/payments/s-pay-100746/sca/s-sca-1/'] ]; }