Skip to content

Commit f471e6e

Browse files
OctopusDeploy release: 14.0.3
1 parent 85a4373 commit f471e6e

File tree

11 files changed

+624
-73
lines changed

11 files changed

+624
-73
lines changed

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
<img src="https://developer.globalpay.com/static/media/logo.db1c4126172e20a5c31cf9d5150cc88a.svg" alt="Global Payments logo" title="Global Payments" align="right" width="225" />
33
</a>
44

5-
# Changelog
6-
## Latest Version v14.0.2 (11/20/25)
5+
## Latest Version v14.0.3 (12/04/25)
76
### Enhancements:
7+
- [Portico] Add EMVChipCondition element support
88
- [GPAPI] Enable GP-API access using Portico credentials
99

10+
## v14.0.2 (11/20/25)
11+
### Enhancements:
12+
- [Portico] Bug fix cert endpoint ignored when using production credentials
13+
1014
## v14.0.1 (11/13/25)
1115
### Enhancements:
1216
- [GPAPI] Added ContractReference field in Stored Credential

metadata.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<xml>
2-
<releaseNumber>14.0.2</releaseNumber>
2+
<releaseNumber>14.0.3</releaseNumber>
33
</xml>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace GlobalPayments\Api\Entities\Enums;
4+
5+
/**
6+
* EMV Chip Condition enumeration
7+
*
8+
* Indicates the status of previous chip read attempts for fallback scenarios
9+
*/
10+
class EmvChipCondition
11+
{
12+
/**
13+
* Previous chip read was successful
14+
*/
15+
const CHIP_FAILED_PREV_SUCCESS = 'CHIP_FAILED_PREV_SUCCESS';
16+
17+
/**
18+
* Previous chip read also failed
19+
*/
20+
const CHIP_FAILED_PREV_FAILED = 'CHIP_FAILED_PREV_FAILED';
21+
}
Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace GlobalPayments\Api\Entities\GpApi;
46

5-
class AccessTokenRequest
7+
class AccessTokenRequest implements \JsonSerializable
68
{
79
public $app_id;
810
public $nonce;
@@ -11,17 +13,18 @@ class AccessTokenRequest
1113
public $seconds_to_expire;
1214
public $interval_to_expire;
1315
public $permissions;
16+
public $credentials;
1417

15-
/**
16-
* AccessTokenRequest constructor.
17-
* @param $app_id
18-
* @param $nonce
19-
* @param $secret
20-
* @param $grant_type
21-
* @param $seconds_to_expire
22-
* @param $interval_to_expire
23-
*/
24-
public function __construct($app_id, $nonce, $secret, $grant_type, $seconds_to_expire, $interval_to_expire, $permissions)
18+
public function __construct(
19+
$app_id,
20+
$nonce,
21+
$secret,
22+
$grant_type,
23+
$seconds_to_expire,
24+
$interval_to_expire,
25+
$permissions,
26+
$credentials = null
27+
)
2528
{
2629
$this->app_id = $app_id;
2730
$this->nonce = $nonce;
@@ -30,5 +33,39 @@ public function __construct($app_id, $nonce, $secret, $grant_type, $seconds_to_e
3033
$this->seconds_to_expire = $seconds_to_expire;
3134
$this->interval_to_expire = $interval_to_expire;
3235
$this->permissions = $permissions;
36+
$this->credentials = $credentials;
37+
}
38+
39+
public function jsonSerialize(): array
40+
{
41+
$data = [];
42+
43+
if ($this->app_id !== null) {
44+
$data['app_id'] = $this->app_id;
45+
}
46+
if ($this->nonce !== null) {
47+
$data['nonce'] = $this->nonce;
48+
}
49+
if ($this->secret !== null) {
50+
$data['secret'] = $this->secret;
51+
}
52+
53+
$data['grant_type'] = $this->grant_type;
54+
55+
if ($this->seconds_to_expire !== null) {
56+
$data['seconds_to_expire'] = $this->seconds_to_expire;
57+
}
58+
if ($this->interval_to_expire !== null) {
59+
$data['interval_to_expire'] = $this->interval_to_expire;
60+
}
61+
if (!empty($this->permissions)) {
62+
$data['permissions'] = $this->permissions;
63+
}
64+
65+
if (!empty($this->credentials)) {
66+
$data['credentials'] = $this->credentials;
67+
}
68+
69+
return $data;
3370
}
3471
}
Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,94 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace GlobalPayments\Api\Entities\GpApi;
46

57
use GlobalPayments\Api\Gateways\IAccessTokenProvider;
68

79
class GpApiSessionInfo implements IAccessTokenProvider
810
{
9-
private static function generateSecret($nonce, $appKey)
11+
private static function generateSecret(string $nonce, string $appKey): string
1012
{
1113
return hash('SHA512', $nonce . $appKey);
1214
}
1315

14-
private static function generateNonce()
16+
private static function generateNonce(): string
1517
{
16-
$base = new \DateTime();
17-
return $base->format(\DateTime::RFC3339);
18+
return (new \DateTime())->format(\DateTime::RFC3339);
1819
}
1920

20-
public function signIn($appId, $appKey, $secondsToExpire = null, $intervalToExpire = null, $permissions = []) : GpApiRequest
21+
public function signIn(
22+
$appId,
23+
$appKey,
24+
$secondsToExpire = null,
25+
$intervalToExpire = null,
26+
$permissions = [],
27+
$porticoCredentials = null,
28+
$secretApiKey = null
29+
): GpApiRequest
2130
{
2231
$nonce = self::generateNonce();
32+
$credentials = null;
33+
34+
// Build credentials array if Portico credentials are provided
35+
if (!empty($porticoCredentials)) {
36+
$credentials = [];
37+
38+
if (!empty($porticoCredentials['deviceId'])) {
39+
$credentials[] = ['name' => 'device_id', 'value' => $porticoCredentials['deviceId']];
40+
}
41+
if (!empty($porticoCredentials['siteId'])) {
42+
$credentials[] = ['name' => 'site_id', 'value' => $porticoCredentials['siteId']];
43+
}
44+
if (!empty($porticoCredentials['licenseId'])) {
45+
$credentials[] = ['name' => 'license_id', 'value' => $porticoCredentials['licenseId']];
46+
}
47+
if (!empty($porticoCredentials['username'])) {
48+
$credentials[] = ['name' => 'username', 'value' => $porticoCredentials['username']];
49+
}
50+
if (!empty($porticoCredentials['password'])) {
51+
$credentials[] = ['name' => 'password', 'value' => $porticoCredentials['password']];
52+
}
53+
54+
if (!empty($appKey)) {
55+
$credentials[] = ['name' => 'apikey', 'value' => $appKey];
56+
}
57+
}
58+
59+
if (!empty($secretApiKey)) {
60+
if (empty($credentials)) {
61+
$credentials = [];
62+
}
63+
$credentials[] = ['name' => 'apikey', 'value' => $secretApiKey];
64+
}
65+
66+
$finalAppId = null;
67+
$finalNonce = null;
68+
$finalSecret = null;
69+
70+
if (!empty($appId) && !empty($appKey)) {
71+
$finalAppId = $appId;
72+
$finalNonce = $nonce;
73+
$finalSecret = self::generateSecret($nonce, $appKey);
74+
}
2375

2476
$requestBody = new AccessTokenRequest(
25-
$appId,
26-
$nonce,
27-
self::generateSecret($nonce, $appKey),
77+
$finalAppId,
78+
$finalNonce,
79+
$finalSecret,
2880
'client_credentials',
2981
$secondsToExpire,
3082
$intervalToExpire,
31-
$permissions
83+
$permissions,
84+
$credentials
3285
);
3386

3487
return new GpApiRequest(GpApiRequest::ACCESS_TOKEN_ENDPOINT, 'POST', $requestBody);
3588
}
3689

3790
public function singOut(): GpApiRequest
3891
{
39-
// TODO: Implement singOut() method.
92+
throw new \BadMethodCallException('Method not implemented');
4093
}
4194
}

src/Gateways/GpApiConnector.php

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class GpApiConnector extends RestGateway implements IPaymentGateway, ISecure3dPr
4949
const GP_API_VERSION = '2021-03-22';
5050
const IDEMPOTENCY_HEADER = 'x-gp-idempotency';
5151
/**
52-
* @var $gpApiConfig GpApiConfig
52+
* @var GpApiConfig
5353
*/
5454
private $gpApiConfig;
5555
private $accessToken;
@@ -449,18 +449,31 @@ public function getAccessToken()
449449
{
450450
$this->accessToken = null;
451451

452+
// Prepare Portico credentials if provided
453+
$porticoCredentials = null;
454+
if (!empty($this->gpApiConfig->deviceId) || !empty($this->gpApiConfig->siteId) ||
455+
!empty($this->gpApiConfig->licenseId) || !empty($this->gpApiConfig->username) ||
456+
!empty($this->gpApiConfig->password)) {
457+
$porticoCredentials = [
458+
'deviceId' => $this->gpApiConfig->deviceId,
459+
'siteId' => $this->gpApiConfig->siteId,
460+
'licenseId' => $this->gpApiConfig->licenseId,
461+
'username' => $this->gpApiConfig->username,
462+
'password' => $this->gpApiConfig->password
463+
];
464+
}
465+
452466
$request = $this->gpApiConfig->accessTokenProvider->signIn(
453467
$this->gpApiConfig->appId,
454468
$this->gpApiConfig->appKey,
455469
$this->gpApiConfig->secondsToExpire,
456470
$this->gpApiConfig->intervalToExpire,
457-
$this->gpApiConfig->permissions
471+
$this->gpApiConfig->permissions,
472+
$porticoCredentials,
473+
$this->gpApiConfig->secretApiKey
458474
);
459-
try {
460-
$response = parent::doTransaction($request->httpVerb, $request->endpoint, $request->requestBody);
461-
} catch (GatewayException $gatewayException) {
462-
throw $gatewayException;
463-
}
475+
476+
$response = parent::doTransaction($request->httpVerb, $request->endpoint, $request->requestBody);
464477

465478
return new GpApiTokenResponse($response);
466479
}

src/Gateways/PorticoConnector.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,14 @@ public function processAuthorization(AuthorizationBuilder $builder)
224224
$block1->appendChild($xml->createElement('Amt', $builder->amount));
225225
}
226226

227+
if (!empty($builder->emvChipCondition)) {
228+
$emvDataElement = $xml->createElement('EMVData');
229+
$emvDataElement->appendChild(
230+
$xml->createElement('EMVChipCondition', $builder->emvChipCondition)
231+
);
232+
$block1->appendChild($emvDataElement);
233+
}
234+
227235
if ($builder->gratuity !== null) {
228236
$block1->appendChild(
229237
$xml->createElement('GratuityAmtInfo', $builder->gratuity)
@@ -301,7 +309,6 @@ public function processAuthorization(AuthorizationBuilder $builder)
301309
if (!empty($builder->categoryIndicator)) {
302310
$cardOnFileData->appendChild($xml->createElement('CategoryInd', $builder->categoryIndicator));
303311
}
304-
305312
$block1->appendChild($cardOnFileData);
306313
}
307314

@@ -619,9 +626,9 @@ public function processAuthorization(AuthorizationBuilder $builder)
619626

620627
$transaction->appendChild($block1);
621628

622-
$response = $this->doTransaction(
623-
$this->buildEnvelope($xml, $transaction, $builder->clientTransactionId, $builder)
624-
);
629+
$xmlRequest = $this->buildEnvelope($xml, $transaction, $builder->clientTransactionId, $builder);
630+
631+
$response = $this->doTransaction($xmlRequest);
625632
return $this->mapResponse($response, $builder, $this->buildEnvelope($xml, $transaction));
626633
}
627634

0 commit comments

Comments
 (0)