2
2
import json
3
3
from subscribie import settings
4
4
from subscribie .database import database # noqa
5
+ from subscribie .tasks import background_task
5
6
from flask import (
6
7
Blueprint ,
7
8
render_template ,
@@ -249,7 +250,7 @@ def update_payment_fulfillment(stripe_external_id):
249
250
@admin .route ("/stripe/charge" , methods = ["POST" , "GET" ])
250
251
@login_required
251
252
def stripe_create_charge ():
252
- """Charge an existing subscriber x ammount immediately
253
+ """Charge an existing subscriber x amount immediately
253
254
254
255
:param stripe_customer_id: Stripe customer id
255
256
:param amount: Positive integer amount to charge in smallest currency unit
@@ -267,7 +268,7 @@ def stripe_create_charge():
267
268
currency = data ["currency" ]
268
269
statement_descriptor_suffix = data ["statement_descriptor_suffix" ]
269
270
except Exception :
270
- # Assumme form submission
271
+ # Assume form submission
271
272
# Get stripe customer_id from subscribers subscription -> customer reference
272
273
person = Person .query .get (request .form .get ("person_id" ))
273
274
stripe_subscription_id = person .subscriptions [0 ].stripe_subscription_id
@@ -334,12 +335,84 @@ def stripe_create_charge():
334
335
return jsonify (paymentIntent .status )
335
336
336
337
338
+ @background_task
339
+ def do_pause_stripe_subscription_payment_collection (
340
+ subscription_id , pause_collection_behavior = "keep_as_draft" , app = None
341
+ ):
342
+ """Pause pause_stripe_subscription payment collections via its
343
+ Stripe subscription_id
344
+
345
+ Choices of `pause_collection_behavior` include:
346
+
347
+ - keep_as_draft (Subscribie default- Temporarily offer services for free, and
348
+ collect payment later)
349
+ - void (Temporarily offer services for free and never collect payment)
350
+ - mark_uncollectible (Temporarily offer services for free and
351
+ mark invoice as uncollectible)
352
+ See also: https://docs.stripe.com/billing/subscriptions/pause-payment
353
+ """
354
+ with app .app_context ():
355
+ stripe .api_key = get_stripe_secret_key ()
356
+ connect_account_id = get_stripe_connect_account_id ()
357
+
358
+ if subscription_id is None :
359
+ log .error ("subscription_id cannot be None" )
360
+ return False
361
+ try :
362
+ stripe_subscription = stripe .Subscription .retrieve (
363
+ subscription_id , stripe_account = connect_account_id
364
+ )
365
+ if stripe_subscription .status != "canceled" :
366
+ stripe_pause = stripe .Subscription .modify (
367
+ subscription_id ,
368
+ stripe_account = connect_account_id ,
369
+ pause_collection = {"behavior" : pause_collection_behavior },
370
+ )
371
+ # filtering for the pause_collection value
372
+ stripe_pause_filter = stripe_pause ["pause_collection" ]["behavior" ]
373
+
374
+ # adding the pause_collection status to the
375
+ # stripe_pause_collection column
376
+ pause_collection = Subscription .query .filter_by (
377
+ stripe_subscription_id = subscription_id
378
+ ).first ()
379
+
380
+ pause_collection .stripe_pause_collection = stripe_pause_filter
381
+ database .session .commit ()
382
+ log .debug (f"Subscription paused ({ subscription_id } )" )
383
+ else :
384
+ log .debug (
385
+ f"Skipping. Subscription { subscription_id } because it's canceled."
386
+ )
387
+ except Exception as e :
388
+ msg = f"Error pausing subscription ({ subscription_id } )"
389
+ log .error (f"{ msg } . { e } " )
390
+ raise
391
+
392
+
393
+ @background_task
394
+ def do_pause_all_stripe_subscriptions (app = None ):
395
+ # For each Subscription object, get it's Stripe subscription
396
+ # object and pause it using pause_stripe_subscription.
397
+ with app .app_context ():
398
+ subscriptions = Subscription .query .all ()
399
+ for subscription in subscriptions :
400
+ log .debug (f"Attempting to pause subscription { subscription .uuid } " )
401
+ stripe_subscription_id = subscription .stripe_subscription_id
402
+ try :
403
+ do_pause_stripe_subscription_payment_collection (
404
+ stripe_subscription_id , app = current_app
405
+ )
406
+ except Exception as e :
407
+ log .error (
408
+ f"Error trying to pause subscription { subscription .uuid } . Error: { e } " # noqa: E501
409
+ )
410
+
411
+
337
412
@admin .route ("/stripe/subscriptions/<subscription_id>/actions/pause" )
338
413
@login_required
339
414
def pause_stripe_subscription (subscription_id : str ):
340
415
"""Pause a Stripe subscription"""
341
- stripe .api_key = get_stripe_secret_key ()
342
- connect_account_id = get_stripe_connect_account_id ()
343
416
344
417
if "confirm" in request .args and request .args ["confirm" ] != "1" :
345
418
return render_template (
@@ -349,31 +422,24 @@ def pause_stripe_subscription(subscription_id: str):
349
422
)
350
423
if "confirm" in request .args and request .args ["confirm" ] == "1" :
351
424
try :
352
- stripe_pause = stripe .Subscription .modify (
353
- subscription_id ,
354
- stripe_account = connect_account_id ,
355
- pause_collection = {"behavior" : "void" },
425
+ do_pause_stripe_subscription_payment_collection (
426
+ subscription_id , pause_collection_behavior = "void" , app = current_app
356
427
)
357
- # filtering for the pause_collection value
358
- stripe_pause_filter = stripe_pause ["pause_collection" ]["behavior" ]
359
-
360
- # adding the pause_collection status to the stripe_pause_collection column
361
- pause_collection = Subscription .query .filter_by (
362
- stripe_subscription_id = subscription_id
363
- ).first ()
364
-
365
- pause_collection .stripe_pause_collection = stripe_pause_filter
366
- database .session .commit ()
367
-
368
428
flash ("Subscription paused" )
369
- except Exception as e :
370
- msg = "Error pausing subscription"
429
+ except Exception :
430
+ msg = f "Error pausing subscription ( { subscription_id } ) "
371
431
flash (msg )
372
- log .error (f"{ msg } . { e } " )
373
-
374
432
return redirect (url_for ("admin.subscribers" ))
375
433
376
434
435
+ @admin .route ("/stripe/subscriptions/all/actions/pause" )
436
+ @login_required
437
+ def pause_all_subscribers_subscriptions ():
438
+ """Bulk action to pause all subscriptions in the shop"""
439
+ do_pause_all_stripe_subscriptions () # Background task
440
+ return """All payment collections are being paused in the background. You can move away from this page.""" # noqa: E501
441
+
442
+
377
443
@admin .route ("/stripe/subscriptions/<subscription_id>/actions/resume" )
378
444
@login_required
379
445
def resume_stripe_subscription (subscription_id ):
@@ -525,7 +591,7 @@ def edit():
525
591
526
592
Note plans are immutable, when a change is made to plan, its old
527
593
plan is archived and a new plan is created with a new uuid. This is to
528
- protect data integriry and make sure plan history is retained, via its uuid.
594
+ protect data integrity and make sure plan history is retained, via its uuid.
529
595
If a user needs to change a subscription, they should change to a different
530
596
plan with a different uuid.
531
597
@@ -1030,7 +1096,7 @@ def stripe_connect():
1030
1096
log .error (e )
1031
1097
account = None
1032
1098
1033
- # Setup Stripe webhook endpoint if it dosent already exist
1099
+ # Setup Stripe webhook endpoint if it doesn't already exist
1034
1100
if account :
1035
1101
# Attempt to Updates an existing Account Capability to accept card payments
1036
1102
try :
@@ -1411,11 +1477,23 @@ def subscribers():
1411
1477
)
1412
1478
1413
1479
1480
+ @admin .route ("/subscribers/bulk-operations" )
1481
+ @login_required
1482
+ def subscribers_bulk_operations_index ():
1483
+
1484
+ num_active_subscribers = get_number_of_active_subscribers ()
1485
+ return render_template (
1486
+ "admin/subscribers_bulk_operations_index.html" ,
1487
+ num_active_subscribers = num_active_subscribers ,
1488
+ confirm = request .args .get ("confirm" ),
1489
+ )
1490
+
1491
+
1414
1492
@admin .route ("/recent-subscription-cancellations" )
1415
1493
@login_required
1416
1494
def show_recent_subscription_cancellations ():
1417
1495
"""Get the last 30 days subscription cancellations (if any)
1418
- Note: Stripe api only guarentees the last 30 days of events.
1496
+ Note: Stripe api only guarantees the last 30 days of events.
1419
1497
At time of writing this method performs no caching of events,
1420
1498
see StripeInvoice for possible improvements
1421
1499
"""
@@ -1439,7 +1517,7 @@ def show_recent_subscription_cancellations():
1439
1517
)
1440
1518
if person is None :
1441
1519
log .info (
1442
- f"""Person query retruned None- probably archived.\n
1520
+ f"""Person query returned None- probably archived.\n
1443
1521
Skipping Person with uuid { value .data .object .metadata .person_uuid } """
1444
1522
)
1445
1523
continue
@@ -1700,7 +1778,7 @@ def add_shop_admin():
1700
1778
form = AddShopAdminForm ()
1701
1779
if request .method == "POST" :
1702
1780
if form .validate_on_submit ():
1703
- # Check user dosent already exist
1781
+ # Check user doesn't already exist
1704
1782
email = escape (request .form ["email" ].lower ())
1705
1783
if User .query .filter_by (email = email ).first () is not None :
1706
1784
return f"Error, admin with email ({ email } ) already exists."
@@ -1747,7 +1825,7 @@ def delete_admin_confirmation(id: int):
1747
1825
else :
1748
1826
User .query .filter_by (id = id ).delete ()
1749
1827
database .session .commit ()
1750
- flash ("Account was deleted succesfully " )
1828
+ flash ("Account was deleted successfully " )
1751
1829
1752
1830
except Exception as e :
1753
1831
msg = "Error deleting the admin account"
@@ -1926,7 +2004,7 @@ def rename_shop_post():
1926
2004
1927
2005
@admin .route ("/announce-stripe-connect" , methods = ["GET" ])
1928
2006
def announce_shop_stripe_connect_ids ():
1929
- """Accounce this shop's stripe connect account id(s)
2007
+ """Announce this shop's stripe connect account id(s)
1930
2008
to the STRIPE_CONNECT_ACCOUNT_ANNOUNCER_HOST
1931
2009
- stripe_live_connect_account_id
1932
2010
- stripe_test_connect_account_id
@@ -2162,7 +2240,7 @@ def enable_geo_currency():
2162
2240
@admin .route ("/spamcheck/<string:account_name>" )
2163
2241
@login_required
2164
2242
def check_spam (account_name ) -> int :
2165
- """Check if shop name is likley to be spam or not"""
2243
+ """Check if shop name is likely to be spam or not"""
2166
2244
from subscribie .anti_spam_subscribie_shop_names .run import detect_spam_shop_name
2167
2245
2168
2246
return str (detect_spam_shop_name (account_name ))
0 commit comments