Пример #1
0
def upcoming_invoices():
    get_stripe_secret_key()
    all_subscriptions = Subscription.query.all()
    subscriptions = []
    for subscription in all_subscriptions:
        if subscription.upcoming_invoice() is not None:
            subscriptions.append(subscription)

    return render_template(
        "admin/upcoming_invoices.html",
        subscriptions=subscriptions,
        datetime=datetime,
    )
Пример #2
0
def stripe_onboarding():
    # Determine if in live or test mode
    payment_provider = PaymentProvider.query.first()
    stripe.api_key = get_stripe_secret_key()

    company = Company.query.first()

    # Use existing stripe_connect_account_id, otherwise create stripe connect account
    try:
        print("Trying if there's an existing stripe account")
        account = get_stripe_connect_account()
        print(f"Yes, stripe account found: {account.id}")
    except (
            stripe.error.PermissionError,
            stripe.error.InvalidRequestError,
            AttributeError,
    ):
        print("Could not find a stripe account, Creating stripe account")
        account = create_stripe_connect_account(company)
        if payment_provider.stripe_livemode:
            payment_provider.stripe_live_connect_account_id = account.id
        else:
            payment_provider.stripe_test_connect_account_id = account.id

    database.session.commit()

    session["account_id"] = account.id
    account_link_url = _generate_account_link(account.id)
    try:
        return jsonify({"url": account_link_url})
    except Exception as e:
        return jsonify(error=str(e)), 403
Пример #3
0
def update_stripe_subscription_statuses():
    """Update Stripe subscriptions with their current status
    by querying Stripe api"""
    stripe.api_key = get_stripe_secret_key()
    connect_account = get_stripe_connect_account()
    if stripe.api_key is None:
        logging.error(
            "Stripe api key not set refusing to update subscription statuses")
    if connect_account is None:
        logging.error(
            "Stripe connect account not set. Refusing to update subscription statuses"
        )
    if stripe_connect_active():
        for subscription in Subscription.query.all():
            try:
                stripeSubscription = stripe.Subscription.retrieve(
                    stripe_account=connect_account.id,
                    id=subscription.stripe_subscription_id,
                )
                subscription.stripe_status = stripeSubscription.status
                database.session.commit()
            except Exception as e:
                logging.warning(
                    f"Could not update stripe subscription status: {e}")
    else:
        logging(
            "Refusing to update subscription status since Stripe connect is not active"
        )
Пример #4
0
def stripe_process_event_payment_intent_succeeded(event):
    """Store suceeded payment_intents as transactions
    These events will fire both at the begining of a subscription,
    and also at each successful recuring billing cycle.

    We use backoff because webhook event order is not guaranteed
    """
    logging.info("Processing payment_intent.succeeded")

    data = event["data"]["object"]
    stripe.api_key = get_stripe_secret_key()
    subscribie_subscription = None

    # Get the Subscribie subscription id from Stripe subscription metadata
    try:
        subscribie_checkout_session_id = data["metadata"][
            "subscribie_checkout_session_id"]
    except KeyError:
        # There is no metadata on the event if its an upfront payment
        # So try and get it via the data['invoice'] attribute
        invoice_id = data["invoice"]
        invoice = stripe.Invoice.retrieve(stripe_account=event["account"],
                                          id=invoice_id)
        # Fetch subscription via its invoice
        subscription = stripe.Subscription.retrieve(
            stripe_account=event["account"], id=invoice.subscription)
        subscribie_checkout_session_id = subscription.metadata[
            "subscribie_checkout_session_id"]
    except Exception as e:
        msg = f"Unable to get subscribie_checkout_session_id from event\n{e}"
        logging.error(msg)
        return msg, 500

    # Locate the Subscribie subscription by its subscribie_checkout_session_id
    subscribie_subscription = (database.session.query(Subscription).filter_by(
        subscribie_checkout_session_id=subscribie_checkout_session_id).first())

    # Store the transaction in Transaction model
    # (regardless of if subscription or just one-off plan)
    if (database.session.query(Transaction).filter_by(
            external_id=data["id"]).first() is None):
        transaction = Transaction()
        transaction.amount = data["amount"]
        transaction.payment_status = ("paid" if data["status"] == "succeeded"
                                      else data["status"])
        transaction.external_id = data["id"]
        transaction.external_src = "stripe"
        if subscribie_subscription is not None:
            transaction.person = subscribie_subscription.person
            transaction.subscription = subscribie_subscription
        else:
            print("WARNING: subscribie_subscription not found for this\
              payment_intent.succeeded. The metadata was:")
            print(data["metadata"])
            raise Exception
        database.session.add(transaction)
        database.session.commit()
    return "OK", 200
Пример #5
0
def invoices():
    stripe.api_key = get_stripe_secret_key()
    connect_account = get_stripe_connect_account()
    invoices = stripe.Invoice.list(stripe_account=connect_account.id)

    return render_template(
        "admin/invoices.html",
        invoices=invoices,
        datetime=datetime,
    )
Пример #6
0
def fetch_stripe_upcoming_invoices():
    """Fetch all Stripe upcoming invoices and populate the invoices table"""
    all_subscriptions = Subscription.query.all()
    upcoming_invoices = []
    stripe.api_key = get_stripe_secret_key()
    connect_account = get_stripe_connect_account()

    # Prepare to delete all previous upcomingInvoice
    UpcomingInvoice.query.delete(
    )  # don't commit until finished fetching latest collection # noqa
    for subscription in all_subscriptions:
        try:
            log.info(
                f"Getting upcoming invoice for Stripe subscription: {subscription.stripe_subscription_id}"  # noqa
            )
            if subscription.stripe_subscription_id is not None:
                upcoming_invoice = stripe.Invoice.upcoming(
                    subscription=subscription.stripe_subscription_id,
                    stripe_account=connect_account.id,
                )
                upcoming_invoices.append(upcoming_invoice)
                # Store the UpcomingInvoice
                upcomingInvoice = UpcomingInvoice()
                upcomingInvoice.subscription = subscription
                upcomingInvoice.stripe_invoice_id = None
                upcomingInvoice.stripe_subscription_id = (
                    subscription.stripe_subscription_id)
                upcomingInvoice.stripe_invoice_status = upcoming_invoice.status
                upcomingInvoice.stripe_amount_due = upcoming_invoice.amount_due
                upcomingInvoice.stripe_amount_paid = upcoming_invoice.amount_paid
                upcomingInvoice.stripe_next_payment_attempt = (
                    upcoming_invoice.next_payment_attempt)
                upcomingInvoice.stripe_currency = upcoming_invoice.currency

                database.session.add(upcomingInvoice)

        except stripe.error.InvalidRequestError as e:
            log.error(
                f"Cannot get stripe subscription id: {subscription.stripe_subscription_id}, {e}"  # noqa
            )
        except Exception as e:
            log.error(
                f"Error checking for upcoming invoice for {subscription.id}, {e}"
            )
        database.session.commit()
Пример #7
0
    def upcoming_invoice(self):
        """Return the upcoming invoice (if exists) associated with this Subscription"""
        stripe.api_key = get_stripe_secret_key()
        stripe_connect_account_id = get_stripe_connect_account_id()

        if self.stripe_subscription_id is not None:
            try:
                upcoming_invoice = stripe.Invoice.upcoming(
                    subscription=self.stripe_subscription_id,
                    stripe_account=stripe_connect_account_id,
                )
                return upcoming_invoice
            except stripe.error.InvalidRequestError as e:
                print(
                    f"Cannot get stripe subscription id: {self.stripe_subscription_id}"
                )
                print(e)
        return None
Пример #8
0
    def stripe_subscription_active(self):
        if self.stripe_subscription_id is not None:

            stripe.api_key = get_stripe_secret_key()
            connect_account = get_stripe_connect_account()
            try:
                subscription = stripe.Subscription.retrieve(
                    stripe_account=connect_account.id,
                    id=self.stripe_subscription_id)
                if subscription.pause_collection is not None:
                    return False
                elif subscription.status == "active":
                    return True
            except stripe.error.InvalidRequestError as e:
                logging.error("Could not get stripe subscription status")
                logging.error(e)

        return False
Пример #9
0
def get_subscription_status(stripe_external_id: str) -> str:
    status_on_error = "Unknown"
    if stripe_external_id is None:
        return status_on_error
    try:
        stripe.api_key = get_stripe_secret_key()
        connect_account = get_stripe_connect_account()
        subscription = stripe.Subscription.retrieve(
            stripe_account=connect_account.id, id=stripe_external_id)
        if subscription.pause_collection is not None:
            return "paused"
        else:
            return "active"
    except stripe.error.InvalidRequestError as e:
        print(e)
        return status_on_error
    except ValueError as e:
        print(e)
        return status_on_error
Пример #10
0
def pause_stripe_subscription(subscription_id: str):
    """Pause a Stripe subscription"""
    stripe.api_key = get_stripe_secret_key()
    connect_account_id = get_stripe_connect_account_id()

    try:
        stripe.Subscription.modify(
            subscription_id,
            stripe_account=connect_account_id,
            pause_collection={"behavior": "void"},
        )
        flash("Subscription paused")
    except Exception as e:
        flash("Error pausing subscription")
        print(e)

    if "goback" in request.args:
        return redirect(request.referrer)
    return jsonify(message="Subscription paused",
                   subscription_id=subscription_id)
Пример #11
0
def resume_stripe_subscription(subscription_id):
    """Resume a Stripe subscription"""
    stripe.api_key = get_stripe_secret_key()
    connect_account_id = get_stripe_connect_account_id()

    try:
        stripe.Subscription.modify(
            subscription_id,
            stripe_account=connect_account_id,
            pause_collection=
            "",  # passing empty string unpauses the subscription
        )
        flash("Subscription resumed")
    except Exception as e:
        flash("Error resuming subscription")
        print(e)

    if "goback" in request.args:
        return redirect(request.referrer)

    return jsonify(message="Subscription resumed",
                   subscription_id=subscription_id)
Пример #12
0
def update_stripe_subscription_statuses():
    """Update Stripe subscriptions with their current status
    by querying Stripe api"""
    stripe.api_key = get_stripe_secret_key()
    connect_account = get_stripe_connect_account()
    if stripe.api_key is None:
        log.error(
            "Stripe api key not set refusing to update subscription statuses")
    if connect_account is None:
        log.error(
            "Stripe connect account not set. Refusing to update subscription statuses"
        )
    if stripe_connect_active():
        try:
            stripeSubscriptions = stripe.Subscription.list(
                stripe_account=connect_account.id, limit=100)
            for stripeSubscription in stripeSubscriptions.auto_paging_iter():

                subscription = (database.session.query(Subscription).where(
                    Subscription.stripe_subscription_id ==
                    stripeSubscription.id).first())
                if subscription:

                    subscription.stripe_status = stripeSubscription.status
                    log.info(subscription.stripe_status)
                    log.info(subscription.stripe_subscription_id)
                    database.session.commit()
                else:
                    log.warning(
                        "subscription is in stripe but not in the subscribie database"
                    )
        except Exception as e:
            log.warning(f"Could not update stripe subscription status: {e}")
    else:
        log.warning(
            "Refusing to update subscription status since Stripe connect is not active"
        )
Пример #13
0
def stripe_connect():
    account = None
    stripe_express_dashboard_url = None
    stripe.api_key = get_stripe_secret_key()
    payment_provider = PaymentProvider.query.first()

    try:
        account = get_stripe_connect_account()
        if account is not None and account.charges_enabled and account.payouts_enabled:
            payment_provider.stripe_active = True
        else:
            payment_provider.stripe_active = False
    except (
            stripe.error.PermissionError,
            stripe.error.InvalidRequestError,
            AttributeError,
    ) as e:
        print(e)
        account = None

    # Setup Stripe webhook endpoint if it dosent already exist
    if account:
        try:
            stripe_express_dashboard_url = stripe.Account.create_login_link(
                account.id).url
        except stripe.error.InvalidRequestError:
            stripe_express_dashboard_url = None

    database.session.commit()
    return render_template(
        "admin/settings/stripe/stripe_connect.html",
        stripe_onboard_path=url_for("admin.stripe_onboarding"),
        account=account,
        payment_provider=payment_provider,
        stripe_express_dashboard_url=stripe_express_dashboard_url,
    )
Пример #14
0
def stripe_create_checkout_session():
    data = request.json
    plan = Plan.query.filter_by(uuid=session["plan"]).first()
    person = Person.query.get(session["person_id"])
    charge = {}
    charge["sell_price"] = plan.sell_price
    charge["interval_amount"] = plan.interval_amount
    charge["currency"] = "GBP"
    session["subscribie_checkout_session_id"] = str(uuid4())
    payment_method_types = ["card"]
    success_url = url_for("views.instant_payment_complete",
                          _external=True,
                          plan=plan.uuid)
    cancel_url = url_for("views.order_summary", _external=True)

    stripe.api_key = get_stripe_secret_key()

    metadata = {
        "person_uuid":
        person.uuid,
        "plan_uuid":
        session["plan"],
        "chosen_option_ids":
        json.dumps(session.get("chosen_option_ids", None)),
        "package":
        session.get("package", None),
        "subscribie_checkout_session_id":
        session.get("subscribie_checkout_session_id", None),
    }

    # Add note to seller if present directly on payment metadata
    if session.get("note_to_seller", False):
        metadata["note_to_seller"] = session.get("note_to_seller")

    if plan.requirements.subscription:
        subscription_data = {
            "application_fee_percent": 1.25,
            "metadata": {
                "person_uuid":
                person.uuid,
                "plan_uuid":
                session["plan"],
                "chosen_option_ids":
                json.dumps(session.get("chosen_option_ids", None)),  # noqa
                "package":
                session.get("package", None),
                "subscribie_checkout_session_id":
                session.get("subscribie_checkout_session_id", None),
            },
        }
        # Add note to seller if present on subscription_data metadata
        if session.get("note_to_seller", False):
            subscription_data["metadata"]["note_to_seller"] = session.get(
                "note_to_seller")
        # Add trial period if present
        if plan.days_before_first_charge and plan.days_before_first_charge > 0:
            subscription_data[
                "trial_period_days"] = plan.days_before_first_charge

    if plan.requirements.instant_payment:
        payment_intent_data = {
            "application_fee_amount": 20,
            "metadata": metadata
        }

    if plan.requirements.subscription:
        mode = "subscription"
    else:
        mode = "payment"

    # Build line_items array depending on plan requirements
    line_items = []

    # Add line item for instant_payment if required
    if plan.requirements.instant_payment:
        # Append "Up-front fee" to product name if also a subscription
        plan_name = plan.title
        if plan.requirements.subscription:
            plan_name = "(Up-front fee) " + plan_name

        line_items.append({
            "price_data": {
                "currency": charge["currency"],
                "product_data": {
                    "name": plan_name,
                },
                "unit_amount": charge["sell_price"],
            },
            "quantity": 1,
        })

    if plan.requirements.subscription:
        line_items.append({
            "price_data": {
                "recurring": {
                    "interval": format_to_stripe_interval(plan.interval_unit)
                },
                "currency": charge["currency"],
                "product_data": {
                    "name": plan.title,
                },
                "unit_amount": charge["interval_amount"],
            },
            "quantity": 1,
        })

    # Create Stripe checkout session for payment or subscription mode
    if plan.requirements.subscription:
        stripe_session = stripe.checkout.Session.create(
            stripe_account=data["account_id"],
            payment_method_types=payment_method_types,
            line_items=line_items,
            mode=mode,
            metadata=metadata,
            customer_email=person.email,
            success_url=success_url,
            cancel_url=cancel_url,
            subscription_data=subscription_data,
        )
        return jsonify(id=stripe_session.id)
    elif plan.requirements.instant_payment:
        stripe_session = stripe.checkout.Session.create(
            stripe_account=data["account_id"],
            payment_method_types=payment_method_types,
            line_items=line_items,
            mode=mode,
            metadata=metadata,
            customer_email=person.email,
            success_url=success_url,
            cancel_url=cancel_url,
            payment_intent_data=payment_intent_data,
        )
        return jsonify(id=stripe_session.id)
Пример #15
0
def stripe_process_event_payment_intent_succeeded(event):
    """Store suceeded payment_intents as transactions
    Stripe sends Subscribie events of different types.
    This metod processes the payment_intent_succeeded event.
    This event will fire both at the begining of a subscription,
    and also at each successful recuring billing cycle.

    We use backoff because webhook event order from Stripe is not guaranteed,
    for example a `payment_intent_succeeded` event can be received before a
    `checkout.session.completed` event. If that happens, then the associated
    subscription may not be created yet in Subscribie database.
    Therefore the @backoff.on_exception allows processing of the
    payment_intent_succeeded event to retry until the
    checkout.session.completed event is processed.
    If the backoff fails (max_tries is exceeded) then Stripe will
    retry the event at a later time.

    See also
    - https://stripe.com/docs/api/events/types#event_types-checkout.session.completed
    - https://stripe.com/docs/api/events/types#event_types-payment_intent.succeeded

    """
    log.info("Processing payment_intent.succeeded")

    data = event["data"]["object"]
    stripe.api_key = get_stripe_secret_key()
    subscribie_subscription = None

    # Get the Subscribie subscription id from Stripe subscription metadata
    try:
        subscribie_checkout_session_id = data["metadata"][
            "subscribie_checkout_session_id"]
    except KeyError:
        # There is no subscribie metadata on the event if its an upfront payment
        # So try and get it via the data['invoice'] attribute
        invoice_id = data["invoice"]

        # Stripe payment_intent invoice attribute may be null if
        # the payment intent is an instant charge (meaning not part of a
        # subscrtiption or plan).
        if invoice_id is not None:
            invoice = stripe.Invoice.retrieve(stripe_account=event["account"],
                                              id=invoice_id)
            # Fetch subscription via its invoice
            subscription = stripe.Subscription.retrieve(
                stripe_account=event["account"], id=invoice.subscription)
            subscribie_checkout_session_id = subscription.metadata[
                "subscribie_checkout_session_id"]
        else:
            # If instance charge (not a subscription)
            # there is no checkout session (instant charge)
            subscribie_checkout_session_id = data["id"]
    except Exception as e:
        msg = f"Unable to get subscribie_checkout_session_id from event\n{e}"
        log.error(msg)
        return msg, 500

    # Locate the Subscribie subscription by its subscribie_checkout_session_id
    subscribie_subscription = (database.session.query(Subscription).filter_by(
        subscribie_checkout_session_id=subscribie_checkout_session_id).first())

    # Store the transaction in Transaction model
    # (regardless of if subscription or just one-off plan)
    if (database.session.query(Transaction).filter_by(
            external_id=data["id"]).first() is None):
        transaction = Transaction()
        transaction.amount = data["amount"]
        transaction.payment_status = ("paid" if data["status"] == "succeeded"
                                      else data["status"])
        transaction.external_id = data["id"]
        transaction.external_src = "stripe"
        if subscribie_subscription is not None:
            transaction.person = subscribie_subscription.person
            transaction.subscription = subscribie_subscription
        elif data["metadata"] == {}:
            log.warn(f"Empty metadata: {data}")
            return "Empty metadata", 422
        else:
            log.error("subscribie_subscription not found for this\
              payment_intent.succeeded. The metadata was:")
            log.error(data["metadata"])
            raise Exception
        database.session.add(transaction)
        database.session.commit()
    return "OK", 200
Пример #16
0
def create_subscription(
    email=None,
    package=None,
    chosen_option_ids=None,
    subscribie_checkout_session_id=None,
    stripe_external_id=None,
    stripe_subscription_id=None,
) -> Subscription:
    """Create subscription model
    Note: A subscription model is also created if a plan only has
    one up_front payment (no recuring subscription). This allows
    the storing of chosen options againt their plan choice.
    Chosen option ids may be passed via webhook or through session
    """
    log.info("Creating Subscription model if needed")
    subscription = None  # Initalize subscription model to None

    # Store Subscription against Person locally
    if email is None:
        email = session["email"]

    if package is None:
        package = session["package"]

    person = database.session.query(Person).filter_by(email=email).one()

    # subscribie_checkout_session_id can be passed by stripe metadata (webhook) or
    # via session (e.g. when session only with no up-front payment)
    if subscribie_checkout_session_id is None:
        subscribie_checkout_session_id = session.get(
            "subscribie_checkout_session_id", None)
    log.info(
        f"subscribie_checkout_session_id is: {subscribie_checkout_session_id}")

    # Verify Subscription not already created (e.g. stripe payment webhook)
    # another hook or mandate only payment may have already created the Subscription
    # model, if so, fetch it via its subscribie_checkout_session_id
    if subscribie_checkout_session_id is not None:
        subscription = (Subscription.query.filter_by(
            subscribie_checkout_session_id=subscribie_checkout_session_id).
                        filter(Subscription.person.has(email=email)).first())

    if subscription is None:
        log.info(
            "No existing subscription model found, creating Subscription model"
        )
        # Create new subscription model
        subscription = Subscription(
            sku_uuid=package,
            person=person,
            subscribie_checkout_session_id=subscribie_checkout_session_id,
            stripe_external_id=stripe_external_id,
            stripe_subscription_id=stripe_subscription_id,
        )
        # Add chosen options (if any)
        if chosen_option_ids is None:
            chosen_option_ids = session.get("chosen_option_ids", None)

        if chosen_option_ids:
            log.info(
                f"Applying chosen_option_ids to subscription: {chosen_option_ids}"
            )
            chosen_options = []
            for option_id in chosen_option_ids:
                log.info(f"Locating option id: {option_id}")
                option = Option.query.get(option_id)
                # Store as ChosenOption because options may change after the order
                # has processed. This preserves integrity of the actual chosen options
                chosen_option = ChosenOption()
                if option is not None:
                    chosen_option.option_title = option.title
                    chosen_option.choice_group_title = option.choice_group.title
                    chosen_option.choice_group_id = (
                        option.choice_group.id
                    )  # Used for grouping latest choice
                    chosen_options.append(chosen_option)
                else:
                    log.error(
                        f"Failed to get Open from session option_id: {option_id}"
                    )
            subscription.chosen_options = chosen_options
        else:
            log.info("No chosen_option_ids were found or applied.")

        database.session.add(subscription)
        database.session.commit()
        session["subscription_uuid"] = subscription.uuid

        # If subscription plan has cancel_at set, modify Stripe subscription
        # charge_at property
        stripe.api_key = get_stripe_secret_key()
        connect_account_id = get_stripe_connect_account_id()
        if subscription.plan.cancel_at:
            cancel_at = subscription.plan.cancel_at
            try:
                stripe.Subscription.modify(
                    sid=subscription.stripe_subscription_id,
                    stripe_account=connect_account_id,
                    cancel_at=cancel_at,
                )
                subscription.stripe_cancel_at = cancel_at
                database.session.commit()
            except Exception as e:  # noqa
                log.error("Could not set cancel_at: {e}")

    newSubscriberEmailNotification()
    return subscription
Пример #17
0
def account():
    "A subscribers account home screen"
    stripe.api_key = get_stripe_secret_key()
    stripe_connect_account_id = get_stripe_connect_account_id()
    stripe_publishable_key = get_stripe_publishable_key()
    stripe_default_payment_method = None
    stripe_session = None

    # Get subscribers first subscription to determine stripe customer id
    # excluding one-off plans.
    subscription = (
        database.session.query(Subscription)
        .execution_options(include_archived=True)
        .join(Person, Subscription.person_id == Person.id)
        .join(Plan, Subscription.sku_uuid == Plan.uuid)
        .join(PlanRequirements, Plan.id == PlanRequirements.plan_id)
        .where(Person.email == session["subscriber_id"])
        .where(PlanRequirements.subscription == 1)
        .order_by(Subscription.id.desc())
        .first()
    )
    if subscription:
        try:
            stripe_subscription = stripe.Subscription.retrieve(
                subscription.stripe_subscription_id,
                stripe_account=stripe_connect_account_id,
            )
            stripe_customer_id = stripe_subscription.customer
            stripe_customer = stripe.Customer.retrieve(
                stripe_customer_id, stripe_account=stripe_connect_account_id
            )

            stripe_session = stripe.checkout.Session.create(
                stripe_account=stripe_connect_account_id,
                payment_method_types=["card"],
                mode="setup",
                customer=stripe_customer_id,
                setup_intent_data={
                    "metadata": {
                        "subscription_id": stripe_subscription.id,
                    },
                },
                success_url=url_for("subscriber.account", _external=True)
                + "?stripe_session_id={CHECKOUT_SESSION_ID}",
                cancel_url=url_for("subscriber.account", _external=True),
            )
            if request.args.get("stripe_session_id"):
                # Process stripe update payment request
                # Get Stripe checkout session
                stripe_session = stripe.checkout.Session.retrieve(
                    request.args.get("stripe_session_id"),
                    stripe_account=stripe_connect_account_id,
                )
                # Get setup_intent id from stripe session
                stripe_setup_intent_id = stripe_session.setup_intent
                stripe_setup_intent = stripe.SetupIntent.retrieve(
                    stripe_setup_intent_id, stripe_account=stripe_connect_account_id
                )
                # Update default payment method
                stripe.Customer.modify(
                    stripe_customer.id,
                    stripe_account=stripe_connect_account_id,
                    invoice_settings={
                        "default_payment_method": stripe_setup_intent.payment_method
                    },
                )
                flash("Default payment method updated")
                return redirect(url_for("subscriber.account"))

            # Try to get existing default payment method
            if stripe_customer.invoice_settings.default_payment_method:
                stripe_default_payment_method = stripe.PaymentMethod.retrieve(
                    stripe_customer.invoice_settings.default_payment_method,
                    stripe_account=stripe_connect_account_id,
                )
        except stripe.error.InvalidRequestError as e:
            log.error(f"stripe.error.InvalidRequestError: {e}")

    return render_template(
        "subscriber/account.html",
        stripe_session=stripe_session,
        stripe_publishable_key=stripe_publishable_key,
        stripe_default_payment_method=stripe_default_payment_method,
    )
Пример #18
0
def store_stripe_transaction(stripe_external_id):
    """Store Stripe invoice payment in transactions table"""
    stripe.api_key = get_stripe_secret_key()
    stripe_connect_account_id = get_stripe_connect_account_id()

    invoice = None

    # It might be an upcoming invoice or an existing invoice which
    # is being updated
    # First try fetching paid invoice
    try:
        invoice = stripe.Invoice.retrieve(
            id=stripe_external_id,
            stripe_account=stripe_connect_account_id,
        )
    except stripe.error.InvalidRequestError as e:
        print(
            f"Cannot get stripe invoice subscription id: {stripe_external_id}")
        print(
            "This might be okay. Trying to fetch upcoming invoice with same id..."
        )
        print(e)
        try:
            invoice = stripe.Invoice.upcoming(
                subscription=stripe_external_id,
                stripe_account=stripe_connect_account_id,
            )
        except stripe.error.InvalidRequestError as e:
            print(f"Cannot get stripe upcoming invoice subscription id: \
                  {stripe_external_id}")
            print(e)
            raise Exception(
                f"Cannot locate Stripe subscription invoice {stripe_external_id}"
            )

    # Check if there's an existing payment record in transactions table
    transaction = Transaction.query.filter_by(
        external_id=stripe_external_id).first()
    if transaction is None:
        # No existing transaction found, so add payment info from Stripe invoice
        # Note, if the subscription invoice is unsettled, its amount could
        # potentially change until it's finalized

        # Store as transaction
        transaction = Transaction()
        transaction.amount = invoice.amount_due
        transaction.payment_status = invoice.status
        transaction.comment = str(invoice.lines.data[0].metadata)
        transaction.external_id = stripe_external_id
        transaction.external_src = "stripe"
        # Find related subscribie subscription model if exists
        subscription = Subscription.query.filter_by(
            stripe_external_id=stripe_external_id).first()
        if subscription is not None:
            transaction.subscription = subscription
        try:
            transaction.person = subscription.person
        except AttributeError:  # A transaction may not have a person
            pass

    # Update payment_status to stripe latest status
    transaction.payment_status = invoice.status

    database.session.add(transaction)
    database.session.commit()  # Save/update transaction in transactions table
    return transaction
Пример #19
0
    def invoices(self):
        """Get all invoices for a given person

        Note a person may have zero or more subscriptions,
        with each subscription having zero or more invoices

        For Stripe invoices, the stripe customer id is needed,
        note it is possible (though rare) for one Subscribie customer id
        to have multiple Stripe customer ids. This is not as issue
        since we store the Stripe subscription id, and, if needed, can
        query the Stripe customer id from the Subscription object.
        See:
        - this file class "Subscription" with colum "stripe_subscription_id"
        - https://stripe.com/docs/api/subscriptions/object?lang=python#subscription_object-customer # noqa: E501
        """
        invoices = []
        stripe.api_key = get_stripe_secret_key()
        stripe_account_id = get_stripe_connect_account_id()

        # Get Stripe invoices
        for subscription in self.subscriptions:
            if subscription.stripe_subscription_id != "":
                try:
                    stripe_subscription = stripe.Subscription.retrieve(
                        subscription.stripe_subscription_id,
                        stripe_account=stripe_account_id,
                    )
                    # Get Stripe customer id
                    stripe_customer_id = stripe_subscription.customer
                    # Get Stripe invoices for this customer/subscriber
                    stripe_invoices = stripe.Invoice.list(
                        stripe_account=stripe_account_id,
                        customer=stripe_customer_id,
                    )
                    # loop over all invoices
                    # See https://stripe.com/docs/api/pagination/auto
                    for invoice in stripe_invoices.auto_paging_iter():
                        # If invoice is not paid, check for any payment errors
                        if invoice.status != "paid":
                            try:
                                stripe_decline_code = stripe.PaymentIntent.retrieve(
                                    invoice.payment_intent,
                                    stripe_account=stripe_account_id,
                                ).last_payment_error.decline_code
                                invoice[
                                    "stripe_decline_code"] = stripe_decline_code
                            except Exception as e:
                                log.warning(
                                    f"Could not get Stripe Invoice PaymentIntent last_payment_error decline_code: {e}"  # noqa: E501
                                )
                        else:
                            invoice["stripe_decline_code"] = None
                        invoices.append(invoice)
                except stripe.error.InvalidRequestError as e:
                    log.error(
                        f"Unable to retrieve stripe subscription by id: {subscription.stripe_subscription_id}. {e}"  # noqa: E501
                    )
            else:
                log.debug(
                    f"Skipping fetching invoice for subscription.uuid {subscription.uuid}"  # noqa: E501
                )
        return invoices