#! /usr/bin/env python3.6 """ Python 3.6 or newer required. """ import stripe import json import os from flask import Flask, render_template, jsonify, request from dotenv import load_dotenv, find_dotenv import GlobalVariable as MYSY_GV from pymongo import ReturnDocument import prj_common as mycommon import inspect import sys, os import time from datetime import datetime from dateutil.relativedelta import relativedelta import email_mgt as email_mgt # Setup Stripe python client library load_dotenv(find_dotenv()) # For sample support and debugging, not required for production: stripe.set_app_info( 'stripe-samples/subscription-use-cases/fixed-price', version='0.0.1', url='https://github.com/stripe-samples/subscription-use-cases/fixed-price') stripe.api_version = '2020-08-27' stripe.api_key = "sk_test_51LUUfAAbmaEugrFTrWsfcBWZtbBh9r3HCa2sgeyikG808LjSk3bAdFhV6KxgRZ3vFxooa6RE0c5zBkTuOUrKkyjy00BrsIXAPs" #static_dir = str(os.path.abspath(os.path.join(__file__, "..", os.getenv("STATIC_DIR")))) static_dir = "./" app = Flask(__name__, static_folder=static_dir, static_url_path="", template_folder=static_dir) @app.route('/', methods=['GET']) def get_index(): return render_template('register.html') @app.route('/config', methods=['GET']) def get_config(): # Retrieves two prices with the lookup_keys # `sample_basic` and `sample_premium`. To # create these prices, you can use the Stripe # CLI fixtures command with the supplied # `seed.json` fixture file like so: # # stripe fixtures seed.json # prices = stripe.Price.list( ) return jsonify( publishableKey="pk_test_51LUUfAAbmaEugrFTI25uZBD3IFjbtaL6jUfRV83diDf7nco8worna4NGKhMHbPP71WCwT5EHFRdDNatxPrJWwgZ300kgH5EO4p", prices=prices.data, ) """ Creation d'un client dans Stripe """ def create_customer(diction): # Reads application/json and returns a response #data = json.loads(request.data) try: field_list = ['name', 'email', 'country', 'city'] # recuperation des infos du partenaire cust_name = "" if ("name" in diction.keys()): if diction['name']: cust_name = diction['name'] cust_email = "" if ("email" in diction.keys()): if diction['email']: cust_email = diction['email'] cust_country = "" if ("country" in diction.keys()): if diction['country']: cust_country = diction['country'] cust_city = "" if ("city" in diction.keys()): if diction['city']: cust_city = diction['city'] # Create a new customer object #customer = stripe.Customer.create(email=data['email']) customer = stripe.Customer.create( email=cust_email, name=cust_name, address={ "city": cust_city, "country": cust_country, }, ) if( customer and customer.id ): mycommon.myprint( str(inspect.stack()[0][3]) + " - le partenaire "+str(cust_email)+" a été correctement créé dans Stripe. Son Id = "+str(customer.id)) return True, str(customer.id) else: mycommon.myprint( str(inspect.stack()[0][3]) + " WARNING - Impossble de créer le partenaire " + str( cust_email) + " Stripe.") return False, False except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno)) return False, False """ Recuperation des mode de payement d'un customer """ def get_customer_payement_cards(diction): try: field_list = ['stripe_account_id'] # recuperation des infos du partenaire stripe_account_id = "" if ("stripe_account_id" in diction.keys()): if diction['stripe_account_id']: stripe_account_id = diction['stripe_account_id'] customer_payments = stripe.Customer.list_payment_methods( str(stripe_account_id), type="card", ) #print(" ### customer_payments = "+str(customer_payments.data)) RetObject = [] for val in customer_payments.data : my_card = {} my_card['nom_carte'] = str(val.billing_details.name) my_card['brand'] = str(val.card.brand) my_card['exp_month'] = str(val.card.exp_month) my_card['exp_year'] = str(val.card.exp_year) my_card['last4'] = str(val.card.last4) my_card['createdatetime'] = str( datetime.fromtimestamp(int(str(val.created))) ) RetObject.append(mycommon.JSONEncoder().encode(my_card)) print(" ### RetObject = "+str( RetObject )) return True, RetObject except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno)) return False, False """ Creation d'une carte de payement """ def create_update_payment_card(diction): try: field_list_obligatoire = ['name', 'number', 'exp_month', 'exp_year', 'cvc', 'customerid', 'stripe_paymentmethod_id'] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Impossible de créer el moyen de payement" name = "" if ("name" in diction.keys()): if diction['name']: name = diction['name'] number = "" if ("number" in diction.keys()): if diction['number']: number = diction['number'] exp_month = "" if ("exp_month" in diction.keys()): if diction['exp_month']: exp_month = diction['exp_month'] exp_year = "" if ("exp_year" in diction.keys()): if diction['exp_year']: exp_year = diction['exp_year'] cvc = "" if ("cvc" in diction.keys()): if diction['cvc']: cvc = diction['cvc'] customer_id = "" if ("customerid" in diction.keys()): if diction['customerid']: customer_id = diction['customerid'] stripe_paymentmethod_id = "" if ("stripe_paymentmethod_id" in diction.keys()): if diction['stripe_paymentmethod_id']: stripe_paymentmethod_id = diction['stripe_paymentmethod_id'] """ /!\ IMPORTANT : Si le champs "stripe_paymentmethod_id" est rempli alors c'est une mise à jour : a) on detache b) on créer la nouvelle carte c) on rattacjhe Si le champs "stripe_paymentmethod_id" est vide, alors c'est une creation + Attachement au client """ print(" ## stripe_paymentmethod_id existe. donc suppression de = "+str(stripe_paymentmethod_id)) if (len(stripe_paymentmethod_id) > 2): print(" ### UPDATE ==> detache") detache = stripe.PaymentMethod.detach( stripe_paymentmethod_id, ) #print(" #### detache "+str(detache)) #print(" ### CREATE ") new_card = stripe.PaymentMethod.create( type="card", card={ "number": number, "exp_month": int(exp_month), "exp_year": int(exp_year), "cvc": cvc, }, billing_details={ "name": str(name) }, ) #print(" new_card = "+str(new_card)) #print(" l'ID de la carte est : "+str(new_card.id)) attache = stripe.PaymentMethod.attach( str(new_card.id), customer=str(customer_id), ) #print(" attache = "+str(attache)) return True, new_card except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno)) return False, False """ Cette fonction confirme un abonnement """ def confirm_suscription(diction): try: field_list_obligatoire = ['stripe_payment_id',] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " Impossible de créer el moyen de payement" stripe_payment_id = "" if ("stripe_payment_id" in diction.keys()): if diction['stripe_payment_id']: stripe_payment_id = diction['stripe_payment_id'] print(" ### stripe_payment_id = ", stripe_payment_id) confirmation_payement = stripe.PaymentIntent.confirm( stripe_payment_id, payment_method="pm_card_visa", ) return True, confirmation_payement.id, confirmation_payement.status except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno)) return False, False, False, """ Cette fonction schedul un payeement """ def Schedule_Stripe_Payement(diction): try: field_list_obligatoire = ['token', ] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste ") return False, " - La valeur '" + val + "' n'est pas presente dans liste " mytoken = "" if ("token" in diction.keys()): if (str(diction['token'])): mytoken = str(diction['token']) # recuperation de la susbscription du client local_ret_val = MYSY_GV.dbname['partnair_account'].find_one({'token':mytoken,'active':'1', 'locked':'0'}) if( local_ret_val['stripe_subscription_id'] is None ): mycommon.myprint( str(inspect.stack()[0][3]) + " Impossible de trouver la suscritpion, planification annulée ") return False, " Impossible de trouver la suscritpion, planification annulée " schedul = stripe.SubscriptionSchedule.create( from_subscription=str(local_ret_val['stripe_subscription_id']), ) print(" ### Schedule id = ", schedul['id']) """ Mise à jour du partenaire avec la valeur du suscription id """ local_coll_name = MYSY_GV.dbname['partnair_account'] now = datetime.now() ret_val = local_coll_name.find_one_and_update({'token':mytoken, 'active': '1', 'locked': '0'}, {"$set": {"stripe_subscription_schedule_id": str(schedul['id']), "date_update": str(now)} }, upsert=False, return_document=ReturnDocument.AFTER ) if (ret_val is None or ret_val['_id'] is None): mycommon.myprint( str(inspect.stack()[0][3]) + " - planification des payement : impossible de mettre à jour le client") return False, False, "Planification des payement : impossible de mettre à jour le client" return True, " La planification des payement a bien été activée" except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno)) return False, " Impossible d'activer la plafinification recurrente du payement", def create_subscription(diction): # Simulating authenticated user. Lookup the logged in user in your # database, and set customer_id to the Stripe Customer ID of that user. customer_id = "" if ("customerid" in diction.keys()): if diction['customerid']: customer_id = diction['customerid'] # Extract the price ID from environment variables given the name # of the price passed from the front end. # # `price_id` is the an ID of a Price object on your account. # This was populated using Price's `lookup_key` in the /config endpoint price_id = "" if ("priceid" in diction.keys()): if diction['priceid']: price_id = diction['priceid'] try: # Create the subscription. Note we're using # expand here so that the API will return the Subscription's related # latest invoice, and that latest invoice's payment_intent # so we can collect payment information and confirm the payment on the front end. # Create the subscription subscription = stripe.Subscription.create( customer=customer_id, items=[{ 'price': price_id, }], payment_behavior='default_incomplete', expand=['latest_invoice.payment_intent'], ) print(" ### subscription ID =", subscription['id']) print(" ### customer_id = ", customer_id) ##print(" ### payment_intent = id = " + str(subscription.latest_invoice.payment_intent.id)) """ Mise à jour du partenaire avec la valeur du suscription id """ local_coll_name = MYSY_GV.dbname['partnair_account'] now = datetime.now() ret_val = local_coll_name.find_one_and_update({'stripe_account_id':customer_id, 'active':'1', 'locked':'0'}, {"$set": {"stripe_subscription_id":str(subscription['id']), "date_update": str(now)} }, upsert=False, return_document=ReturnDocument.AFTER ) if (ret_val is None or ret_val['_id'] is None): mycommon.myprint( str(inspect.stack()[0][3]) + " - Creation payement : impossible de mettre à jour le client") return False, False, "Creation payement : impossible de mettre à jour le client" return True, subscription.latest_invoice.payment_intent.id, subscription.latest_invoice.payment_intent.client_secret except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno)) return False, False, False, @app.route('/cancel-subscription', methods=['POST']) def cancel_subscription(): data = json.loads(request.data) try: # Cancel the subscription by deleting it deletedSubscription = stripe.Subscription.delete(data['subscriptionId']) return jsonify(subscription=deletedSubscription) except Exception as e: return jsonify(error=str(e)), 403 @app.route('/subscriptions', methods=['GET']) def list_subscriptions(): # Simulating authenticated user. Lookup the logged in user in your # database, and set customer_id to the Stripe Customer ID of that user. customer_id = request.cookies.get('customer') try: # Cancel the subscription by deleting it subscriptions = stripe.Subscription.list( customer=customer_id, status='all', expand=['data.default_payment_method'] ) return jsonify(subscriptions=subscriptions) except Exception as e: return jsonify(error=str(e)), 403 @app.route('/invoice-preview', methods=['GET']) def preview_invoice(): # Simulating authenticated user. Lookup the logged in user in your # database, and set customer_id to the Stripe Customer ID of that user. customer_id = request.cookies.get('customer') subscription_id = request.args.get('subscriptionId') new_price_lookup_key = request.args.get('newPriceLookupKey') try: # Retrieve the subscription subscription = stripe.Subscription.retrieve(subscription_id) # Retrive the Invoice invoice = stripe.Invoice.upcoming( customer=customer_id, subscription=subscription_id, subscription_items=[{ 'id': subscription['items']['data'][0].id, 'price': os.getenv(new_price_lookup_key), }], ) return jsonify(invoice=invoice) except Exception as e: return jsonify(error=str(e)), 403 @app.route('/update-subscription', methods=['POST']) def update_subscription(): data = json.loads(request.data) try: subscription = stripe.Subscription.retrieve(data['subscriptionId']) update_subscription = stripe.Subscription.modify( data['subscriptionId'], items=[{ 'id': subscription['items']['data'][0].id, 'price': os.getenv(data['newPriceLookupKey'].upper()), }] ) return jsonify(update_subscription) except Exception as e: return jsonify(error=str(e)), 403 """ Cette fonction arrete la prelevement d'un payement à la fin du mois en cours """ def stop_strip_subscription(diction): try: field_list_obligatoire = ['token',] for val in field_list_obligatoire: if val not in diction: mycommon.myprint( str(inspect.stack()[0][3]) + " - La valeur '" + val + "' n'est pas presente dans liste , toutes le données obligatoire ne sont pas fournies ") return False, " Impossible de supprimer le moyen de payement, toutes le données obligatoire ne sont pas fournies" mytoken = "" if ("token" in diction.keys()): if diction['token']: mytoken = diction['token'] retval = mycommon.check_partner_token_validity("", str(mytoken)) if retval is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - Le token n'est pas valide") return False, "Les informations d'identification ne sont pas valident" # Recuperation du recid du partenaire partner_recid = mycommon.get_parnter_recid_from_token(str(mytoken)) if partner_recid is False: mycommon.myprint(str(inspect.stack()[0][3]) + " - Impossible de recuperer le recid du partenaire") return False, " Les informations d'identification sont incorrectes" # recuperation de la susbscription du client local_ret_val = MYSY_GV.dbname['partnair_account'].find_one( {'token': mytoken, 'active': '1', 'locked': '0'}) if (local_ret_val['stripe_subscription_id'] is None): mycommon.myprint( str(inspect.stack()[0][3]) + " Impossible d'annuler le payement ") return False, " Impossible d'annuler le payement " #a = stripe.SubscriptionSchedule.release( str(local_ret_val['stripe_subscription_id']), ) a = stripe.Subscription.modify( str(local_ret_val['stripe_subscription_id']), cancel_at_period_end=True ) arret_abonnement = datetime.today().date() + relativedelta(months=+1) arrete_abonnement_formated = arret_abonnement.strftime("%d/%m/%Y") #mise à jour du partenaire local_coll_name = MYSY_GV.dbname['partnair_account'] now = datetime.now() ret_val = local_coll_name.find_one_and_update({'token': mytoken, 'active': '1', 'locked': '0'}, {"$set": {"end_date_abonnement": str(arrete_abonnement_formated), "date_update": str(now)} }, upsert=False, return_document=ReturnDocument.AFTER ) if (ret_val is None or ret_val['_id'] is None): mycommon.myprint( str(inspect.stack()[0][3]) + " - Arret abonnement : impossible de mettre à jour le client") return False, False, "Arret abonnement : : impossible de mettre à jour le client" partners_email = local_ret_val['email'] partners_nom = local_ret_val['nom'] partners_pack_service = local_ret_val['pack_service'] partners_date_arret = arrete_abonnement_formated local_ret_val = email_mgt.EmailStopAbonnement( partners_email, partners_pack_service, partners_date_arret, partners_nom) if( local_ret_val is False): mycommon.myprint( str(inspect.stack()[0][3]) + " - WARNING : Arret abonnement, Impossible d'envoyer le mail de notification au partenaire") return True, " Votre abonement va s'arreter le "+str(arrete_abonnement_formated) except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() mycommon.myprint(str(inspect.stack()[0][3]) + " -" + str(e) + " - ERRORRRR AT Line : " + str(exc_tb.tb_lineno)) return False, "Impossible d'annuler votre abonnement" @app.route('/webhook', methods=['POST']) def webhook_received(): # You can use webhooks to receive information about asynchronous payment events. # For more about our webhook events check out https://stripe.com/docs/webhooks. webhook_secret = os.getenv('STRIPE_WEBHOOK_SECRET') request_data = json.loads(request.data) if webhook_secret: # Retrieve the event by verifying the signature using the raw body and secret if webhook signing is configured. signature = request.headers.get('stripe-signature') try: event = stripe.Webhook.construct_event( payload=request.data, sig_header=signature, secret=webhook_secret) data = event['data'] except Exception as e: return e event_type = event['type'] else: data = request_data['data'] event_type = request_data['type'] data_object = data['object'] if event_type == 'invoice.payment_succeeded': if data_object['billing_reason'] == 'subscription_create': # The subscription automatically activates after successful payment # Set the payment method used to pay the first invoice # as the default payment method for that subscription subscription_id = data_object['subscription'] payment_intent_id = data_object['payment_intent'] # Retrieve the payment intent used to pay the subscription payment_intent = stripe.PaymentIntent.retrieve(payment_intent_id) # Set the default payment method stripe.Subscription.modify( subscription_id, default_payment_method=payment_intent.payment_method ) print("Default payment method set for subscription:" + payment_intent.payment_method) elif event_type == 'invoice.payment_failed': # If the payment fails or the customer does not have a valid payment method, # an invoice.payment_failed event is sent, the subscription becomes past_due. # Use this webhook to notify your user that their payment has # failed and to retrieve new card details. # print(data) print('Invoice payment failed: %s', event.id) elif event_type == 'invoice.finalized': # If you want to manually send out invoices to your customers # or store them locally to reference to avoid hitting Stripe rate limits. # print(data) print('Invoice finalized: %s', event.id) elif event_type == 'customer.subscription.deleted': # handle subscription cancelled automatically based # upon your subscription settings. Or if the user cancels it. # print(data) print('Subscription canceled: %s', event.id) return jsonify({'status': 'success'})