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
1 change: 1 addition & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ src/
- Backward-compat typo alias (`vefiyFingerPrint`) remains supported.
- DirectPost supports purchase, authorize, complete callbacks, store-only flow, and additive server-to-server operations (`capture`, `refund`, `void`).
- EMV 3DS order management is exposed via `createEMV3DSOrder()`.
- Advanced optional DirectPost fields are supported for surcharge reporting, MCR card scheme routing hint, and result/callback parameter passthrough.

### 3) Hosted Payment

Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,32 @@ The following gateways are provided by this package:
}
```

#### DirectPost Advanced Optional Parameters

```php
$response = $gateway->purchase([
'amount' => '112.00',
'currency' => 'AUD',
'transactionId' => 'ORDER-ADV-100',
'card' => $card,
'returnUrl' => 'https://example.com/payment/response',
'notifyUrl' => 'https://example.com/payment/callback',

// Optional reporting/callback field control
'resultParams' => 'merchant,refid,rescode,restext',
'callbackParams' => 'merchant,refid,rescode,restext',

// Optional surcharge reporting fields
'surchargeEnabled' => true,
'surchargeAmount' => '12.00',
'surchargeRate' => '5.00',
'surchargeFee' => '7.00',

// Optional MCR route hint
'cardScheme' => 'scheme', // or 'eftpos'
])->send();
```

### NAB Transact DirectPost v2 UnionPay Online Payment

```php
Expand Down
4 changes: 4 additions & 0 deletions docs/feature-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ Out of scope:
| Reversal/void (server-to-server) | `void()` | `DirectPostReversalRequest` | Implemented |
| DirectPost API response mapper | n/a | `DirectPostApiResponse` | Implemented |
| Fingerprint verification helper | `webhook()` | `DirectPostWebhookRequest` | Implemented |
| Result/callback parameter passthrough | `setResultParams()/setCallbackParams()` | `DirectPostAbstractRequest` | Implemented |
| Surcharge reporting parameters | `setSurcharge*()` | `DirectPostAbstractRequest` | Implemented |
| MCR card scheme passthrough | `setCardScheme()` | `DirectPostAbstractRequest` | Implemented |
| EMV 3DS txnType mapping | `setHasEMV3DSEnabled(true)` | `DirectPostAbstractRequest` | Implemented |
| Risk-managed txnType mapping | `setHasRiskManagementEnabled(true)` | `DirectPostAbstractRequest` | Implemented |
| Risk + EMV txnType mapping | both flags enabled | `DirectPostAbstractRequest` | Implemented |
Expand All @@ -57,6 +60,7 @@ Out of scope:
| UnionPay purchase redirect | `purchase()` | `UnionPayPurchaseRequest` | Implemented |
| UnionPay callback completion | `completePurchase()` | `UnionPayCompletePurchaseRequest` | Implemented |
| UnionPay completion response mapping | n/a | `UnionPayCompletePurchaseResponse` | Implemented |
| UPOP currency/flow guards | `purchase()` validation | `UnionPayPurchaseRequest` | Implemented |

## Transport Layer

Expand Down
6 changes: 5 additions & 1 deletion docs/features-implemented-tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ omnipay-nabtransact
│ │ ├── Security/txn behavior
│ │ │ ├── fingerprint generation + verification
│ │ │ ├── risk + EMV txnType resolution
│ │ │ ├── surcharge parameter passthrough
│ │ │ ├── card scheme (MCR) passthrough
│ │ │ └── result/callback parameter passthrough
│ │ │ └── legacy typo alias support: vefiyFingerPrint()
│ │ └── transport + timeout control for server-to-server calls
│ │
Expand All @@ -44,7 +47,8 @@ omnipay-nabtransact
│ │
│ └── NABTransact_UnionPay (UnionPayGateway)
│ ├── purchase() -> UnionPayPurchaseRequest
│ └── completePurchase() -> UnionPayCompletePurchaseRequest
│ ├── completePurchase() -> UnionPayCompletePurchaseRequest
│ └── UPOP guard rails (currency/risk/EMV validation)
├── Response Models
│ ├── SecureXMLResponse
Expand Down
154 changes: 135 additions & 19 deletions src/Message/DirectPostAbstractRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,121 @@ abstract class DirectPostAbstractRequest extends AbstractRequest
*/
protected $txnType = '0';

public function getResultParams()
{
return $this->getParameter('resultParams');
}

public function setResultParams($value)
{
return $this->setParameter('resultParams', $value);
}

public function getCallbackParams()
{
return $this->getParameter('callbackParams');
}

public function setCallbackParams($value)
{
return $this->setParameter('callbackParams', $value);
}

public function getCardScheme()
{
return $this->getParameter('cardScheme');
}

public function setCardScheme($value)
{
return $this->setParameter('cardScheme', $value);
}

public function getSurchargeEnabled()
{
return $this->getParameter('surchargeEnabled');
}

public function setSurchargeEnabled($value)
{
return $this->setParameter('surchargeEnabled', $value);
}

public function getSurchargeAmount()
{
return $this->getParameter('surchargeAmount');
}

public function setSurchargeAmount($value)
{
return $this->setParameter('surchargeAmount', $value);
}

public function getSurchargeRate()
{
return $this->getParameter('surchargeRate');
}

public function setSurchargeRate($value)
{
return $this->setParameter('surchargeRate', $value);
}

public function getSurchargeFee()
{
return $this->getParameter('surchargeFee');
}

public function setSurchargeFee($value)
{
return $this->setParameter('surchargeFee', $value);
}

/**
* @param array<string,mixed> $data
*
* @return string
*/
protected function buildFingerprintFromFields(array $data)
{
$fields = [
'EPS_MERCHANT',
'__TRANSACTION_PASSWORD__',
'EPS_TXNTYPE',
'EPS_REFERENCEID',
'EPS_AMOUNT',
'EPS_TIMESTAMP',
];

if (isset($data['EPS_ORDERID'])) {
$fields[] = 'EPS_ORDERID';
}

if (isset($data['EPS_CARDSCHEME'])) {
$fields[] = 'EPS_CARDSCHEME';
}

if (isset($data['EPS_SURCHARGEENABLED'])) {
$fields[] = 'EPS_SURCHARGEENABLED';
$fields[] = 'EPS_SURCHARGEAMOUNT';
$fields[] = 'EPS_SURCHARGERATE';
$fields[] = 'EPS_SURCHARGEFEE';
}

$hashable = [];
foreach ($fields as $field) {
if ($field === '__TRANSACTION_PASSWORD__') {
$hashable[] = (string) $this->getTransactionPassword();

continue;
}

$hashable[] = isset($data[$field]) ? (string) $data[$field] : '';
}

return hash_hmac('sha256', implode('|', $hashable), $this->getTransactionPassword());
}

/**
* @return string
*/
Expand Down Expand Up @@ -54,25 +169,7 @@ protected function resolveTxnType()
*/
public function generateFingerprint(array $data)
{
$hashable = [
$data['EPS_MERCHANT'],
$this->getTransactionPassword(),
$data['EPS_TXNTYPE'],
$data['EPS_REFERENCEID'],
$data['EPS_AMOUNT'],
$data['EPS_TIMESTAMP'],
];

if ($this->getHasEMV3DSEnabled()) {
$hashable = array_merge(
$hashable,
[$data['EPS_ORDERID']]
);
}

$hash = implode('|', $hashable);

return hash_hmac('sha256', $hash, $this->getTransactionPassword());
return $this->buildFingerprintFromFields($data);
}

/**
Expand All @@ -95,10 +192,22 @@ public function getBaseData()
$data['EPS_CALLBACKURL'] = $this->getNotifyUrl();
}

if ($resultParams = $this->getResultParams()) {
$data['EPS_RESULTPARAMS'] = $resultParams;
}

if ($callbackParams = $this->getCallbackParams()) {
$data['EPS_CALLBACKPARAMS'] = $callbackParams;
}

if ($currency = $this->getCurrency()) {
$data['EPS_CURRENCY'] = $currency;
}

if ($cardScheme = $this->getCardScheme()) {
$data['EPS_CARDSCHEME'] = $cardScheme;
}

$card = $this->getParameter('card');

if ($card instanceof CreditCard) {
Expand Down Expand Up @@ -135,6 +244,13 @@ public function getBaseData()
$data['EPS_ORDERID'] = $this->getTransactionReference();
}

if ((bool) $this->getSurchargeEnabled()) {
$data['EPS_SURCHARGEENABLED'] = 'true';
$data['EPS_SURCHARGEAMOUNT'] = (string) ($this->getSurchargeAmount() !== null ? $this->getSurchargeAmount() : '0.00');
$data['EPS_SURCHARGERATE'] = (string) ($this->getSurchargeRate() !== null ? $this->getSurchargeRate() : '0.00');
$data['EPS_SURCHARGEFEE'] = (string) ($this->getSurchargeFee() !== null ? $this->getSurchargeFee() : '0.00');
}

$data['EPS_FINGERPRINT'] = $this->generateFingerprint($data);

return $data;
Expand Down
38 changes: 37 additions & 1 deletion src/Message/DirectPostStoreRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ class DirectPostStoreRequest extends DirectPostAuthorizeRequest
*/
public $txnType = '8';

public function getStoreType()
{
return $this->getParameter('storeType');
}

public function setStoreType($value)
{
return $this->setParameter('storeType', $value);
}

/**
* @return array
*/
Expand All @@ -23,6 +33,32 @@ public function getData()
$this->setAmount('0.00');
}

return parent::getData();
$data = parent::getData();
$data['EPS_STORE'] = 'true';
$data['EPS_STORETYPE'] = (string) ($this->getStoreType() ?: 'TOKEN');
$data['EPS_FINGERPRINT'] = $this->generateFingerprint($data);

return $data;
}

/**
* @param array $data
*/
public function generateFingerprint(array $data)
{
if ((string) $this->txnType !== '8' || !isset($data['EPS_STORETYPE'])) {
return parent::generateFingerprint($data);
}

$hashable = [
$data['EPS_MERCHANT'],
$this->getTransactionPassword(),
$data['EPS_TXNTYPE'],
$data['EPS_STORETYPE'],
$data['EPS_REFERENCEID'],
$data['EPS_TIMESTAMP'],
];

return hash_hmac('sha256', implode('|', $hashable), $this->getTransactionPassword());
}
}
17 changes: 17 additions & 0 deletions src/Message/UnionPayPurchaseRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Omnipay\NABTransact\Message;

use Omnipay\Common\Exception\InvalidRequestException;

/**
* UnionPayPurchaseRequest.
*/
Expand All @@ -19,6 +21,21 @@ public function getData()
{
$this->validate('amount', 'returnUrl', 'transactionId');

if ((bool) $this->getHasRiskManagementEnabled()) {
throw new InvalidRequestException('UPOP does not support risk-management transaction types.');
}

if ((bool) $this->getHasEMV3DSEnabled()) {
throw new InvalidRequestException('UPOP does not support EMV 3DS transaction types.');
}

if ($this->getCurrency() !== null) {
$currency = strtoupper((string) $this->getCurrency());
if (!in_array($currency, ['AUD', 'CNY'], true)) {
throw new InvalidRequestException('UPOP only supports AUD or CNY currencies.');
}
}

$data = $this->getBaseData();

$data['EPS_PAYMENTCHOICE'] = 'UPOP';
Expand Down
46 changes: 46 additions & 0 deletions tests/Message/DirectPostAdvancedFieldsRequestTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Omnipay\NABTransact\Message;

use Omnipay\NABTransact\Tests\Support\TestCase;

class DirectPostAdvancedFieldsRequestTest extends TestCase
{
public function testAddsAdvancedDirectPostFieldsToPayload()
{
$request = new DirectPostPurchaseRequest($this->getHttpClient(), $this->getHttpRequest());
$request->initialize([
'merchantId' => 'XYZ0010',
'transactionPassword' => 'abcd1234',
'amount' => '112.00',
'currency' => 'AUD',
'returnUrl' => 'https://www.example.com/return',
'notifyUrl' => 'https://www.example.com/callback',
'transactionId' => 'ORDER-ADV-100',
'card' => [
'number' => '4444333322221111',
'expiryMonth' => '12',
'expiryYear' => '2030',
'cvv' => '123',
],
'surchargeEnabled' => true,
'surchargeAmount' => '12.00',
'surchargeRate' => '5.00',
'surchargeFee' => '7.00',
'cardScheme' => 'scheme',
'resultParams' => 'merchant,refid,rescode,restext',
'callbackParams' => 'merchant,refid,rescode,restext',
]);

$data = $request->getData();

$this->assertSame('scheme', $data['EPS_CARDSCHEME']);
$this->assertSame('true', $data['EPS_SURCHARGEENABLED']);
$this->assertSame('12.00', $data['EPS_SURCHARGEAMOUNT']);
$this->assertSame('5.00', $data['EPS_SURCHARGERATE']);
$this->assertSame('7.00', $data['EPS_SURCHARGEFEE']);
$this->assertSame('merchant,refid,rescode,restext', $data['EPS_RESULTPARAMS']);
$this->assertSame('merchant,refid,rescode,restext', $data['EPS_CALLBACKPARAMS']);
}
}

Loading