Stripe: ApplePay/Payment Request Button (#988)

As discussed, this is a WIP for integrating Stripe's Payment Request Buttons (with also includes the ApplePay-Button on iOS-devices).

Todos:
- [x] Payment Request Button is still displayed, even when a card has already been tokenized (when going back in the order-flow)
- [x] The domains used need to be verified using the Stripe API to enable ApplePay: https://stripe.com/docs/stripe-js/elements/payment-request-button#verifying-your-domain-with-apple-pay
- [x] Migration: Get the account-country for existing Stripe Connect users
- [x] Migration: Verify the domains using the above mentioned API for existing users
- [x] Converting the chargeable amount is not right for non-decimal currencies like JPY

Other considerations:
- On iOS-devices using Safari (probably also on MacBooks, etc. - not tested), the [regular payment request button](https://user-images.githubusercontent.com/157270/38515749-f53f8392-3be9-11e8-8917-61ef78dd354a.png) is automatically replaced with a [buy with Apple Pay button](https://docs-assets.developer.apple.com/published/094d0eb90e/988c36a8-a43c-4ff9-85ef-beda16c4b7c9.png).
- On all other platforms, the generic payment request button is displayed. Even if the device supports a specific payment provider like Google Pay, Microsoft Wallet, Samsung Pay, etc., the generic button will first offer the cards saved within the webbrowser in addition to the other payment methods. Only upon selecting the specific payment provider like GPay, the corresponding payment flow is started.
- Right now, the rendering of the payment button is completely in the hands of Stripe. Once pretix takes on the task of doing this, we should try to detect if the browser supports well known payment methods like GPay in addition to the browser-saved cards. If that's the case, we should add the corresponding marks onto the "Pay Now"-Button (like [this](https://developers.google.com/pay/api/images/brand-guidelines/google-pay-mark.png), [this](https://assets.pcmag.com/media/images/490984-samsung-pay.png?width=1600&height=900), or [this](https://www.firstffcu.com/images/MS-Wallet_stacked_rgb_grey.png)), so the customer can identify the purpose of the button easier.

- [x] Also, all of this is still based against the pretix 1.x codebase ;-)
This commit is contained in:
Martin Gross
2018-08-15 09:22:31 +02:00
committed by Raphael Michel
parent 673a4e6805
commit a4ced609cd
19 changed files with 309 additions and 18 deletions

View File

@@ -15,6 +15,7 @@ from django.urls import reverse
from django.utils.crypto import get_random_string
from django.utils.http import urlquote
from django.utils.translation import pgettext, ugettext, ugettext_lazy as _
from django_countries import countries
from pretix import __version__
from pretix.base.decimal import round_decimal
@@ -25,7 +26,12 @@ from pretix.base.settings import SettingsSandbox
from pretix.helpers.urls import build_absolute_uri as build_global_uri
from pretix.multidomain.urlreverse import build_absolute_uri
from pretix.plugins.stripe.forms import StripeKeyValidator
from pretix.plugins.stripe.models import ReferencedStripeObject
from pretix.plugins.stripe.models import (
ReferencedStripeObject, RegisteredApplePayDomain,
)
from pretix.plugins.stripe.tasks import (
get_stripe_account_key, stripe_verify_domain,
)
logger = logging.getLogger('pretix.plugins.stripe')
@@ -109,6 +115,9 @@ class StripeSettingsHolder(BasePaymentProvider):
else:
return {}
else:
allcountries = list(countries)
allcountries.insert(0, ('', _('Select country')))
fields = [
('publishable_key',
forms.CharField(
@@ -128,6 +137,13 @@ class StripeSettingsHolder(BasePaymentProvider):
StripeKeyValidator(['sk_', 'rk_']),
),
)),
('merchant_country',
forms.ChoiceField(
choices=allcountries,
label=_('Merchant country'),
help_text=_('The country in which your Stripe-account is registred in. Usually, this is your '
'country of residence.'),
)),
]
d = OrderedDict(
fields + [
@@ -235,9 +251,12 @@ class StripeMethod(BasePaymentProvider):
places = settings.CURRENCY_PLACES.get(self.event.currency, 2)
return round_decimal(float(cents) / (10 ** places), self.event.currency)
def _get_amount(self, payment):
def _decimal_to_int(self, amount):
places = settings.CURRENCY_PLACES.get(self.event.currency, 2)
return int(payment.amount * 10 ** places)
return int(amount * 10 ** places)
def _get_amount(self, payment):
return self._decimal_to_int(payment.amount)
@property
def api_kwargs(self):
@@ -510,7 +529,11 @@ class StripeCC(StripeMethod):
public_name = _('Credit card')
method = 'cc'
def payment_form_render(self, request) -> str:
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():
stripe_verify_domain.apply_async(args=(self.event.pk, request.host))
ui = self.settings.get('ui', default='pretix')
if ui == 'checkout':
template = get_template('pretixplugins/stripe/checkout_payment_form_stripe_checkout.html')
@@ -519,6 +542,7 @@ class StripeCC(StripeMethod):
ctx = {
'request': request,
'event': self.event,
'total': self._decimal_to_int(total),
'settings': self.settings,
}
return template.render(ctx)