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
6 changes: 6 additions & 0 deletions assets/shop/js/retry_payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ function showPaymentDetails(paymentCode) {
.forEach(function(element) {
element.disabled = false;
});

const checkoutButton = document.querySelector('form[name="sylius_checkout_select_payment"] button[type="submit"]');
if (checkoutButton) {
const hiddenFor = ['tpay_apple_pay', 'tpay_google_pay'];
checkoutButton.hidden = hiddenFor.includes(paymentCode);
}
}

function resetPaymentDetails() {
Expand Down
1 change: 1 addition & 0 deletions assets/shop/order_show_entrypoint.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import './scss/style.scss';
import './scss/pay_by_link.scss';
import './js/apple_pay';
import './js/blik_code';
import './js/retry_payment';
import './js/visa_mobile';
Expand Down
12 changes: 12 additions & 0 deletions config/services_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use CommerceWeavers\SyliusTpayPlugin\Test\Payum\Cypher\FakeCypher;
use Tests\CommerceWeavers\SyliusTpayPlugin\Helper\AlwaysValidChecksumVerifier;
use Tests\CommerceWeavers\SyliusTpayPlugin\Helper\AlwaysValidSignatureVerifier;

return function(ContainerConfigurator $container): void {
$services = $container->services();
Expand All @@ -14,4 +16,14 @@
env('PAYUM_CYPHER_KEY'),
])
;

$services->set('commerce_weavers_sylius_tpay.tests.always_valid_signature_verifier', AlwaysValidSignatureVerifier::class);

$services->set('commerce_weavers_sylius_tpay.tests.always_valid_checksum_verifier', AlwaysValidChecksumVerifier::class);

$services->set('commerce_weavers_sylius_tpay.tpay.security.notification.verifier.signature')
->alias('commerce_weavers_sylius_tpay.tpay.security.notification.verifier.signature', 'commerce_weavers_sylius_tpay.tests.always_valid_signature_verifier');

$services->set('commerce_weavers_sylius_tpay.tpay.security.notification.verifier.checksum')
->alias('commerce_weavers_sylius_tpay.tpay.security.notification.verifier.checksum', 'commerce_weavers_sylius_tpay.tests.always_valid_checksum_verifier');
};
10 changes: 10 additions & 0 deletions config/twig_hooks/shop/checkout/select_payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
'priority' => 150,
],
],
'sylius_shop.order.show.content.form.select_payment.payment' => [
'apple_pay' => [
'template' => '@CommerceWeaversSyliusTpayPlugin/shop/order/pay/apple_pay.html.twig',
'priority' => -300,
],
'google_pay' => [
'template' => '@CommerceWeaversSyliusTpayPlugin/shop/order/pay/google_pay.html.twig',
'priority' => -300,
],
],
'sylius_shop.shared.form.select_payment.payment.choice.details' => [
'payment_image' => [
'template' => '@CommerceWeaversSyliusTpayPlugin/shop/checkout/select_payment/payment/choice/details/image.html.twig',
Expand Down
3 changes: 2 additions & 1 deletion public/shop/scripts/order-show.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/Model/OrderLastNewPaymentAwareTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ public function getLastCartPayment(): ?PaymentInterface
{
return $this->getLastPayment('cart');
}

public function getLastNewPayment(): ?PaymentInterface
{
return $this->getLastPayment('new');
}
}
11 changes: 2 additions & 9 deletions templates/shop/cart/complete/apple_pay.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,7 @@

{% if payment is not null and payment.method.gatewayConfig.factoryName == 'tpay_apple_pay' %}
{% set form = get_hookable_context().form %}
{% set gatewayConfig = payment.method.gatewayConfig %}

<input type="hidden" data-apple-pay-amount value="{{ order.total|cw_tpay_convert_minor_to_major_currency }}">
<input type="hidden" data-apple-pay-merchant-identifier value="{{ cw_tpay_get_gateway_config_value(payment.method.gatewayConfig, 'apple_pay_merchant_id') }}">
<input type="hidden" data-apple-pay-currency value="{{ order.currencyCode }}">
<input type="hidden" data-apple-pay-channel-name value="{{ order.channel.name }}" />

<apple-pay-button data-apple-pay-button buttonstyle="black" type="plain" locale="{{ order.localeCode|replace({'_': '-'}) }}"></apple-pay-button>
{{ form_row(form.tpay.apple_pay_token, { attr: { 'data-apple-pay-token-input': '' } }) }}

<script crossorigin src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"></script>
{% include '@CommerceWeaversSyliusTpayPlugin/shop/partial/apple_pay_button.html.twig' with { order, form, gatewayConfig } %}
{% endif %}
256 changes: 1 addition & 255 deletions templates/shop/cart/complete/google_pay.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -3,261 +3,7 @@

{% if payment is not null and payment.method.gatewayConfig.factoryName == 'tpay_google_pay' %}
{% set form = get_hookable_context().form %}

{% set gatewayConfig = payment.method.gatewayConfig %}
{% set productionMode = gatewayConfig.config['production_mode'] ?? true %}
{% set tpayMerchantId = gatewayConfig.config['merchant_id'] ?? '' %}
{% set googleMerchantId = gatewayConfig.config['google_merchant_id'] ?? '' %}

<div id="cw-tpay-google-pay-container" style="margin: 2em auto 0 auto; width: 330px; height: 54px;"></div>
{{ form_row(form.tpay.google_pay_token) }}

<script>
const ENVIRONMENT = '{{ productionMode ? 'PRODUCTION' : 'TEST' }}';
const TPAY_MERCHANT_ID = '{{ tpayMerchantId }}';
const GOOGLE_MERCHANT_ID = '{{ googleMerchantId }}';
const CURRENCY_CODE = 'PLN';
const COUNTRY_CODE = '{{ order.localeCode|slice(-2, 2) }}';
const TOTAL_PRICE = '{{ (order.total / 100)|format }}';

/**
* Define the version of the Google Pay API referenced when creating your
* configuration
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|apiVersion in PaymentDataRequest}
*/
const baseRequest = {
apiVersion: 2,
apiVersionMinor: 0
};

/**
* Card networks supported by your site and your gateway
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
*/
const allowedCardNetworks = ["MASTERCARD", "VISA"];

/**
* Card authentication methods supported by your site and your gateway
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
*/
const allowedCardAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];

/**
* Identify your gateway and your site's gateway merchant identifier
*
* The Google Pay API response will return an encrypted payment method capable
* of being charged by a supported gateway after payer authorization
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#gateway|PaymentMethodTokenizationSpecification}
*/
const tokenizationSpecification = {
type: "PAYMENT_GATEWAY",
parameters: {
gateway: "tpaycom",
gatewayMerchantId: TPAY_MERCHANT_ID,
},
};

/**
* Describe your site's support for the CARD payment method and its required
* fields
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
*/
const baseCardPaymentMethod = {
type: 'CARD',
parameters: {
allowedAuthMethods: allowedCardAuthMethods,
allowedCardNetworks: allowedCardNetworks
}
};

/**
* Describe your site's support for the CARD payment method including optional
* fields
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
*/
const cardPaymentMethod = Object.assign(
{},
baseCardPaymentMethod,
{
tokenizationSpecification: tokenizationSpecification
}
);

/**
* An initialized google.payments.api.PaymentsClient object or null if not yet set
*
* @see {@link getGooglePaymentsClient}
*/
let paymentsClient = null;

/**
* Configure your site's support for payment methods supported by the Google Pay
* API.
*
* Each member of allowedPaymentMethods should contain only the required fields,
* allowing reuse of this base request when determining a viewer's ability
* to pay and later requesting a supported payment method
*
* @returns {object} Google Pay API version, payment methods supported by the site
*/
function getGoogleIsReadyToPayRequest() {
return Object.assign(
{},
baseRequest,
{
allowedPaymentMethods: [baseCardPaymentMethod]
}
);
}

/**
* Configure support for the Google Pay API
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|PaymentDataRequest}
* @returns {object} PaymentDataRequest fields
*/
function getGooglePaymentDataRequest() {
const paymentDataRequest = Object.assign({}, baseRequest);
paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod];
paymentDataRequest.transactionInfo = getGoogleTransactionInfo();

if (ENVIRONMENT === 'PRODUCTION') {
paymentDataRequest.merchantInfo = {
merchantId: GOOGLE_MERCHANT_ID,
};
}

return paymentDataRequest;
}

/**
* Return an active PaymentsClient or initialize
*
* @see {@link https://developers.google.com/pay/api/web/reference/client#PaymentsClient|PaymentsClient constructor}
* @returns {google.payments.api.PaymentsClient} Google Pay API client
*/
function getGooglePaymentsClient() {
if ( paymentsClient === null ) {
paymentsClient = new google.payments.api.PaymentsClient({environment: ENVIRONMENT});
}
return paymentsClient;
}

/**
* Initialize Google PaymentsClient after Google-hosted JavaScript has loaded
*
* Display a Google Pay payment button after confirmation of the viewer's
* ability to pay.
*/
function onGooglePayLoaded() {
const paymentsClient = getGooglePaymentsClient();
paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
.then(function(response) {
if (response.result) {
addGooglePayButton();
prefetchGooglePaymentData();
}
})
.catch(function(err) {
console.error(err);
});
}

/**
* Add a Google Pay purchase button alongside an existing checkout button
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#ButtonOptions|Button options}
* @see {@link https://developers.google.com/pay/api/web/guides/brand-guidelines|Google Pay brand guidelines}
*/
function addGooglePayButton() {
const paymentsClient = getGooglePaymentsClient();
const button =
paymentsClient.createButton({
onClick: onGooglePaymentButtonClicked,
buttonColor: 'default',
buttonType: 'order',
buttonRadius: 4,
buttonSizeMode: 'fill',
allowedPaymentMethods: [baseCardPaymentMethod]
});
document.getElementById('cw-tpay-google-pay-container').appendChild(button);
}

/**
* Provide Google Pay API with a payment amount, currency, and amount status
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo|TransactionInfo}
* @returns {object} transaction info, suitable for use as transactionInfo property of PaymentDataRequest
*/
function getGoogleTransactionInfo() {
return {
countryCode: COUNTRY_CODE,
currencyCode: CURRENCY_CODE,
totalPriceStatus: 'FINAL',
totalPrice: TOTAL_PRICE
};
}

/**
* Prefetch payment data to improve performance
*
* @see {@link https://developers.google.com/pay/api/web/reference/client#prefetchPaymentData|prefetchPaymentData()}
*/
function prefetchGooglePaymentData() {
const paymentDataRequest = getGooglePaymentDataRequest();

// transactionInfo must be set but does not affect cache
paymentDataRequest.transactionInfo = {
currencyCode: CURRENCY_CODE,
countryCode: COUNTRY_CODE,
totalPriceStatus: 'NOT_CURRENTLY_KNOWN'
};

const paymentsClient = getGooglePaymentsClient();
paymentsClient.prefetchPaymentData(paymentDataRequest);
}

/**
* Show Google Pay payment sheet when Google Pay payment button is clicked
*/
function onGooglePaymentButtonClicked() {
const paymentDataRequest = getGooglePaymentDataRequest();
paymentDataRequest.transactionInfo = getGoogleTransactionInfo();

const paymentsClient = getGooglePaymentsClient();
paymentsClient.loadPaymentData(paymentDataRequest)
.then(function(paymentData) {
processPayment(paymentData);
})
.catch(function(err) {
console.error(err);
});
}
/**
* Process payment data returned by the Google Pay API
*
* @param {object} paymentData response from Google Pay API after user approves payment
* @see {@link https://developers.google.com/pay/api/web/reference/response-objects#PaymentData|PaymentData object reference}
*/
function processPayment(paymentData) {
const paymentToken = btoa(paymentData.paymentMethodData.tokenizationData.token);
const checkoutCompleteForm = document.querySelector('[name=sylius_checkout_complete]');
const googlePayTokenInput = document.querySelector('[name="sylius_checkout_complete[tpay][google_pay_token]"]')
const googlePayButton = document.getElementById('cw-tpay-google-pay-container').getElementsByTagName('button')[0];

if (googlePayButton !== undefined) {
googlePayButton.disabled = true;
}

googlePayTokenInput.value = paymentToken;
checkoutCompleteForm.submit();
}
</script>
<script async src="https://pay.google.com/gp/p/js/pay.js" onload="onGooglePayLoaded()"></script>
{% include '@CommerceWeaversSyliusTpayPlugin/shop/partial/google_pay_button.html.twig' with { order, form, gatewayConfig } %}
{% endif %}
8 changes: 8 additions & 0 deletions templates/shop/order/pay/apple_pay.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% set order = get_hookable_context().resource %}
{% set form = get_hookable_context().payment_form %}
{% set payment = form.vars.data %}
{% set gatewayConfig = payment.method.gatewayConfig %}

<div data-payment-details="tpay_apple_pay">
{% include '@CommerceWeaversSyliusTpayPlugin/shop/partial/apple_pay_button.html.twig' with { order, form, gatewayConfig } %}
</div>
8 changes: 8 additions & 0 deletions templates/shop/order/pay/google_pay.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% set order = get_hookable_context().resource %}
{% set form = get_hookable_context().payment_form %}
{% set payment = form.vars.data %}
{% set gatewayConfig = payment.method.gatewayConfig %}

<div data-payment-details="tpay_google_pay">
{% include '@CommerceWeaversSyliusTpayPlugin/shop/partial/google_pay_button.html.twig' with { order, form, gatewayConfig } %}
</div>
9 changes: 9 additions & 0 deletions templates/shop/partial/apple_pay_button.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<input type="hidden" data-apple-pay-amount value="{{ order.total|cw_tpay_convert_minor_to_major_currency }}">
<input type="hidden" data-apple-pay-merchant-identifier value="{{ cw_tpay_get_gateway_config_value(gatewayConfig, 'apple_pay_merchant_id') }}">
<input type="hidden" data-apple-pay-currency value="{{ order.currencyCode }}">
<input type="hidden" data-apple-pay-channel-name value="{{ order.channel.name }}" />

<apple-pay-button data-apple-pay-button buttonstyle="black" type="plain" locale="{{ order.localeCode|replace({'_': '-'}) }}"></apple-pay-button>
{{ form_row(form.tpay.apple_pay_token, { attr: { 'data-apple-pay-token-input': '' } }) }}

<script crossorigin src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"></script>
Loading
Loading