The payment transaction has been completed, however the payment is pending.
In some cases (e. g. authorization transaction or invoice payments) this is normal.
diff --git a/examples/Success.php b/examples/Success.php
index d7f3c0da..22125534 100755
--- a/examples/Success.php
+++ b/examples/Success.php
@@ -29,7 +29,7 @@
-
Success
+
Success
The payment has been successfully completed.
-
+
Try
@@ -76,7 +76,7 @@
You can set a Card type to recurring in order to register it and charge later as well as implement recurring payments.
-
+
Try
@@ -88,7 +88,7 @@
-
+
Try
@@ -100,7 +100,7 @@
-
+
Try
@@ -112,7 +112,7 @@
-
+
Try
@@ -124,7 +124,7 @@
-
+
Try
@@ -136,7 +136,7 @@
-
+
Try
@@ -148,7 +148,7 @@
-
+
Try
@@ -160,7 +160,7 @@
-
+
Try
@@ -172,7 +172,7 @@
-
+
Try
@@ -186,7 +186,7 @@
Please refer to the example of Invoice Factoring if you don't want to add the customer via payment form.
-
+
Try
@@ -200,7 +200,7 @@
Please refer to the example of Invoice guaranteed if you want to add the customer data withing the payment form.
-
+
Try
@@ -213,7 +213,7 @@
You can try authorize and direct charge.
-
+
Try
@@ -226,7 +226,7 @@
You can set a Pay Pal type to recurring in order to register it and charge later as well as implement recurring payments.
-
+
Try
@@ -238,7 +238,7 @@
-
+
Try
@@ -250,7 +250,7 @@
-
+
Try
@@ -262,7 +262,7 @@
-
+
Try
@@ -274,7 +274,7 @@
-
+
Try
@@ -292,7 +292,7 @@
Documentation
-
+
Try
@@ -309,7 +309,7 @@
Documentation
-
+
Try
diff --git a/examples/log/.gitkeep b/examples/log/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/src/Constants/ApiResponseCodes.php b/src/Constants/ApiResponseCodes.php
index 76392b8c..fc6d5b08 100755
--- a/src/Constants/ApiResponseCodes.php
+++ b/src/Constants/ApiResponseCodes.php
@@ -66,6 +66,7 @@ class ApiResponseCodes
const API_ERROR_AMOUNT_IS_MISSING = 'API.340.200.130';
const API_ERROR_CUSTOMER_DOES_NOT_EXIST = 'API.410.100.100';
const API_ERROR_CUSTOMER_ID_ALREADY_EXISTS = 'API.410.200.010';
+ const API_ERROR_ADDRESS_NAME_TO_LONG = 'API.410.200.031';
const API_ERROR_CUSTOMER_CAN_NOT_BE_FOUND = 'API.500.100.100';
const API_ERROR_REQUEST_DATA_IS_INVALID = 'API.500.300.999';
const API_ERROR_RECURRING_PAYMENT_NOT_SUPPORTED = 'API.500.550.004';
diff --git a/src/Heidelpay.php b/src/Heidelpay.php
index 500a08e2..828a747c 100755
--- a/src/Heidelpay.php
+++ b/src/Heidelpay.php
@@ -64,7 +64,7 @@ class Heidelpay implements HeidelpayParentInterface, PaymentServiceInterface, Re
const BASE_URL = 'api.heidelpay.com';
const API_VERSION = 'v1';
const SDK_TYPE = 'HeidelpayPHP';
- const SDK_VERSION = '1.2.6.0';
+ const SDK_VERSION = '1.2.7.0';
/** @var string $key */
private $key;
@@ -97,14 +97,16 @@ class Heidelpay implements HeidelpayParentInterface, PaymentServiceInterface, Re
* Construct a new heidelpay object.
*
* @param string $key The private key your received from your heidelpay contact person.
- * @param string $locale The locale of the customer defining defining the translation.
+ * @param string $locale The locale of the customer defining defining the translation (e.g. 'en-GB' or 'de-DE').
+ *
+ * @link https://docs.heidelpay.com/docs/web-integration#section-localization-and-languages
*
* @throws RuntimeException A RuntimeException will be thrown if the key is not of type private.
*/
public function __construct($key, $locale = null)
{
$this->setKey($key);
- $this->locale = $locale;
+ $this->setLocale($locale);
$this->resourceService = new ResourceService($this);
$this->paymentService = new PaymentService($this);
@@ -158,14 +160,14 @@ public function getLocale()
/**
* Sets the customer locale.
*
- * @param string $locale The customer locale to set.
- * Refer to the documentation under https://docs.heidelpay.com for a list of supported values.
+ * @param string|null $locale The customer locale to set.
+ * Ref. https://docs.heidelpay.com for a list of supported values.
*
* @return Heidelpay This heidelpay object.
*/
public function setLocale($locale): Heidelpay
{
- $this->locale = $locale;
+ $this->locale = str_replace('_', '-', $locale);
return $this;
}
@@ -1022,7 +1024,7 @@ public function debugLog($message)
if ($this->isDebugMode()) {
$debugHandler = $this->getDebugHandler();
if ($debugHandler instanceof DebugHandlerInterface) {
- $debugHandler->log($message);
+ $debugHandler->log('(' . (string)(getmypid()) . ') ' . $message);
}
}
}
diff --git a/src/Resources/PaymentTypes/BasePaymentType.php b/src/Resources/PaymentTypes/BasePaymentType.php
index 6b75aa14..a5ecec47 100755
--- a/src/Resources/PaymentTypes/BasePaymentType.php
+++ b/src/Resources/PaymentTypes/BasePaymentType.php
@@ -28,6 +28,16 @@
abstract class BasePaymentType extends AbstractHeidelpayResource
{
+ /**
+ * Return true for invoice types.
+ *
+ * @return bool
+ */
+ public function isInvoiceType(): bool
+ {
+ return false;
+ }
+
//
/**
diff --git a/src/Resources/PaymentTypes/Invoice.php b/src/Resources/PaymentTypes/Invoice.php
index 432c8d58..a2a0aa7c 100755
--- a/src/Resources/PaymentTypes/Invoice.php
+++ b/src/Resources/PaymentTypes/Invoice.php
@@ -25,8 +25,10 @@
namespace heidelpayPHP\Resources\PaymentTypes;
use heidelpayPHP\Traits\CanDirectCharge;
+use heidelpayPHP\Traits\IsInvoiceType;
class Invoice extends BasePaymentType
{
use CanDirectCharge;
+ use IsInvoiceType;
}
diff --git a/src/Resources/PaymentTypes/InvoiceFactoring.php b/src/Resources/PaymentTypes/InvoiceFactoring.php
index 2aff7ac0..d35207ef 100755
--- a/src/Resources/PaymentTypes/InvoiceFactoring.php
+++ b/src/Resources/PaymentTypes/InvoiceFactoring.php
@@ -25,8 +25,10 @@
namespace heidelpayPHP\Resources\PaymentTypes;
use heidelpayPHP\Traits\CanDirectChargeWithCustomer;
+use heidelpayPHP\Traits\IsInvoiceType;
class InvoiceFactoring extends BasePaymentType
{
use CanDirectChargeWithCustomer;
+ use IsInvoiceType;
}
diff --git a/src/Resources/PaymentTypes/InvoiceGuaranteed.php b/src/Resources/PaymentTypes/InvoiceGuaranteed.php
index 71b29a90..2f4a2547 100755
--- a/src/Resources/PaymentTypes/InvoiceGuaranteed.php
+++ b/src/Resources/PaymentTypes/InvoiceGuaranteed.php
@@ -25,8 +25,10 @@
namespace heidelpayPHP\Resources\PaymentTypes;
use heidelpayPHP\Traits\CanDirectChargeWithCustomer;
+use heidelpayPHP\Traits\IsInvoiceType;
class InvoiceGuaranteed extends BasePaymentType
{
use CanDirectChargeWithCustomer;
+ use IsInvoiceType;
}
diff --git a/src/Services/HttpService.php b/src/Services/HttpService.php
index deab41ba..c659321e 100755
--- a/src/Services/HttpService.php
+++ b/src/Services/HttpService.php
@@ -78,7 +78,7 @@ public function setHttpAdapter(HttpAdapterInterface $httpAdapter): HttpService
public function getEnvironmentService(): EnvironmentService
{
if (!$this->environmentService instanceof EnvironmentService) {
- $this->environmentService = new EnvironmentService();
+ $this->environmentService = new EnvironmentService();
}
return $this->environmentService;
}
@@ -119,9 +119,10 @@ public function send(
$heidelpayObj = $resource->getHeidelpayObject();
// perform request
- $url = $this->getEnvironmentUrl($uri);
+ $url = $this->getEnvironmentUrl($uri);
$payload = $resource->jsonSerialize();
- $this->initRequest($heidelpayObj, $url, $payload, $httpMethod);
+ $headers = $this->composerHttpHeaders($heidelpayObj);
+ $this->initRequest($url, $payload, $httpMethod, $headers);
$httpAdapter = $this->getAdapter();
$response = $httpAdapter->execute();
$responseCode = $httpAdapter->getResponseCode();
@@ -131,7 +132,7 @@ public function send(
try {
$this->handleErrors($responseCode, $response);
} finally {
- $this->debugLog($heidelpayObj, $payload, $responseCode, $httpMethod, $url, $response);
+ $this->debugLog($heidelpayObj, $payload, $headers, $responseCode, $httpMethod, $url, $response);
}
return $response;
@@ -140,31 +141,18 @@ public function send(
/**
* Initializes and returns the http request object.
*
- * @param Heidelpay $heidelpay
- * @param string $uri
- * @param string $payload
- * @param string $httpMethod
+ * @param string $uri
+ * @param string $payload
+ * @param string $httpMethod
+ * @param $httpHeaders
*
* @throws RuntimeException
*/
- private function initRequest(Heidelpay $heidelpay, $uri, $payload, $httpMethod)
+ private function initRequest($uri, $payload, $httpMethod, $httpHeaders)
{
$httpAdapter = $this->getAdapter();
$httpAdapter->init($uri, $payload, $httpMethod);
$httpAdapter->setUserAgent(Heidelpay::SDK_TYPE);
-
- // Set HTTP-headers
- $locale = $heidelpay->getLocale();
- $key = $heidelpay->getKey();
- $httpHeaders = [
- 'Authorization' => 'Basic ' . base64_encode($key . ':'),
- 'Content-Type' => 'application/json',
- 'SDK-VERSION' => Heidelpay::SDK_VERSION,
- 'SDK-TYPE' => Heidelpay::SDK_TYPE
- ];
- if (!empty($locale)) {
- $httpHeaders['Accept-Language'] = $locale;
- }
$httpAdapter->setHeaders($httpHeaders);
}
@@ -185,15 +173,15 @@ private function handleErrors($responseCode, $response)
$responseObject = json_decode($response, false);
if ($responseCode >= 400 || isset($responseObject->errors)) {
- $code = null;
- $errorId = null;
+ $code = null;
+ $errorId = null;
$customerMessage = $code;
$merchantMessage = $customerMessage;
if (isset($responseObject->errors[0])) {
- $errors = $responseObject->errors[0];
+ $errors = $responseObject->errors[0];
$merchantMessage = $errors->merchantMessage ?? '';
$customerMessage = $errors->customerMessage ?? '';
- $code = $errors->code ?? '';
+ $code = $errors->code ?? '';
}
if (isset($responseObject->id)) {
$errorId = $responseObject->id;
@@ -209,15 +197,24 @@ private function handleErrors($responseCode, $response)
/**
* @param Heidelpay $heidelpayObj
* @param string $payload
+ * @param mixed $headers
* @param int $responseCode
* @param string $httpMethod
* @param string $url
* @param string|null $response
*/
- public function debugLog(Heidelpay $heidelpayObj, $payload, $responseCode, $httpMethod, string $url, $response)
- {
+ public function debugLog(
+ Heidelpay $heidelpayObj,
+ $payload,
+ $headers,
+ $responseCode,
+ $httpMethod,
+ string $url,
+ $response
+ ) {
$heidelpayObj->debugLog($httpMethod . ': ' . $url);
$writingOperations = [HttpAdapterInterface::REQUEST_POST, HttpAdapterInterface::REQUEST_PUT];
+ $heidelpayObj->debugLog('Headers: ' . json_encode($headers, JSON_UNESCAPED_SLASHES));
if (in_array($httpMethod, $writingOperations, true)) {
$heidelpayObj->debugLog('Request: ' . $payload);
}
@@ -250,4 +247,26 @@ private function getEnvironmentUrl($uri): string
$envUrl[] = Heidelpay::BASE_URL;
return 'https://' . implode('-', $envUrl) . '/' . Heidelpay::API_VERSION . $uri;
}
+
+ /**
+ * @param Heidelpay $heidelpay
+ *
+ * @return array
+ */
+ public function composerHttpHeaders(Heidelpay $heidelpay): array
+ {
+ $locale = $heidelpay->getLocale();
+ $key = $heidelpay->getKey();
+ $httpHeaders = [
+ 'Authorization' => 'Basic ' . base64_encode($key . ':'),
+ 'Content-Type' => 'application/json',
+ 'SDK-VERSION' => Heidelpay::SDK_VERSION,
+ 'SDK-TYPE' => Heidelpay::SDK_TYPE
+ ];
+ if (!empty($locale)) {
+ $httpHeaders['Accept-Language'] = $locale;
+ }
+
+ return $httpHeaders;
+ }
}
diff --git a/src/Traits/IsInvoiceType.php b/src/Traits/IsInvoiceType.php
new file mode 100644
index 00000000..3d67a0cf
--- /dev/null
+++ b/src/Traits/IsInvoiceType.php
@@ -0,0 +1,36 @@
+
+ *
+ * @package heidelpayPHP\Traits
+ */
+namespace heidelpayPHP\Traits;
+
+trait IsInvoiceType
+{
+ /**
+ * Return true for invoice types.
+ */
+ public function isInvoiceType(): bool
+ {
+ return true;
+ }
+}
diff --git a/test/integration/CustomerTest.php b/test/integration/CustomerTest.php
index cc6d5e0d..d99338ef 100755
--- a/test/integration/CustomerTest.php
+++ b/test/integration/CustomerTest.php
@@ -369,6 +369,30 @@ public function customerShouldBeFetchedByCustomerIdAndUpdatedIfItAlreadyExists()
$this->assertEquals($customer->getId(), $newCustomerData->getId());
}
+ /**
+ * Verify customer address can take a name as long as both first and lastname concatenated.
+ *
+ * @test
+ *
+ * @throws HeidelpayApiException A HeidelpayApiException 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 addressNameCanHoldFirstAndLastNameConcatenated()
+ {
+ $customerId = 'customer' . self::generateRandomId();
+ $customer = $this->getMaximumCustomerInclShippingAddress()->setCustomerId($customerId);
+ $longName = 'firstfirstfirstfirstfirstfirstfirstfirst lastlastlastlastlastlastlastlastlastlast';
+ $customer->getShippingAddress()->setName($longName);
+ $this->heidelpay->createCustomer($customer);
+ $this->assertEquals($longName, $customer->getShippingAddress()->getName());
+
+ $veryLongName = $longName . 'X';
+ $customer->getShippingAddress()->setName($veryLongName);
+ $this->expectException(HeidelpayApiException::class);
+ $this->expectExceptionCode(ApiResponseCodes::API_ERROR_ADDRESS_NAME_TO_LONG);
+ $this->heidelpay->updateCustomer($customer);
+ }
+
//
//
diff --git a/test/integration/ExceptionTest.php b/test/integration/ExceptionTest.php
index 82759712..008f359a 100755
--- a/test/integration/ExceptionTest.php
+++ b/test/integration/ExceptionTest.php
@@ -60,7 +60,7 @@ public function apiExceptionShouldHoldClientMessage()
}
try {
- $this->heidelpay->setLocale('de_DE');
+ $this->heidelpay->setLocale('de-DE');
$this->heidelpay->authorize(1.0, 'EUR', $giropay, self::RETURN_URL);
} catch (HeidelpayApiException $e) {
$this->assertInstanceOf(HeidelpayApiException::class, $e);
diff --git a/test/unit/HeidelpayTest.php b/test/unit/HeidelpayTest.php
index f393abf8..fc864eaa 100755
--- a/test/unit/HeidelpayTest.php
+++ b/test/unit/HeidelpayTest.php
@@ -68,11 +68,11 @@ public function constructorShouldInitPropertiesProperly()
$this->assertEquals('s-priv-1234', $heidelpay->getKey());
$this->assertEquals(null, $heidelpay->getLocale());
- $heidelpaySwiss = new Heidelpay('s-priv-1234', 'de_CH');
- $this->assertEquals('de_CH', $heidelpaySwiss->getLocale());
+ $heidelpaySwiss = new Heidelpay('s-priv-1234', 'de-CH');
+ $this->assertEquals('de-CH', $heidelpaySwiss->getLocale());
- $heidelpayGerman = new Heidelpay('s-priv-1234', 'de_DE');
- $this->assertEquals('de_DE', $heidelpayGerman->getLocale());
+ $heidelpayGerman = new Heidelpay('s-priv-1234', 'de-DE');
+ $this->assertEquals('de-DE', $heidelpayGerman->getLocale());
}
/**
diff --git a/test/unit/Services/HttpServiceTest.php b/test/unit/Services/HttpServiceTest.php
index 71a61b45..712ff2c3 100755
--- a/test/unit/Services/HttpServiceTest.php
+++ b/test/unit/Services/HttpServiceTest.php
@@ -188,28 +188,44 @@ public function sendShouldLogDebugMessagesIfDebugModeAndHandlerAreSet()
{
$httpServiceMock = $this->getMockBuilder(HttpService::class)->setMethods(['getAdapter'])->getMock();
- $adapterMock = $this->getMockBuilder(CurlAdapter::class)->setMethods(
- ['init', 'setUserAgent', 'setHeaders', 'execute', 'getResponseCode', 'close']
- )->getMock();
+ $adapterMock = $this->getMockBuilder(CurlAdapter::class)->setMethods(['init', 'setUserAgent', 'setHeaders', 'execute', 'getResponseCode', 'close'])->getMock();
$adapterMock->method('execute')->willReturn('{"response":"myResponseString"}');
$adapterMock->method('getResponseCode')->willReturnOnConsecutiveCalls('200', '201');
$httpServiceMock->method('getAdapter')->willReturn($adapterMock);
$loggerMock = $this->getMockBuilder(DummyDebugHandler::class)->setMethods(['log'])->getMock();
- $loggerMock->expects($this->exactly(5))->method('log')->withConsecutive(
+ $loggerMock->expects($this->exactly(7))->method('log')->withConsecutive(
[ $this->callback(
static function ($string) {
- return str_replace(['dev-api', 'stg-api'], 'api', $string) === 'GET: https://api.heidelpay.com/v1/my/uri/123';
+ return str_replace(['dev-api', 'stg-api'], 'api', $string) === '(' . (string)(getmypid()) . ') GET: https://api.heidelpay.com/v1/my/uri/123';
})
],
- ['Response: (200) {"response":"myResponseString"}'],
[ $this->callback(
static function ($string) {
- return str_replace(['dev-api', 'stg-api'], 'api', $string) === 'POST: https://api.heidelpay.com/v1/my/uri/123';
+ $matches = [];
+ preg_match('/^(?:\([\d]*\) Headers: )({.*})/', $string, $matches);
+ $elements = json_decode($matches[1], true);
+ return array_key_exists('Authorization', $elements) && array_key_exists('Content-Type', $elements) &&
+ array_key_exists('SDK-TYPE', $elements) && array_key_exists('SDK-VERSION', $elements);
+ })
+ ],
+ ['(' . (string)(getmypid()) . ') Response: (200) {"response":"myResponseString"}'],
+ [ $this->callback(
+ static function ($string) {
+ return str_replace(['dev-api', 'stg-api'], 'api', $string) === '(' . (string)(getmypid()) . ') POST: https://api.heidelpay.com/v1/my/uri/123';
+ })
+ ],
+ [ $this->callback(
+ static function ($string) {
+ $matches = [];
+ preg_match('/^(?:\([\d]*\) Headers: )({.*})/', $string, $matches);
+ $elements = json_decode($matches[1], true);
+ return array_key_exists('Authorization', $elements) && array_key_exists('Content-Type', $elements) &&
+ array_key_exists('SDK-TYPE', $elements) && array_key_exists('SDK-VERSION', $elements);
})
],
- ['Request: {"dummyResource": "JsonSerialized"}'],
- ['Response: (201) {"response":"myResponseString"}']
+ ['(' . (string)(getmypid()) . ') Request: {"dummyResource": "JsonSerialized"}'],
+ ['(' . (string)(getmypid()) . ') Response: (201) {"response":"myResponseString"}']
);
/** @var DebugHandlerInterface $loggerMock */