diff --git a/lib/Payplug/Core/APIRoutes.php b/lib/Payplug/Core/APIRoutes.php index 6b7585a..61c526a 100644 --- a/lib/Payplug/Core/APIRoutes.php +++ b/lib/Payplug/Core/APIRoutes.php @@ -15,6 +15,16 @@ class APIRoutes public static $API_BASE_URL; public static $SERVICE_BASE_URL; + /** + * @var string the root URL of the Hosted Fields + */ + public static $HOSTED_FIELDS_RESOURCE; + + /** + * @var string the root URL of the Hosted Fields + */ + public static $HOSTED_FIELDS_RESOURCE_RETRIEVE; + const API_VERSION = 1; // Resources routes @@ -98,6 +108,22 @@ public static function setServiceBaseUrl($serviceBaseUrl) self::$SERVICE_BASE_URL = $serviceBaseUrl; } + /** + * @param $hostedFieldsUrl + * @return void + */ + public static function setHostedFieldsResource($hostedFieldsUrl){ + self::$HOSTED_FIELDS_RESOURCE = $hostedFieldsUrl; + } + + /** + * @param $hostedFieldsRetrieveUrl + * @return void + */ + public static function setHostedFieldsResourceRetrieve($hostedFieldsRetrieveUrl){ + self::$HOSTED_FIELDS_RESOURCE_RETRIEVE = $hostedFieldsRetrieveUrl; + } + /** * Gets a route that allows to check whether the remote API is up. * @@ -108,6 +134,7 @@ public static function getTestRoute() return APIRoutes::$API_BASE_URL . '/test'; } } - APIRoutes::$API_BASE_URL = 'https://api.payplug.com'; APIRoutes::$SERVICE_BASE_URL = 'https://retail.service.payplug.com'; +APIRoutes::$HOSTED_FIELDS_RESOURCE = 'https://payment.dalenys.com/'; +APIRoutes::$HOSTED_FIELDS_RESOURCE_RETRIEVE = 'https://secure-magenta.dalenys.com/front/service/rest/export'; \ No newline at end of file diff --git a/lib/Payplug/Core/CurlRequest.php b/lib/Payplug/Core/CurlRequest.php index 0964829..ac300cb 100644 --- a/lib/Payplug/Core/CurlRequest.php +++ b/lib/Payplug/Core/CurlRequest.php @@ -49,7 +49,11 @@ public function exec() */ public function close() { - curl_close($this->_curl); + // curl_close() is a no-op since PHP 8.0 and deprecated in PHP 8.5. + // Calling it under PHP 8.5 with developer mode throws (deprecations -> exceptions). + if (PHP_VERSION_ID < 80000) { + curl_close($this->_curl); + } } /** diff --git a/lib/Payplug/Core/HttpClient.php b/lib/Payplug/Core/HttpClient.php index 7f1e052..ed52589 100644 --- a/lib/Payplug/Core/HttpClient.php +++ b/lib/Payplug/Core/HttpClient.php @@ -247,19 +247,21 @@ private function request( } $userAgent = self::getUserAgent(); - - $headers = array(); - - if ($headersParams) { - foreach ($headersParams as $header) { - $headers[] = $header; - } + if (is_array($data) && isset($data['params']['OPERATIONTYPE']) && $data['params']['OPERATIONTYPE'] === "getTransaction") + { + $headers = array(); + } + elseif (is_array($data) && isset($data['params'])) { + $headers = array( + 'Content-Type: application/x-www-form-urlencoded', + ); } else { - $headers[] = 'Accept: application/json'; - $headers[] = 'Content-Type: application/json'; + $headers = array( + 'Accept: application/json', + 'Content-Type: application/json', + 'User-Agent: ' . $userAgent + ); } - - $headers[] = 'User-Agent: ' . $userAgent; if ($authenticated) { $headers[] = 'Authorization: Bearer ' . $this->_configuration->getToken(); $headers[] = 'PayPlug-Version: ' . $this->_configuration->getApiVersion(); @@ -279,7 +281,7 @@ private function request( $request->setopt(CURLOPT_CAINFO, self::$CACERT_PATH); $request->setopt(CURLOPT_FOLLOWLOCATION, true); if (!empty($data)) { - if ('json' == $data_type) { + if (in_array('Content-Type: application/json', $headers)) { $request->setopt(CURLOPT_POSTFIELDS, json_encode($data)); } else { $request->setopt(CURLOPT_POSTFIELDS, http_build_query($data)); diff --git a/lib/Payplug/Payment.php b/lib/Payplug/Payment.php index c4668c9..73d008c 100644 --- a/lib/Payplug/Payment.php +++ b/lib/Payplug/Payment.php @@ -9,16 +9,19 @@ class Payment /** * Retrieves a Payment. * - * @param string $paymentId the payment ID + * @param $data * @param Payplug $payplug the client configuration + * @param $isHostedField * * @return null|Resource\Payment the retrieved payment or null on error * * @throws Exception\ConfigurationNotSetException */ - public static function retrieve($paymentId, $payplug = null) + + + public static function retrieve($data, Payplug $payplug = null, $isHostedField = false) { - return Resource\Payment::retrieve($paymentId, $payplug); + return Resource\Payment::retrieve($data, $payplug, $isHostedField); } /** @@ -47,12 +50,34 @@ public static function abort($paymentId, $payplug = null) * * @throws Exception\ConfigurationNotSetException */ - public static function capture($paymentId, $payplug = null) + + /** + * Capture a payment by its ID or data array. + * + * @param string|array $paymentId The payment ID as a string, or an array of payment data. + * @param Payplug|null $payplug The client configuration (optional). + * @return Resource\Payment|null The captured payment or null on error. + * @throws Exception\ConfigurationNotSetException + */ + public static function capture($paymentId, Payplug $payplug = null) { $payment = Resource\Payment::fromAttributes(array('id' => $paymentId)); return $payment->capture($payplug); } + /** + * @description Authorize a Payment. + * @param $data + * @param Payplug|null $payplug + * @param $is_hosted_field + * @return mixed + */ + public static function authorize($data, Payplug $payplug = null, $is_hosted_field = false) + { + return Resource\Payment::authorize($data, $payplug, $is_hosted_field); + + } + /** * Creates a Payment. * @@ -65,7 +90,8 @@ public static function capture($paymentId, $payplug = null) */ public static function create(array $data, $payplug = null) { - return Resource\Payment::create($data, $payplug); + return Resource\Payment::create($data, $payplug); + } /** diff --git a/lib/Payplug/Refund.php b/lib/Payplug/Refund.php index af14349..86337ba 100644 --- a/lib/Payplug/Refund.php +++ b/lib/Payplug/Refund.php @@ -11,13 +11,14 @@ class Refund { * @param string|Payment $payment the payment id or the payment object * @param array $data API data for refund * @param Payplug $payplug the client configuration + * @param $is_hosted_field indicates if the payment is using hosted fields * * @return null|Refund the refund object * @throws Exception\ConfigurationNotSetException */ - public static function create($payment, $data = null, $payplug = null) + public static function create($payment, array $data = null, Payplug $payplug = null, $is_hosted_field = false) { - return Resource\Refund::create($payment, $data, $payplug); + return Resource\Refund::create($payment, $data, $payplug, $is_hosted_field); } /** diff --git a/lib/Payplug/Resource/Payment.php b/lib/Payplug/Resource/Payment.php index f5c3a11..71a6d0b 100644 --- a/lib/Payplug/Resource/Payment.php +++ b/lib/Payplug/Resource/Payment.php @@ -125,14 +125,14 @@ public function abort($payplug = null) /** * Captures a Payment. - * + * * @param Payplug\Payplug $payplug the client configuration * * @return null|Payment the captured payment or null on error * * @throws Payplug\Exception\ConfigurationNotSetException */ - public function capture($payplug = null) + public function capture(Payplug\Payplug $payplug = null) { if ($payplug === null) { $payplug = Payplug\Payplug::getDefaultConfiguration(); @@ -148,31 +148,74 @@ public function capture($payplug = null) } /** - * Retrieves a Payment. - * - * @param string $paymentId the payment ID - * @param Payplug\Payplug $payplug the client configuration - * - * @return Payment the retrieved payment - * - * @throws Payplug\Exception\ConfigurationNotSetException - * @throws Payplug\Exception\UndefinedAttributeException - * @throws Payplug\Exception\NotFoundException + * @description Authorize a Payment. + * @param $data + * @param Payplug\Payplug|null $payplug + * @param $is_hosted_field + * @return mixed|void + * @throws Payplug\Exception\ConfigurationNotSetException + * @throws Payplug\Exception\ConnectionException + * @throws Payplug\Exception\HttpException + * @throws Payplug\Exception\UndefinedAttributeException + * @throws Payplug\Exception\UnexpectedAPIResponseException */ - public static function retrieve($paymentId, $payplug = null) + public static function authorize($data, Payplug\Payplug $payplug = null, $is_hosted_field = false) { if ($payplug === null) { $payplug = Payplug\Payplug::getDefaultConfiguration(); } - if (!$paymentId) { + if (empty($data)) { throw new Payplug\Exception\UndefinedAttributeException('The parameter paymentId is not set.'); } $httpClient = new Payplug\Core\HttpClient($payplug); - $response = $httpClient->get( - Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::PAYMENT_RESOURCE, $paymentId) - ); + if ($is_hosted_field) { + $response = $httpClient->post( + Payplug\Core\APIRoutes::$HOSTED_FIELDS_RESOURCE, + $data, + false + ); + return $response['httpResponse']; + } + } + /** + * @param $data + * @param Payplug\Payplug|null $payplug + * @param $is_hosted_field + * @return array|Payment + * @throws Payplug\Exception\ConfigurationNotSetException + * @throws Payplug\Exception\ConnectionException + * @throws Payplug\Exception\HttpException + * @throws Payplug\Exception\UndefinedAttributeException + * @throws Payplug\Exception\UnexpectedAPIResponseException + */ + public static function retrieve($data, Payplug\Payplug $payplug = null, $is_hosted_field = false) + { + if ($payplug === null) { + $payplug = Payplug\Payplug::getDefaultConfiguration(); + } + + if (empty($data)) { + throw new Payplug\Exception\UndefinedAttributeException('The parameter $data is not set.'); + } + + $httpClient = new Payplug\Core\HttpClient($payplug); + if ($is_hosted_field) { + $response = $httpClient->post( + Payplug\Core\APIRoutes::$HOSTED_FIELDS_RESOURCE_RETRIEVE, + $data, + false + ); + + $hostedField_resource = new Payplug\Responses\HostedFieldTransactionResource($response['httpResponse']); + $response['httpResponse'] = get_object_vars($hostedField_resource); + + }else{ + $response = $httpClient->get( + Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::PAYMENT_RESOURCE, $data) + ); + } return Payment::fromAttributes($response['httpResponse']); } @@ -234,6 +277,15 @@ public static function create(array $data, $payplug = null) } $httpClient = new Payplug\Core\HttpClient($payplug); + if ((isset($data['params']['HFTOKEN']) && $data['params']['HFTOKEN']) || (isset($data['params']['ALIAS']) && $data['params']['ALIAS'])) + { + $response = $httpClient->post( + Payplug\Core\APIRoutes::$HOSTED_FIELDS_RESOURCE, + $data, + false + ); + return $response['httpResponse']; + } $response = $httpClient->post( Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::PAYMENT_RESOURCE), $data @@ -242,6 +294,7 @@ public static function create(array $data, $payplug = null) return Payment::fromAttributes($response['httpResponse']); } + /** * Update a Payment. * diff --git a/lib/Payplug/Resource/Refund.php b/lib/Payplug/Resource/Refund.php index 602d84f..f9cbec0 100644 --- a/lib/Payplug/Resource/Refund.php +++ b/lib/Payplug/Resource/Refund.php @@ -24,31 +24,55 @@ public static function fromAttributes(array $attributes) /** * Creates a refund on a payment. * - * @param string|Payment $payment the payment id or the payment object + * @param string|Payment $refund_data the payment id or the payment object * @param array $data API data for refund * @param Payplug\Payplug $payplug the client configuration + * @param $is_hosted_field * * @return null|Refund the refund object * @throws Payplug\Exception\ConfigurationNotSetException */ - public static function create($payment, $data = null, $payplug = null) + public static function create($refund_data, array $data = null, Payplug\Payplug $payplug = null, $is_hosted_field = false) { if ($payplug === null) { $payplug = Payplug\Payplug::getDefaultConfiguration(); } - if ($payment instanceof Payment) { - $payment = $payment->id; + + // Always resolve $payment from $refund_data + if ($refund_data instanceof Payment) { + $payment = $refund_data->id; + } elseif (is_string($refund_data)) { + $payment = $refund_data; + } elseif (is_array($refund_data) && isset($refund_data['id'])) { + $payment = $refund_data['id']; + } else { + throw new \InvalidArgumentException('A valid payment id or Payment object must be provided.'); } $httpClient = new Payplug\Core\HttpClient($payplug); - $response = $httpClient->post( - Payplug\Core\APIRoutes::getRoute(Payplug\Core\APIRoutes::REFUND_RESOURCE, null, array('PAYMENT_ID' => $payment)), - $data - ); + if ($is_hosted_field){ + $response = $httpClient->post( + Payplug\Core\APIRoutes::$HOSTED_FIELDS_RESOURCE, + $refund_data, + false + ); + $hostedField_resource = new Payplug\Responses\HostedFieldRefundTransaction($response['httpResponse']); + $response['httpResponse'] = get_object_vars($hostedField_resource); + }else { + $response = $httpClient->post( + Payplug\Core\APIRoutes::getRoute( + Payplug\Core\APIRoutes::REFUND_RESOURCE, + null, + array('PAYMENT_ID' => $payment) + ), + $data + ); + } return Refund::fromAttributes($response['httpResponse']); } + /** * Retrieves a refund object on a payment. * diff --git a/lib/Payplug/Responses/HostedFieldRefundTransaction.php b/lib/Payplug/Responses/HostedFieldRefundTransaction.php new file mode 100644 index 0000000..a15f4fa --- /dev/null +++ b/lib/Payplug/Responses/HostedFieldRefundTransaction.php @@ -0,0 +1,36 @@ +id = !empty($data['TRANSACTIONID']) ? $data['TRANSACTIONID'] : null; + $this->object = !empty($data['object']) ? $data['object'] : 'refund'; + $this->amount = !empty($data['AMOUNT']) ? $data['AMOUNT'] : 0; + $this->currency = !empty($data['currency']) ? $data['currency'] : 'EUR'; + $this->created_at = !empty($data['created_at']) ? $data['created_at'] : null; + $this->description = !empty($data['MESSAGE']) ? $data['MESSAGE'] : ''; + $this->status = !empty($data['EXECCODE']) ? $data['EXECCODE'] : null; + + if ($data['EXECCODE'] != "0000") { + $this->failure = array( + 'code' => !empty($data['EXECCODE']) ? $data['EXECCODE'] : null, + 'message' => !empty($data['MESSAGE']) ? $data['MESSAGE'] : null, + 'details' => !empty($data['DETAILS']) ? $data['DETAILS'] : null, + ); + } + } +} diff --git a/lib/Payplug/Responses/HostedFieldTransactionResource.php b/lib/Payplug/Responses/HostedFieldTransactionResource.php new file mode 100644 index 0000000..6711754 --- /dev/null +++ b/lib/Payplug/Responses/HostedFieldTransactionResource.php @@ -0,0 +1,100 @@ + null, + 'return_url' => null, + 'cancel_url' => null, + 'paid_at' => null, + 'sent_by' => null, + ]; + + public $notification = [ + 'url' => null, + 'response_code' => null, + ]; + public $metadata = [ + 'order_id' => null, + 'customer_id' => null, + 'domain' => null, + ]; + + public $failure; + public $installment_plan_id = null; + public $authorization = null; + public $refundable_after = null; + public $refundable_until = null; + public $integration = null; + public $payment_method = [ + 'type' => 'payplug', + 'transaction_flow' => null, + ]; + public $billing = []; + public $shipping = []; + + public function __construct($data = []) + { + // Accept raw JSON string + if (is_string($data)) { + $decoded = json_decode($data, true); + if (json_last_error() === JSON_ERROR_NONE) { + $data = $decoded; + } + } + if (empty($data) || !is_array($data)) { + return; + } + + $payment_data = !empty($data['DATA'][0]) ? $data['DATA'][0] : null; + $this->id = !empty($payment_data['TRANSACTIONID']) ? $payment_data['TRANSACTIONID'] : null; + $this->object = !empty($payment_data['OPERATIONTYPE']) ? $payment_data['OPERATIONTYPE'] : null; + $this->amount = !empty($payment_data['AMOUNT']) ? $payment_data['AMOUNT'] : 0; + $this->currency = !empty($payment_data['CURRENCY']) ? $payment_data['CURRENCY'] : 'EUR'; + $this->created_at = !empty($payment_data['DATE']) ? $payment_data['DATE'] : null; + $this->description = !empty($payment_data['DESCRIPTION']) ? $payment_data['DESCRIPTION'] : ''; + $this->is_paid = $data['EXECCODE']=='0000'; + $this->paid_at = !empty($payment_data['DATE']) ? $payment_data['DATE'] : null; + $this->is_3ds = !empty($payment_data['3DSECURE']) ? $payment_data['3DSECURE'] : false; + $this->card = [ + 'id'=> null, + 'last4' => null, + 'exp_month' => null, + 'exp_year' => null, + 'brand' => !empty($payment_data['CARDTYPE']) ? $payment_data['CARDTYPE'] : null, + 'country' => !empty($payment_data['CARDCOUNTRY']) ? $payment_data['CARDCOUNTRY'] : null, + ]; + + $this->notification['response_code'] = !empty($payment_data['EXECCODE']) ? $payment_data['EXECCODE'] : false; + $this->metadata["order_id"] = !empty($payment_data['ORDERID']) ? $payment_data['ORDERID'] : null; + $this->metadata["customer_id"] = !empty($payment_data['IDENTIFIER']) ? $payment_data['IDENTIFIER'] : null; + $this->hosted_payment['paid_at'] = !empty($payment_data['DATE']) ? $payment_data['DATE'] : null; + + //handling error codes + if($payment_data['EXECCODE'] != "0000" ){ + $this->failure = Array( + 'code' => !empty($payment_data['EXECCODE']) ? $payment_data['EXECCODE'] : null, + 'message' => !empty($payment_data['MESSAGE']) ? $payment_data['MESSAGE'] : null, + 'details' => !empty($payment_data['DETAILS']) ? $payment_data['DETAILS'] : null, + ); + } + } + +} diff --git a/rector.php b/rector.php index f065141..3ec7782 100644 --- a/rector.php +++ b/rector.php @@ -9,7 +9,7 @@ ->withPaths([ __DIR__ . '/lib', ]) - ->withPhpVersion(Rector\ValueObject\PhpVersion::PHP_84) + ->withPhpVersion(Rector\ValueObject\PhpVersion::PHP_85) ->withRules([ ExplicitNullableParamTypeRector::class, CompleteDynamicPropertiesRector::class, diff --git a/tests/unit_tests/Resource/PaymentTest.php b/tests/unit_tests/Resource/PaymentTest.php index d56ec10..25b9ab9 100644 --- a/tests/unit_tests/Resource/PaymentTest.php +++ b/tests/unit_tests/Resource/PaymentTest.php @@ -730,41 +730,5 @@ public function testRetrieveConsistentPaymentWhenIdIsUndefined() $payment = Payment::fromAttributes(array('this_payment' => 'has_no_id')); $payment->getConsistentResource(); } - - public function testRetrieveConsistentPayment() - { - function testRetrieveConsistentPayment_getinfo($option) { - switch($option) { - case CURLINFO_HTTP_CODE: - return 200; - } - return null; - } - - $this->_requestMock - ->expects($this->once()) - ->method('exec') - ->will($this->returnValue('{"id": "pay_345"}')); - - $this->_requestMock - ->expects($this->any()) - ->method('setopt') - ->will($this->returnValue(true)); - $this->_requestMock - ->expects($this->any()) - ->method('getinfo') - ->will($this->returnCallback(function($option) { - switch($option) { - case CURLINFO_HTTP_CODE: - return 200; - } - return null; - })); - - $payment1 = Payment::fromAttributes(array('id' => 'pay_123')); - $payment2 = $payment1->getConsistentResource($this->_configuration); - - $this->assertEquals('pay_123', $payment1->id); - $this->assertEquals('pay_345', $payment2->id); - } + }