Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 21 additions & 19 deletions src/Adapter/ApplepayAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,27 @@ class ApplepayAdapter
private $request;

/**
* @param string $merchantValidationURL URL for merchant validation request
* @param ApplepaySession $applePaySession Containing applepay session data.
* @param string $merchantValidationCertificatePath Path to merchant identification certificate
* @param string|null $merchantValidationCertificateKeyChainPath
* @param string $merchantValidationURL URL for merchant validation request
* @param ApplepaySession $applePaySession Containing applepay session data.
*
* @return string|null
*
* @throws ApplepayMerchantValidationException
*/
public function validateApplePayMerchant(
string $merchantValidationURL,
ApplepaySession $applePaySession,
string $merchantValidationCertificatePath,
?string $merchantValidationCertificateKeyChainPath = null
ApplepaySession $applePaySession
): ?string {
if (!$this->validMerchantValidationDomain($merchantValidationURL)) {
throw new ApplepayMerchantValidationException('Invalid URL used for merchantValidation request.');
throw new ApplepayMerchantValidationException("Invalid URL used for merchantValidation request.");
}
if ($this->request === null) {
throw new ApplepayMerchantValidationException('No curl adapter initiated yet. Make sure to cal init() function before.');
}
$payload = $applePaySession->jsonSerialize();
$this->init(
$merchantValidationURL,
$payload,
$merchantValidationCertificatePath,
$merchantValidationCertificateKeyChainPath
);
$this->setOption(CURLOPT_URL, $merchantValidationURL);
$this->setOption(CURLOPT_POSTFIELDS, $payload);

$sessionResponse = $this->execute();
$this->close();
return $sessionResponse;
Expand All @@ -71,6 +67,7 @@ public function validateApplePayMerchant(
*
* @param string $merchantValidationURL URL used for merchant validation request.
*
* @return bool
*/
public function validMerchantValidationDomain(string $merchantValidationURL): bool
{
Expand All @@ -81,18 +78,20 @@ public function validMerchantValidationDomain(string $merchantValidationURL): bo
}

/**
* {@inheritDoc}
* @param string $sslCert Path to merchant identification certificate.
* @param string|null $sslKey Path to merchant identification key file.
* This is necessary if the ssl cert file doesn't contain key already.
* @param string|null $caCert Path to CA certificate.
*/
public function init($url, $payload, $sslCert, $caCert = null): void
public function init(string $sslCert, string $sslKey = null, string $caCert = null): void
{
$timeout = EnvironmentService::getTimeout();
$curlVerbose = EnvironmentService::isCurlVerbose();

$this->request = curl_init($url);
$this->request = curl_init();
$this->setOption(CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$this->setOption(CURLOPT_POST, 1);
$this->setOption(CURLOPT_DNS_USE_GLOBAL_CACHE, false);
$this->setOption(CURLOPT_POSTFIELDS, $payload);
$this->setOption(CURLOPT_FAILONERROR, false);
$this->setOption(CURLOPT_TIMEOUT, $timeout);
$this->setOption(CURLOPT_CONNECTTIMEOUT, $timeout);
Expand All @@ -104,7 +103,10 @@ public function init($url, $payload, $sslCert, $caCert = null): void
$this->setOption(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);

$this->setOption(CURLOPT_SSLCERT, $sslCert);
if (isset($caCert)) {
if (isset($sslKey) && !empty($sslKey)) {
$this->setOption(CURLOPT_SSLKEY, $sslKey);
}
if (isset($caCert) && !empty($caCert)) {
$this->setOption(CURLOPT_CAINFO, $caCert);
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/Services/EnvironmentService.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class EnvironmentService
public const ENV_VAR_TEST_PRIVATE_KEY_NON_3DS = 'UNZER_PAPI_TEST_PRIVATE_KEY_NON_3DS';
public const ENV_VAR_TEST_PUBLIC_KEY_NON_3DS = 'UNZER_PAPI_TEST_PUBLIC_KEY_NON_3DS';

public const ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE = 'UNZER_APPLE_MERCHANT_CERTIFICATE_PATH';
public const ENV_VAR_TEST_APPLE_MERCHANT_ID_FOLDER = 'UNZER_APPLE_MERCHANT_ID_PATH';
public const ENV_VAR_TEST_APPLE_CA_CERTIFICATE = 'UNZER_APPLE_CA_CERTIFICATE_PATH';

private const ENV_VAR_NAME_TIMEOUT = 'UNZER_PAPI_TIMEOUT';
Expand Down Expand Up @@ -142,13 +142,13 @@ public static function getTestPublicKey($non3ds = false): string
}

/**
* Returns the apple merchant certificate path set via environment variable.
* Returns the path to apple merchant ID folder set via environment variable.
*
* @return string
*/
public static function getAppleMerchantCertificatePath(): string
public static function getAppleMerchantIdPath(): string
{
return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE] ?? '');
return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_ID_FOLDER] ?? '');
}

/**
Expand Down
136 changes: 106 additions & 30 deletions test/integration/ApplepayAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,74 +34,143 @@

class ApplepayAdapterTest extends BaseIntegrationTest
{
/** @var string $appleCaCertificatePath Path to ca Certificate file. */
protected $appleCaCertificatePath;
/** @var string $merchantValidationUrl merchant validation url for testing. */
private $merchantValidationUrl;
private $merchantValidationCertificatePath;
private $appleCaCertificatePath;

public function domainShouldBeValidatedCorrectlyDP()
{
return [
'invalid: example.domain.com' => ['https://example.domain.com', false],
'valid: https://apple-pay-gateway.apple.com/some/path' => ['https://apple-pay-gateway.apple.com/some/path', true],
'valid: https://cn-apple-pay-gateway.apple.com' => ['https://cn-apple-pay-gateway.apple.com', true],
'invalid: apple-pay-gateway-nc-pod1.apple.com' => ['apple-pay-gateway-nc-pod1.apple.com', false],
'invalid: (empty)' => ['', false],
];
}
/** @var string $applepayCombinedCertPath Path to combined certificate file. */
private $applepayCombinedCertPath;
/** @var string $applepayCertPath Path to merchant ID certificate file. */
private $applepayCertPath;
/** @var string $applepayKeyPath Path to merchant ID key file. */
private $applepayKeyPath;

/**
* @inheritDoc
*/
protected function setUp(): void
{
$this->merchantValidationUrl = 'https://apple-pay-gateway-cert.apple.com/paymentservices/startSession';
$this->merchantValidationCertificatePath = EnvironmentService::getAppleMerchantCertificatePath();

$appleMerchantIdPath = EnvironmentService::getAppleMerchantIdPath();
$this->applepayCertPath = $appleMerchantIdPath . 'merchant_id.pem';
$this->applepayKeyPath = $appleMerchantIdPath . 'merchant_id.key';
$this->applepayCombinedCertPath = $appleMerchantIdPath . 'apple-pay-cert.pem';
$this->appleCaCertificatePath = EnvironmentService::getAppleCaCertificatePath();
}

/**
* test merchant validation request.
* Test merchant validation request.
*
* @test
*
* @throws \UnzerSDK\Exceptions\ApplepayMerchantValidationException
* @throws ApplepayMerchantValidationException
*/
public function verifyMerchantValidationRequest(): void
{
$applepaySession = $this->createApplepaySession();
$appleAdapter = new ApplepayAdapter();
$appleAdapter->init($this->applepayCertPath, $this->applepayKeyPath, $this->appleCaCertificatePath);

$validationResponse = $appleAdapter->validateApplePayMerchant(
$this->merchantValidationUrl,
$applepaySession,
$this->merchantValidationCertificatePath,
$this->appleCaCertificatePath
$applepaySession
);

$this->assertNotNull($validationResponse);
}

/**
* test merchant validation request without ca certificate.
* @return ApplepaySession
*/
private function createApplepaySession(): ApplepaySession
{
return new ApplepaySession('merchantIdentifier', 'displayName', 'domainName');
}

/**
* Test merchant validation request without ca certificate.
*
* @test
*
* @throws \UnzerSDK\Exceptions\ApplepayMerchantValidationException
* @throws ApplepayMerchantValidationException
*/
public function merchantValidationWorksWithoutCaCert(): void
public function merchantValidationWorksWithApplepayCertOnly(): void
{
$applepaySession = $this->createApplepaySession();
$appleAdapter = new ApplepayAdapter();
$appleAdapter->init($this->applepayCombinedCertPath);

$validationResponse = $appleAdapter->validateApplePayMerchant(
$this->merchantValidationUrl,
$applepaySession,
$this->merchantValidationCertificatePath
$applepaySession
);

$this->assertNotNull($validationResponse);
}

/**
* Test merchant validation request without ca certificate.
*
* @test
*
* @throws ApplepayMerchantValidationException
*/
public function merchantValidationWorksWithCertAndKey(): void
{
$applepaySession = $this->createApplepaySession();
$appleAdapter = new ApplepayAdapter();
$appleAdapter->init($this->applepayCertPath, $this->applepayKeyPath);

$validationResponse = $appleAdapter->validateApplePayMerchant(
$this->merchantValidationUrl,
$applepaySession
);

$this->assertNotNull($validationResponse);
}

/**
* Test merchant validation request without key and only the merchant id certificate should throw exception.
*
* @test
*
* @throws ApplepayMerchantValidationException
*/
public function missingKeyShouldThrowException(): void
{
$applepaySession = $this->createApplepaySession();
$appleAdapter = new ApplepayAdapter();
$appleAdapter->init($this->applepayCertPath);

$this->expectException(ApplepayMerchantValidationException::class);
$appleAdapter->validateApplePayMerchant(
$this->merchantValidationUrl,
$applepaySession
);
}

/**
* Test merchant validation request without init() call should throw exception.
*
* @test
*
* @throws ApplepayMerchantValidationException
*/
public function missingInitCallThrowsException(): void
{
$applepaySession = $this->createApplepaySession();
$appleAdapter = new ApplepayAdapter();

$this->expectException(ApplepayMerchantValidationException::class);
$this->expectExceptionMessage('No curl adapter initiated yet. Make sure to cal init() function before.');

$appleAdapter->validateApplePayMerchant(
$this->merchantValidationUrl,
$applepaySession
);
}

/**
* Merchant validation call should throw Exception if domain of Validation url is invalid.
*
Expand All @@ -112,14 +181,14 @@ public function merchantValidationThrowsErrorForInvalidDomain(): void
{
$applepaySession = $this->createApplepaySession();
$appleAdapter = new ApplepayAdapter();
$appleAdapter->init($this->applepayCombinedCertPath);

$this->expectException(ApplepayMerchantValidationException::class);
$this->expectExceptionMessage('Invalid URL used for merchantValidation request.');

$appleAdapter->validateApplePayMerchant(
'https://invalid.domain.com/some/path',
$applepaySession,
$this->merchantValidationCertificatePath
$applepaySession
);
}

Expand All @@ -141,11 +210,18 @@ public function domainShouldBeValidatedCorrectly($validationUrl, $expectedResult
$this->assertEquals($expectedResult, $domainValidation);
}

/**
* @return ApplepaySession
/** Provides different urls to test domain validation.
*
* @return array[]
*/
private function createApplepaySession(): ApplepaySession
public function domainShouldBeValidatedCorrectlyDP(): array
{
return new ApplepaySession('merchantIdentifier', 'displayName', 'domainName');
return [
'invalid: example.domain.com' => ['https://example.domain.com', false],
'valid: https://apple-pay-gateway.apple.com/some/path' => ['https://apple-pay-gateway.apple.com/some/path', true],
'valid: https://cn-apple-pay-gateway.apple.com' => ['https://cn-apple-pay-gateway.apple.com', true],
'invalid: apple-pay-gateway-nc-pod1.apple.com' => ['apple-pay-gateway-nc-pod1.apple.com', false],
'invalid: (empty)' => ['', false],
];
}
}