3535from django .urls import reverse
3636from django .urls import reverse_lazy
3737from django .utils import timezone
38- from django .utils .crypto import get_random_string
3938from django .utils .translation import gettext_lazy as _
4039from django .views import View
4140from django .views .generic import CreateView
@@ -2332,7 +2331,7 @@ def get_success_url(self):
23322331 )
23332332
23342333
2335- class PublisherStripeOauthConnectView (
2334+ class PublisherStripeConnectView (
23362335 PublisherAdminAccessMixin , UserPassesTestMixin , RedirectView
23372336):
23382337 """Redirect the user to the correct Stripe connect URL for the publisher."""
@@ -2347,92 +2346,92 @@ def get_redirect_url(self, *args, **kwargs):
23472346
23482347 publisher = self .get_object ()
23492348
2350- # Save a state nonce to verify that the Stripe oauth flow can't be replayed or forged
2351- stripe_state = get_random_string (30 )
2352- self .request .session ["stripe_state" ] = stripe_state
2353- self .request .session ["stripe_connect_publisher" ] = publisher .slug
2354-
2355- params = {
2356- "client_id" : settings .STRIPE_CONNECT_CLIENT_ID ,
2357- # "suggested_capabilities[]": "transfers",
2358- "stripe_user[email]" : self .request .user .email ,
2359- "state" : stripe_state ,
2360- "redirect_uri" : self .request .build_absolute_uri (
2361- reverse ("publisher_stripe_oauth_return" )
2362- ),
2363- }
2364- return f"https://connect.stripe.com/express/oauth/authorize?{ urllib .parse .urlencode (params )} "
2349+ # https://docs.stripe.com/api/accounts/create
2350+ account = stripe .Account .create (type = "express" )
2351+
2352+ # Users get redirected back to this page if the link expired, etc.
2353+ refresh_url = self .request .build_absolute_uri (
2354+ reverse ("publisher_settings" , args = [publisher .slug ])
2355+ )
2356+ # Users get redirected back to this page after completing the Stripe onboarding flow
2357+ # This doesn’t mean that all information has been collected or that there are no outstanding requirements on the account.
2358+ # This only means the flow was entered and exited properly.
2359+ # The return URL is also used if the user clicks "back to EthicalAds" in the Stripe flow.
2360+ self .request .session ["stripe_account_id" ] = account .stripe_id
2361+ return_url = self .request .build_absolute_uri (
2362+ reverse ("publisher_stripe_return" , args = [publisher .slug ])
2363+ )
2364+
2365+ account_link = stripe .AccountLink .create (
2366+ account = account .stripe_id ,
2367+ refresh_url = refresh_url ,
2368+ return_url = return_url ,
2369+ type = "account_onboarding" ,
2370+ )
2371+
2372+ return account_link .url
23652373
23662374 def get_object (self , queryset = None ): # pylint: disable=unused-argument
23672375 return get_object_or_404 (Publisher , slug = self .kwargs ["publisher_slug" ])
23682376
23692377
2370- @login_required
2371- def publisher_stripe_oauth_return (request ):
2372- """Handle the oauth return flow from Stripe - save the account on the publisher."""
2373- # A stripe token we passed when setup started - needs to be double checked
2374- state = request .GET .get ("state" , "" )
2375- oauth_code = request .GET .get ("code" , "" )
2378+ class PublisherStripeReturnView (
2379+ PublisherAdminAccessMixin , UserPassesTestMixin , RedirectView
2380+ ):
2381+ """
2382+ Stripe redirects to here after a publisher goes through the connect flow.
23762383
2377- if request .user .is_staff :
2378- publishers = Publisher .objects .all ()
2379- else :
2380- publishers = request .user .publishers .all ()
2384+ Just a note that we still need to check that the user completed the flow successfully.
2385+ See: https://docs.stripe.com/connect/express-accounts#return-user
2386+ """
23812387
2382- publisher = publishers .filter (
2383- slug = request .session .get ("stripe_connect_publisher" , "" )
2384- ).first ()
2388+ model = Publisher
2389+ permanent = False
23852390
2386- if state == request .session .get ("stripe_state" ) and publisher :
2387- response = None
2388- log .debug (
2389- "Using stripe auth code to connect publisher account. Publisher = [%s]" ,
2390- publisher ,
2391- )
2392- try :
2393- response = stripe .OAuth .token (
2394- grant_type = "authorization_code" , code = oauth_code
2391+ def get_redirect_url (self , * args , ** kwargs ):
2392+ if not settings .STRIPE_CONNECT_CLIENT_ID :
2393+ messages .error (self .request , _ ("Stripe is not configured" ))
2394+ return reverse ("dashboard-home" )
2395+
2396+ publisher = self .get_object ()
2397+ account_id = self .request .session .get ("stripe_account_id" , "" )
2398+
2399+ if not account_id :
2400+ messages .error (
2401+ self .request , _ ("There was a problem connecting your Stripe account" )
23952402 )
2396- except stripe .oauth_error .OAuthError :
2397- log .error ("Invalid Stripe authorization code: %s" , oauth_code )
2398- except Exception :
2399- log .error ("An unknown Stripe error occurred." )
2403+ log .error ("No account returned from Stripe." )
2404+ return reverse ("dashboard-home" )
24002405
2401- if response :
2402- connected_account_id = response [ "stripe_user_id" ]
2406+ # Get the account from Stripe to verify the connect workflow completed
2407+ account = stripe . Account . retrieve ( account_id )
24032408
2404- try :
2405- # Retrieve the Stripe connected account and save it on the publisher
2406- publisher .djstripe_account = Account .sync_from_stripe_data (
2407- stripe .Account .retrieve (connected_account_id )
2408- )
2409- except stripe .error .StripeError :
2410- log .exception (
2411- "Stripe returned a connected account, but it could not be retrieved."
2412- )
2409+ # This is the field we need to check to see if the account is fully set up
2410+ # https://docs.stripe.com/connect/express-accounts#return-user
2411+ if account .details_submitted :
2412+ publisher .djstripe_account = Account .sync_from_stripe_data (account )
24132413
24142414 # Deprecated field
2415- publisher .stripe_connected_account_id = connected_account_id
2415+ publisher .stripe_connected_account_id = account_id
24162416
24172417 publisher .payout_method = PAYOUT_STRIPE
24182418 publisher .save ()
2419- messages .success (request , _ ("Successfully connected your Stripe account" ))
2420-
2421- # Delete saved stripe state
2422- del request .session ["stripe_state" ]
2423- del request .session ["stripe_connect_publisher" ]
2419+ messages .success (
2420+ self .request , _ ("Successfully connected your Stripe account" )
2421+ )
2422+ else :
2423+ messages .warning (
2424+ self .request ,
2425+ _ (
2426+ "The Stripe setup wasn't completed. "
2427+ "You can resume the process at any time or try a different payout method."
2428+ ),
2429+ )
24242430
2425- return redirect (reverse ("publisher_main" , args = [publisher .slug ]))
2426- else :
2427- log .warning (
2428- "Stripe state or publisher do not check out. State = [%s], Publisher = [%s]" ,
2429- state ,
2430- publisher ,
2431- )
2431+ return reverse ("publisher_settings" , args = [publisher .slug ])
24322432
2433- messages .error (request , _ ("There was a problem connecting your Stripe account" ))
2434- log .error ("There was a problem connecting a Stripe account." )
2435- return redirect (reverse ("dashboard-home" ))
2433+ def get_object (self , queryset = None ): # pylint: disable=unused-argument
2434+ return get_object_or_404 (Publisher , slug = self .kwargs ["publisher_slug" ])
24362435
24372436
24382437class PublisherPayoutListView (PublisherAccessMixin , UserPassesTestMixin , ListView ):
0 commit comments