Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PW-7214] - Implement combo card funding source selection for POS payments in the frontend #1749

Merged
merged 8 commits into from
Oct 5, 2022
Merged
51 changes: 34 additions & 17 deletions Gateway/Http/Client/TransactionPosCloudSync.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Adyen\AdyenException;
use Adyen\Payment\Helper\ChargedCurrency;
use Adyen\Payment\Helper\Data;
use Adyen\Payment\Helper\PaymentMethods;
use Adyen\Payment\Helper\PointOfSale;
use Adyen\Payment\Logger\AdyenLogger;
use Adyen\Payment\Model\Ui\AdyenPosCloudConfigProvider;
Expand Down Expand Up @@ -108,7 +109,11 @@ public function placeRequest(TransferInterface $transferObject): array
$terminalId = $request['terminalID'];

if (array_key_exists('chainCalls', $request)) {
$quote = $this->initiatePosPayment($terminalId, $request['numberOfInstallments']);
$quote = $this->initiatePosPayment(
$terminalId,
$request['fundingSource'],
$request['numberOfInstallments']
);
$quoteInfoInstance = $quote->getPayment()->getMethodInstance()->getInfoInstance();
$timeDiff = (int)$statusDate - (int)$quoteInfoInstance->getAdditionalInformation('initiateDate');
$serviceId = $quoteInfoInstance->getAdditionalInformation('serviceID');
Expand All @@ -117,7 +122,6 @@ public function placeRequest(TransferInterface $transferObject): array
$serviceId = $request['serviceID'];
}


$totalTimeout = $this->adyenHelper->getAdyenPosCloudConfigData('total_timeout', $this->storeId);
if ($timeDiff > $totalTimeout) {
throw new LocalizedException(__("POS connection timed out."));
Expand Down Expand Up @@ -180,15 +184,18 @@ public function placeRequest(TransferInterface $transferObject): array
* Initiate a POS payment by sending a /sync call to Adyen
*
* @param string $terminalId
* @param string $fundingSource
* @param string|null $numberOfInstallments
* @return CartInterface
* @throws AdyenException
* @throws LocalizedException
* @throws NoSuchEntityException
*/
public function initiatePosPayment(string $terminalId, ?string $numberOfInstallments): CartInterface
{

public function initiatePosPayment(
string $terminalId,
string $fundingSource,
?string $numberOfInstallments
): CartInterface {
// Validate JSON that has just been parsed if it was in a valid format
if (json_last_error() !== JSON_ERROR_NONE) {
throw new LocalizedException(
Expand Down Expand Up @@ -248,22 +255,32 @@ public function initiatePosPayment(string $terminalId, ?string $numberOfInstallm
]
];

if (isset($numberOfInstallments)) {
$request['SaleToPOIRequest']['PaymentRequest']['PaymentData'] = [
"PaymentType" => "Instalment",
"Instalment" => [
"InstalmentType" => "EqualInstalments",
"SequenceNumber" => 1,
"Period" => 1,
"PeriodUnit" => "Monthly",
"TotalNbOfPayments" => intval($numberOfInstallments)
]
];
if ($fundingSource === PaymentMethods::FUNDING_SOURCE_CREDIT) {
if (isset($numberOfInstallments)) {
$request['SaleToPOIRequest']['PaymentRequest']['PaymentData'] = [
"PaymentType" => "Instalment",
"Instalment" => [
"InstalmentType" => "EqualInstalments",
"SequenceNumber" => 1,
"Period" => 1,
"PeriodUnit" => "Monthly",
"TotalNbOfPayments" => intval($numberOfInstallments)
]
];
} else {
$request['SaleToPOIRequest']['PaymentData'] = [
'PaymentType' => $transactionType,
];
}

$request['SaleToPOIRequest']['PaymentRequest']['PaymentTransaction']['TransactionConditions'] = [
"DebitPreferredFlag" => false
];
} else {
} elseif ($fundingSource === PaymentMethods::FUNDING_SOURCE_DEBIT) {
$request['SaleToPOIRequest']['PaymentRequest']['PaymentTransaction']['TransactionConditions'] = [
"DebitPreferredFlag" => true
];

$request['SaleToPOIRequest']['PaymentData'] = [
'PaymentType' => $transactionType,
];
Expand Down
6 changes: 4 additions & 2 deletions Gateway/Request/PosCloudBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,16 @@ public function build(array $buildSubject)
$body = [
'terminalID' => $payment->getAdditionalInformation('terminal_id'),
'numberOfInstallments' => $payment->getAdditionalInformation('number_of_installments'),
'chainCalls' => $payment->getAdditionalInformation('chain_calls')
'chainCalls' => $payment->getAdditionalInformation('chain_calls'),
'fundingSource' => $payment->getAdditionalInformation('funding_source')
];
} else {
$body = [
"response" => $payment->getAdditionalInformation("terminalResponse"),
"serviceID" => $payment->getAdditionalInformation("serviceID"),
"initiateDate" => $payment->getAdditionalInformation("initiateDate"),
"terminalID" => $payment->getAdditionalInformation("terminal_id")
"terminalID" => $payment->getAdditionalInformation("terminal_id"),
'fundingSource' => $payment->getAdditionalInformation('funding_source')
];
}

Expand Down
3 changes: 3 additions & 0 deletions Helper/PaymentMethods.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class PaymentMethods extends AbstractHelper
"scheme" => "card"
];

const FUNDING_SOURCE_DEBIT = 'debit';
const FUNDING_SOURCE_CREDIT = 'credit';

/**
* @var CartRepositoryInterface
*/
Expand Down
12 changes: 12 additions & 0 deletions Model/Ui/AdyenPosCloudConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public function getConfig()

if ($this->adyenHelper->getAdyenPosCloudConfigDataFlag("active")) {
$config['payment']['adyenPos']['connectedTerminals'] = $this->getConnectedTerminals();
$config['payment']['adyenPos']['fundingSourceOptions'] = $this->getFundingSourceOptions();
}

// has installments by default false
Expand Down Expand Up @@ -133,4 +134,15 @@ protected function getConnectedTerminals()

return [];
}

/**
* @return string[]
*/
protected function getFundingSourceOptions(): array
{
return [
'credit' => 'Credit Card',
'debit' => 'Debit Card'
];
}
}
4 changes: 3 additions & 1 deletion Observer/AdyenPosCloudDataAssignObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ class AdyenPosCloudDataAssignObserver extends AbstractDataAssignObserver
const TERMINAL_ID = 'terminal_id';
const NUMBER_OF_INSTALLMENTS = 'number_of_installments';
const CHAIN_CALLS = 'chain_calls';
const FUNDING_SOURCE = 'funding_source';

/**
* @var array
*/
protected $additionalInformationList = [
self::TERMINAL_ID,
self::NUMBER_OF_INSTALLMENTS,
self::CHAIN_CALLS
self::CHAIN_CALLS,
self::FUNDING_SOURCE
];

/**
Expand Down
6 changes: 6 additions & 0 deletions view/frontend/web/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@
.payment-method+#hpp_actionModalWrapper {
border-bottom: 1px solid #ccc;
}

#adyen_pos_cloud_connected_terminals_div,
#adyen_pos_cloud_funding_source_div,
#adyen_pos_cloud_installments_div {
margin-bottom: 15px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ define(
defaults: {
template: 'Adyen_Payment/payment/pos-cloud-form'
},
fundingSource: ko.observable('credit'),
initObservable: function () {
this._super()
.observe([
'terminalId',
'fundingSource',
'installments',
'installment'
]);
Expand All @@ -48,15 +50,15 @@ define(
},
initialize: function () {
this._super();
var self = this;
let self = this;

// installments
var allInstallments = self.getAllInstallments();
var grandTotal = quote.totals().grand_total;
var precision = quote.getPriceFormat().precision;
var currencyCode = quote.totals().quote_currency_code;
let allInstallments = self.getAllInstallments();
let grandTotal = quote.totals().grand_total;
let precision = quote.getPriceFormat().precision;
let currencyCode = quote.totals().quote_currency_code;

var numberOfInstallments = installmentsHelper.getInstallmentsWithPrices(allInstallments, grandTotal, precision, currencyCode);
let numberOfInstallments = installmentsHelper.getInstallmentsWithPrices(allInstallments, grandTotal, precision, currencyCode);

if (numberOfInstallments) {
self.installments(numberOfInstallments);
Expand All @@ -71,7 +73,7 @@ define(
}
},
placeOrderPos: function () {
var self = this;
let self = this;
return $.when(
placeOrderAction(self.getData(), new Messages())
).fail(
Expand All @@ -92,10 +94,10 @@ define(
)
},
getConnectedTerminals: function () {
var connectedTerminals = [];
let connectedTerminals = [];
const connectedTerminalsList = window.checkoutConfig.payment.adyenPos.connectedTerminals;

for (var i = 0; i < connectedTerminalsList.length; i++) {
for (let i = 0; i < connectedTerminalsList.length; i++) {
connectedTerminals.push(
{
key: connectedTerminalsList[i],
Expand All @@ -106,6 +108,34 @@ define(

return connectedTerminals;
},
isFundingSourceAvailable: function () {
if (quote.billingAddress() === null) {
return false;
}
let countryId = quote.billingAddress().countryId;
let currencyCode = quote.totals().quote_currency_code;
let allowedCurrenciesByCountry = {
'BR': 'BRL',
'MX': 'MXN',
};
return allowedCurrenciesByCountry[countryId] &&
currencyCode === allowedCurrenciesByCountry[countryId];
},
getFundingSourceOptions: function () {
let fundingSource = [];
const fundingSourceOptions = window.checkoutConfig.payment.adyenPos.fundingSourceOptions;

for (let i = 0; i < Object.values(fundingSourceOptions).length; i++) {
fundingSource.push(
{
value: Object.keys(fundingSourceOptions)[i],
key: Object.values(fundingSourceOptions)[i]
}
);
}

return fundingSource;
},
/**
* Get data for place order
* @returns {{method: *}}
Expand All @@ -116,12 +146,13 @@ define(
additional_data: {
'terminal_id': this.terminalId(),
'number_of_installments': this.installment(),
'chain_calls': true
'chain_calls': true,
'funding_source': this.fundingSource()
}
};
},
hasInstallments: function () {
return window.checkoutConfig.payment.adyenPos.hasInstallments;
return window.checkoutConfig.payment.adyenPos.hasInstallments && this.fundingSource() === 'credit';
},
getAllInstallments: function () {
return window.checkoutConfig.payment.adyenPos.installments;
Expand Down
19 changes: 19 additions & 0 deletions view/frontend/web/template/payment/pos-cloud-form.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@
</div>
</div>

<!-- ko if: (isFundingSourceAvailable())-->
<div class="field required" data-bind="attr: {id: getCode() + '_funding_source_div'}, visible: isFundingSourceAvailable()">
<label data-bind="attr: {for: getCode() + '_funding_source'}" class="label">
<span><!-- ko text: $t('Credit/Debit Card')--><!-- /ko --></span>
</label>
<div class="control">
<select class="select"
name="paymentMethod[funding_source]"
data-bind="attr: {id: getCode() + '_funding_source', 'data-container': getCode() + '-funding-source', 'data-validate': JSON.stringify({required:true})},
options: getFundingSourceOptions(),
optionsValue: 'value',
optionsText: 'key',
value: fundingSource"
>
</select>
</div>
</div>
<!-- /ko -->

<!-- ko if: (hasInstallments())-->
<div class="field required"
data-bind="attr: {id: getCode() + '_installments_div'}, visible: installments().length > 0">
Expand Down