From 951e498089f6d00c412e0283839a44f66dc40bff Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 14 Oct 2020 18:42:34 +0200 Subject: [PATCH] Use Stripe's new connect onboardning --- src/pretix/base/orderimport.py | 5 +- src/pretix/plugins/stripe/payment.py | 16 +-- src/pretix/plugins/stripe/signals.py | 14 ++- .../pretixplugins/stripe/presale_head.html | 8 +- src/pretix/plugins/stripe/urls.py | 6 +- src/pretix/plugins/stripe/views.py | 116 +++++++----------- 6 files changed, 69 insertions(+), 96 deletions(-) diff --git a/src/pretix/base/orderimport.py b/src/pretix/base/orderimport.py index 1b49ab382f..8ba6a47090 100644 --- a/src/pretix/base/orderimport.py +++ b/src/pretix/base/orderimport.py @@ -18,8 +18,9 @@ from i18nfield.strings import LazyI18nString from pretix.base.channels import get_all_sales_channels from pretix.base.forms.questions import guess_country from pretix.base.models import ( - ItemVariation, OrderPosition, QuestionAnswer, QuestionOption, Seat, - Question) + ItemVariation, OrderPosition, Question, QuestionAnswer, QuestionOption, + Seat, +) from pretix.base.services.pricing import get_price from pretix.base.settings import ( COUNTRIES_WITH_STATE_IN_ADDRESS, PERSON_NAME_SCHEMES, diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py index c29db066af..7acc296be3 100644 --- a/src/pretix/plugins/stripe/payment.py +++ b/src/pretix/plugins/stripe/payment.py @@ -15,7 +15,6 @@ from django.db import transaction from django.http import HttpRequest from django.template.loader import get_template from django.urls import reverse -from django.utils.crypto import get_random_string from django.utils.safestring import mark_safe from django.utils.timezone import now from django.utils.translation import gettext, gettext_lazy as _, pgettext @@ -52,17 +51,10 @@ class StripeSettingsHolder(BasePaymentProvider): self.settings = SettingsSandbox('payment', 'stripe', event) def get_connect_url(self, request): - request.session['payment_stripe_oauth_event'] = request.event.pk - if 'payment_stripe_oauth_token' not in request.session: - request.session['payment_stripe_oauth_token'] = get_random_string(32) - return ( - "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={}&state={}" - "&scope=read_write&redirect_uri={}" - ).format( - self.settings.connect_client_id, - request.session['payment_stripe_oauth_token'], - urllib.parse.quote(build_global_uri('plugins:stripe:oauth.return')), - ) + return reverse('plugins:stripe:oauth.connect', kwargs={ + 'organizer': self.event.organizer.slug, + 'event': self.event.slug, + }) def settings_content_render(self, request): if self.settings.connect_client_id and not self.settings.secret_key: diff --git a/src/pretix/plugins/stripe/signals.py b/src/pretix/plugins/stripe/signals.py index ddccc9205f..f07527b712 100644 --- a/src/pretix/plugins/stripe/signals.py +++ b/src/pretix/plugins/stripe/signals.py @@ -38,15 +38,19 @@ def html_head_presale(sender, request=None, **kwargs): provider = StripeSettingsHolder(sender) url = resolve(request.path_info) + + if provider.settings.connect_client_id: + pubkey = provider.settings.connect_publishable_key + if (provider.settings.get('endpoint', 'live') == 'test' or sender.testmode): + pubkey = provider.settings.connect_test_publishable_key + else: + pubkey = provider.settings.publishable_key + if provider.settings.get('_enabled', as_type=bool) and ("checkout" in url.url_name or "order.pay" in url.url_name): template = get_template('pretixplugins/stripe/presale_head.html') ctx = { 'event': sender, - 'settings': provider.settings, - 'testmode': ( - (provider.settings.get('endpoint', 'live') == 'test' or sender.testmode) - and provider.settings.publishable_test_key - ) + 'pubkey': pubkey, } return template.render(ctx) else: diff --git a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/presale_head.html b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/presale_head.html index 6aa35780d7..a4c5146779 100644 --- a/src/pretix/plugins/stripe/templates/pretixplugins/stripe/presale_head.html +++ b/src/pretix/plugins/stripe/templates/pretixplugins/stripe/presale_head.html @@ -8,12 +8,8 @@ {% compress css %} {% endcompress %} -{% if testmode %} - -{% else %} - -{% endif %} + {% if settings.connect_user_id %} {% endif %} - + diff --git a/src/pretix/plugins/stripe/urls.py b/src/pretix/plugins/stripe/urls.py index 82af46c182..908d1d0551 100644 --- a/src/pretix/plugins/stripe/urls.py +++ b/src/pretix/plugins/stripe/urls.py @@ -4,8 +4,8 @@ from pretix.multidomain import event_url from .views import ( OrganizerSettingsFormView, ReturnView, ScaReturnView, ScaView, - applepay_association, oauth_disconnect, oauth_return, redirect_view, - webhook, + applepay_association, oauth_connect, oauth_disconnect, oauth_return, + redirect_view, webhook, ) event_patterns = [ @@ -29,6 +29,8 @@ organizer_patterns = [ urlpatterns = [ url(r'^control/event/(?P[^/]+)/(?P[^/]+)/stripe/disconnect/', oauth_disconnect, name='oauth.disconnect'), + url(r'^control/event/(?P[^/]+)/(?P[^/]+)/stripe/connect/', + oauth_connect, name='oauth.connect'), url(r'^control/organizer/(?P[^/]+)/stripeconnect/', OrganizerSettingsFormView.as_view(), name='settings.connect'), url(r'^_stripe/webhook/$', webhook, name='webhook'), diff --git a/src/pretix/plugins/stripe/views.py b/src/pretix/plugins/stripe/views.py index 61d9162cfb..e3e41510a4 100644 --- a/src/pretix/plugins/stripe/views.py +++ b/src/pretix/plugins/stripe/views.py @@ -2,7 +2,6 @@ import hashlib import json import logging -import requests import stripe from django.contrib import messages from django.core import signing @@ -29,6 +28,7 @@ from pretix.control.permissions import ( ) from pretix.control.views.event import DecoupleMixin from pretix.control.views.organizer import OrganizerDetailViewMixin +from pretix.helpers.urls import build_absolute_uri as build_global_uri from pretix.multidomain.urlreverse import eventreverse from pretix.plugins.stripe.forms import OrganizerStripeSettingsForm from pretix.plugins.stripe.models import ReferencedStripeObject @@ -57,87 +57,30 @@ def redirect_view(request, *args, **kwargs): @scopes_disabled() def oauth_return(request, *args, **kwargs): - if 'payment_stripe_oauth_event' not in request.session: + if 'payment_stripe_oauth_event' not in request.session or 'payment_stripe_oauth_account' not in request.session: messages.error(request, _('An error occurred during connecting with Stripe, please try again.')) return redirect(reverse('control:index')) event = get_object_or_404(Event, pk=request.session['payment_stripe_oauth_event']) - if request.GET.get('state') != request.session['payment_stripe_oauth_token']: - messages.error(request, _('An error occurred during connecting with Stripe, please try again.')) - return redirect(reverse('control:event.settings.payment.provider', kwargs={ - 'organizer': event.organizer.slug, - 'event': event.slug, - 'provider': 'stripe_settings' - })) - gs = GlobalSettingsObject() - testdata = {} + stripe.api_key = gs.settings.payment_stripe_connect_secret_key or gs.settings.payment_stripe_connect_test_secret_key try: - resp = requests.post('https://connect.stripe.com/oauth/token', data={ - 'grant_type': 'authorization_code', - 'client_secret': ( - gs.settings.payment_stripe_connect_secret_key or gs.settings.payment_stripe_connect_test_secret_key - ), - 'code': request.GET.get('code') - }) - data = resp.json() - - if 'error' not in data: - account = stripe.Account.retrieve( - data['stripe_user_id'], - api_key=gs.settings.payment_stripe_connect_secret_key or gs.settings.payment_stripe_connect_test_secret_key - ) + account = stripe.Account.retrieve(request.session['payment_stripe_oauth_account']) except: logger.exception('Failed to obtain OAuth token') messages.error(request, _('An error occurred during connecting with Stripe, please try again.')) else: - if 'error' not in data and data['livemode']: - try: - testresp = requests.post('https://connect.stripe.com/oauth/token', data={ - 'grant_type': 'refresh_token', - 'client_secret': gs.settings.payment_stripe_connect_test_secret_key, - 'refresh_token': data['refresh_token'] - }) - testdata = testresp.json() - except: - logger.exception('Failed to obtain OAuth token') - messages.error(request, _('An error occurred during connecting with Stripe, please try again.')) - return redirect(reverse('control:event.settings.payment.provider', kwargs={ - 'organizer': event.organizer.slug, - 'event': event.slug, - 'provider': 'stripe_settings' - })) + event.settings.payment_stripe_connect_user_id = account.id + event.settings.payment_stripe_connect_user_name = ( + account.get('business_profile', {}).get('name') or account.get('email') + ) + if request.session.get('payment_stripe_oauth_enable', False): + event.settings.payment_stripe__enabled = True + del request.session['payment_stripe_oauth_enable'] - if 'error' in data: - messages.error(request, _('Stripe returned an error: {}').format(data['error_description'])) - elif data['livemode'] and 'error' in testdata: - messages.error(request, _('Stripe returned an error: {}').format(testdata['error_description'])) - else: - messages.success(request, - _('Your Stripe account is now connected to pretix. You can change the settings in ' - 'detail below.')) - event.settings.payment_stripe_publishable_key = data['stripe_publishable_key'] - # event.settings.payment_stripe_connect_access_token = data['access_token'] we don't need it, right? - event.settings.payment_stripe_connect_refresh_token = data['refresh_token'] - event.settings.payment_stripe_connect_user_id = data['stripe_user_id'] - event.settings.payment_stripe_merchant_country = account.get('country') - if account.get('business_name') or account.get('display_name') or account.get('email'): - event.settings.payment_stripe_connect_user_name = ( - account.get('business_name') or account.get('display_name') or account.get('email') - ) - - if data['livemode']: - event.settings.payment_stripe_publishable_test_key = testdata['stripe_publishable_key'] - else: - event.settings.payment_stripe_publishable_test_key = event.settings.payment_stripe_publishable_key - - if request.session.get('payment_stripe_oauth_enable', False): - event.settings.payment_stripe__enabled = True - del request.session['payment_stripe_oauth_enable'] - - stripe_verify_domain.apply_async(args=(event.pk, get_domain_for_event(event))) + stripe_verify_domain.apply_async(args=(event.pk, get_domain_for_event(event))) return redirect(reverse('control:event.settings.payment.provider', kwargs={ 'organizer': event.organizer.slug, @@ -409,6 +352,41 @@ def oauth_disconnect(request, **kwargs): })) +@event_permission_required('can_change_event_settings') +def oauth_connect(request, **kwargs): + gs = GlobalSettingsObject() + stripe.api_key = gs.settings.payment_stripe_connect_secret_key or gs.settings.payment_stripe_connect_test_secret_key + + request.session['payment_stripe_oauth_event'] = request.event.pk + try: + account = stripe.Account.create( + type='standard', + metadata={ + 'organizer': request.organizer.slug, + } + ) + request.session['payment_stripe_oauth_account'] = account.stripe_id + account_link = stripe.AccountLink.create( + account=account.stripe_id, + return_url=build_global_uri('plugins:stripe:oauth.return'), + refresh_url=build_global_uri('plugins:stripe:oauth.connect', kwargs={ + 'organizer': request.organizer.slug, + 'event': request.event.slug, + }), + type='account_onboarding', + ) + except: + logger.exception('Failed to obtain account link') + messages.error(request, _('An error occurred during connecting with Stripe, please try again.')) + return redirect(reverse('control:event.settings.payment.provider', kwargs={ + 'organizer': request.event.organizer.slug, + 'event': request.event.slug, + 'provider': 'stripe_settings' + })) + + return redirect(account_link.url) + + @xframe_options_exempt def applepay_association(request, *args, **kwargs): r = render(request, 'pretixplugins/stripe/apple-developer-merchantid-domain-association')