forked from CGM_Public/pretix_original
PProv: Implement detection of wallets such as Google Pay and Apple Pay
This commit is contained in:
@@ -249,7 +249,7 @@ class SecurityMiddleware(MiddlewareMixin):
|
||||
|
||||
h = {
|
||||
'default-src': ["{static}"],
|
||||
'script-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
|
||||
'script-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com', 'https://pay.google.com'],
|
||||
'object-src': ["'none'"],
|
||||
'frame-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
|
||||
'style-src': ["{static}", "{media}"],
|
||||
|
||||
@@ -78,6 +78,16 @@ from pretix.presale.views.cart import cart_session, get_or_create_cart_id
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WalletQueries:
|
||||
APPLEPAY = 'applepay'
|
||||
GOOGLEPAY = 'googlepay'
|
||||
|
||||
WALLETS = (
|
||||
(APPLEPAY, pgettext_lazy('payment', 'Apple Pay')),
|
||||
(GOOGLEPAY, pgettext_lazy('payment', 'Google Pay')),
|
||||
)
|
||||
|
||||
|
||||
class PaymentProviderForm(Form):
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
@@ -436,6 +446,16 @@ class BasePaymentProvider:
|
||||
d['_restrict_to_sales_channels']._as_type = list
|
||||
return d
|
||||
|
||||
@property
|
||||
def walletqueries(self):
|
||||
"""
|
||||
A list of wallet payment methods that should be dynamically joined to the public name of the payment method,
|
||||
if they are available to the user.
|
||||
The detection is made on a best effort basis with no guarantees of correctness and actual availability.
|
||||
Wallets that pretix can check for are exposed through pretix.base.payment.WalletQueries.
|
||||
"""
|
||||
return []
|
||||
|
||||
def settings_form_clean(self, cleaned_data):
|
||||
"""
|
||||
Overriding this method allows you to inject custom validation into the settings form.
|
||||
|
||||
@@ -59,7 +59,7 @@ from pretix import __version__
|
||||
from pretix.base.decimal import round_decimal
|
||||
from pretix.base.forms import SecretKeySettingsField
|
||||
from pretix.base.models import Event, OrderPayment, OrderRefund, Quota
|
||||
from pretix.base.payment import BasePaymentProvider, PaymentException
|
||||
from pretix.base.payment import BasePaymentProvider, PaymentException, WalletQueries
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
@@ -747,6 +747,11 @@ class StripeCC(StripeMethod):
|
||||
public_name = _('Credit card')
|
||||
method = 'cc'
|
||||
|
||||
@property
|
||||
def walletqueries(self):
|
||||
# ToDo: Check against Stripe API, if ApplePay and GooglePay are even activated/available
|
||||
return [WalletQueries.APPLEPAY, WalletQueries.GOOGLEPAY]
|
||||
|
||||
def payment_form_render(self, request, total) -> str:
|
||||
account = get_stripe_account_key(self)
|
||||
if not RegisteredApplePayDomain.objects.filter(account=account, domain=request.host).exists():
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
{% load money %}
|
||||
{% load bootstrap3 %}
|
||||
{% load rich_text %}
|
||||
{% block custom_header %}
|
||||
{{ block.super }}
|
||||
{% include "pretixpresale/event/fragment_walletdetection_head.html" %}
|
||||
{% endblock %}
|
||||
{% block inner %}
|
||||
{% if current_payments %}
|
||||
<p>{% trans "You already selected the following payment methods:" %}</p>
|
||||
@@ -71,7 +75,8 @@
|
||||
{% if selected == p.provider.identifier %}checked="checked"{% endif %}
|
||||
id="input_payment_{{ p.provider.identifier }}"
|
||||
aria-describedby="payment_{{ p.provider.identifier }}"
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}"/>
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}"
|
||||
data-wallets="{{ p.provider.walletqueries|join:"|" }}" />
|
||||
<label for="input_payment_{{ p.provider.identifier }}"><strong>{{ p.provider.public_name }}</strong></label>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{% load static %}
|
||||
{% load compress %}
|
||||
|
||||
{% compress js %}
|
||||
<script type="text/javascript" src="{% static "pretixpresale/js/walletdetection.js" %}"></script>
|
||||
{% endcompress %}
|
||||
@@ -3,6 +3,10 @@
|
||||
{% load eventurl %}
|
||||
{% load money %}
|
||||
{% block title %}{% trans "Change payment method" %}{% endblock %}
|
||||
{% block custom_header %}
|
||||
{{ block.super }}
|
||||
{% include "pretixpresale/event/fragment_walletdetection_head.html" %}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
{% blocktrans trimmed with code=order.code %}
|
||||
@@ -29,7 +33,8 @@
|
||||
<input type="radio" name="payment" value="{{ p.provider.identifier }}"
|
||||
data-parent="#payment_accordion"
|
||||
{% if selected == p.provider.identifier %}checked="checked"{% endif %}
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}" />
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}"
|
||||
data-wallets="{{ p.provider.walletqueries|join:"|" }}"/>
|
||||
<strong>{{ p.provider.public_name }}</strong>
|
||||
</label>
|
||||
</h4>
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
'use strict';
|
||||
|
||||
var walletdetection = {
|
||||
applepay: function () {
|
||||
// This is a weak check for Apple Pay - in order to do a proper check, we would need to also call
|
||||
// canMakePaymentsWithActiveCard(merchantIdentifier)
|
||||
|
||||
return !!(window.ApplePaySession && window.ApplePaySession.canMakePayments());
|
||||
},
|
||||
googlepay: function () {
|
||||
// Checking for Google Pay is a little bit more involved, since it requires including the Google Pay JS SDK, and
|
||||
// providing a lot of information.
|
||||
// So for the time being, we only check if Google Pay is available in TEST-mode, which should hopefully give us a
|
||||
// good enough idea if Google Pay could be present on this device; even though there are still a lot of other
|
||||
// factors that could inhibit Google Pay from actually being offered to the customer.
|
||||
|
||||
const baseRequest = {
|
||||
apiVersion: 2,
|
||||
apiVersionMinor: 0
|
||||
};
|
||||
const tokenizationSpecification = {
|
||||
type: 'PAYMENT_GATEWAY',
|
||||
parameters: {
|
||||
'gateway': 'example',
|
||||
'gatewayMerchantId': 'exampleGatewayMerchantId'
|
||||
}
|
||||
};
|
||||
const allowedCardNetworks = ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"];
|
||||
const allowedCardAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];
|
||||
const baseCardPaymentMethod = {
|
||||
type: 'CARD',
|
||||
parameters: {
|
||||
allowedAuthMethods: allowedCardAuthMethods,
|
||||
allowedCardNetworks: allowedCardNetworks
|
||||
}
|
||||
};
|
||||
const cardPaymentMethod = Object.assign(
|
||||
{tokenizationSpecification: tokenizationSpecification},
|
||||
baseCardPaymentMethod
|
||||
);
|
||||
|
||||
return $.ajax({
|
||||
url: 'https://pay.google.com/gp/p/js/pay.js',
|
||||
dataType: 'script',
|
||||
success: function () {
|
||||
const paymentsClient = new google.payments.api.PaymentsClient({environment: 'TEST'});
|
||||
const isReadyToPayRequest = Object.assign({}, baseRequest);
|
||||
isReadyToPayRequest.allowedPaymentMethods = [baseCardPaymentMethod];
|
||||
|
||||
paymentsClient.isReadyToPay(isReadyToPayRequest)
|
||||
.then(function (response) {
|
||||
if (response.result) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
name_map: {
|
||||
applepay: gettext('Apple Pay'),
|
||||
googlepay: gettext('Google Pay'),
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
let requestedWallets = Array();
|
||||
let paymentMethods = $('[data-wallets][data-wallets!=""]');
|
||||
|
||||
paymentMethods.each(function () {
|
||||
let $s = $(this);
|
||||
requestedWallets = requestedWallets.concat($s.data("wallets").split("|"));
|
||||
})
|
||||
|
||||
// Filtering out any doubles
|
||||
requestedWallets = new Set(requestedWallets);
|
||||
|
||||
// Perform the actual check *only* on the requested wallets, if they are supported by the browser
|
||||
let availableWallets = Array();
|
||||
requestedWallets.forEach(function (it) {
|
||||
if (walletdetection[it]()) {
|
||||
availableWallets.push(it);
|
||||
}
|
||||
});
|
||||
|
||||
paymentMethods.each(function () {
|
||||
let $s = $(this);
|
||||
let wallets = Array();
|
||||
|
||||
// Run the translation on the available wallet strings before pushing them out.
|
||||
$s.data("wallets").split("|").forEach(function (it) {
|
||||
if (availableWallets.includes(it)) {
|
||||
wallets.push(gettext(walletdetection.name_map[it]));
|
||||
}
|
||||
})
|
||||
|
||||
// In case there is no wallets available, we do not want to flicker the screen
|
||||
if (wallets.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The first selector is used on the regular payment-step of the checkout flow
|
||||
// The second selector is used for the payment method change view.
|
||||
// In the long run, the layout of both pages should be adjusted to be one.
|
||||
let textselector = $s.next('label').find('strong');
|
||||
let textselector2 = $s.next("strong");
|
||||
textselector.fadeOut(300, function () {
|
||||
wallets.unshift(textselector.text());
|
||||
textselector.text(wallets.join(", "));
|
||||
textselector.fadeIn(300);
|
||||
});
|
||||
textselector2.fadeOut(300, function () {
|
||||
wallets.unshift(textselector2.text());
|
||||
textselector2.text(wallets.join(", "));
|
||||
textselector2.fadeIn(300);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user