Exemplo n.º 1
0
def _handle_failed_tip_sub(body):
    sub = _get_tip_subscription(body)
    if not sub: return {'warning': 'subscription unrecognized'}

    closed = body['data']['object']['closed']
    pod = sub.podcast
    if closed:
        sub.deactivated = True
        sub.save()

        send_notification_email(
            None,
            ugettext('Your subscription to %s was cancelled') % pod.name,
            ugettext('We attempted to charge your card for your '
                     'subscription to %s, but the payment failed multiple '
                     'times. If you wish to remain subscribed, please '
                     'visit the link below to enter new payment '
                     'information.\n\n%s') %
            (pod.name, BASE_URL + reverse('tip_jar', podcast_slug=pod.slug)),
            email=sub.tipper.email_address)

        return {'success': 'nastygram sent, subscription deactivated'}
    else:
        send_notification_email(
            None,
            ugettext('Your subscription to %s has problems') % pod.name,
            ugettext('We attempted to charge your card for your '
                     'subscription to %s, but the payment failed. Please '
                     'visit the tip jar and update your subscription with '
                     'new card details as soon as possible. You can do that '
                     'at the link below.\n\n%s') %
            (pod.name, BASE_URL + reverse('tip_jar', podcast_slug=pod.slug)),
            email=sub.tipper.email_address)

        return {'success': 'nastygram sent'}
Exemplo n.º 2
0
def _handle_failed_tip_sub(body):
    sub = _get_tip_subscription(body)
    if not sub: return {'warning': 'subscription unrecognized'}

    closed = body['data']['object']['closed']
    pod = sub.podcast
    if closed:
        sub.deactivated = True
        sub.save()

        send_notification_email(
            None,
            ugettext('Your subscription to %s was cancelled') % pod.name,
            ugettext('We attempted to charge your card for your '
                     'subscription to %s, but the payment failed multiple '
                     'times. If you wish to remain subscribed, please '
                     'visit the link below to enter new payment '
                     'information.\n\n%s') %
                (pod.name, BASE_URL + reverse('tip_jar', podcast_slug=pod.slug)),
            email=sub.tipper.email_address)

        return {'success': 'nastygram sent, subscription deactivated'}
    else:
        send_notification_email(
            None,
            ugettext('Your subscription to %s has problems') % pod.name,
            ugettext('We attempted to charge your card for your '
                     'subscription to %s, but the payment failed. Please '
                     'visit the tip jar and update your subscription with '
                     'new card details as soon as possible. You can do that '
                     'at the link below.\n\n%s') %
                (pod.name, BASE_URL + reverse('tip_jar', podcast_slug=pod.slug)),
            email=sub.tipper.email_address)

        return {'success': 'nastygram sent'}
Exemplo n.º 3
0
def _send_one_time_tip(req, podcast, owner_us, amount):
    email = req.POST.get('email')
    token = req.POST.get('token')

    tip_user = TipUser.tip_user_from(email_address=email)

    application_fee = int(amount * 0.3) if owner_us.plan == PLAN_DEMO else 0
    try:
        stripe_charge = stripe.Charge.create(
            amount=amount,
            application_fee=application_fee,
            currency='usd',
            description='Tip for %s' % podcast.name,
            destination=owner_us.stripe_payout_managed_account,
            source=token,
        )

    except Exception as e:
        rollbar.report_message('Error when sending tip: %s' % str(e), 'error')
        return {'error': str(e)}

    podcast.total_tips += amount
    podcast.save()

    tip_event = TipEvent(
        tipper=tip_user,
        podcast=podcast,
        amount=amount,
        fee_amount=application_fee,
        stripe_charge=stripe_charge.id)
    tip_event.save()

    send_notification_email(
        None,
        ugettext('Thanks for leaving a tip!'),
        ugettext('Your tip was sent: %s received $%0.2f. Thanks for supporting your '
                 'favorite content creators!') %
                (podcast.name, float(amount) / 100),
        email=email)
    send_notification_email(
        podcast.owner,
        ugettext('Your podcast was tipped!'),
        ugettext('%s received a tip of $%0.2f from %s. You should send them an email '
                 'thanking them for their generosity.') % (
            podcast.name, float(amount) / 100, tip_user.email_address))

    NotificationHook.trigger_notification(
        podcast=podcast,
        trigger_type='tip',
        data={'tipper': tip_user.email_address,
              'amount': amount})

    return {'success': True}
Exemplo n.º 4
0
def _send_one_time_tip(req, podcast, owner_us, amount):
    email = req.POST.get('email')
    token = req.POST.get('token')

    tip_user = TipUser.tip_user_from(email_address=email)

    application_fee = int(amount * 0.3) if owner_us.plan == PLAN_DEMO else 0
    try:
        stripe_charge = stripe.Charge.create(
            amount=amount,
            application_fee=application_fee,
            currency='usd',
            description='Tip for %s' % podcast.name,
            destination=owner_us.stripe_payout_managed_account,
            source=token,
        )

    except Exception as e:
        rollbar.report_message('Error when sending tip: %s' % str(e), 'error')
        return {'error': str(e)}

    Podcast.objects.filter(id=podcast.id).update(total_tips=F('total_tips') + amount)

    tip_event = TipEvent(
        tipper=tip_user,
        podcast=podcast,
        amount=amount,
        fee_amount=application_fee,
        stripe_charge=stripe_charge.id)
    tip_event.save()

    send_notification_email(
        None,
        ugettext('Thanks for leaving a tip!'),
        ugettext('Your tip was sent: %s received $%0.2f. Thanks for supporting your '
                 'favorite content creators!') %
                (podcast.name, float(amount) / 100),
        email=email)
    send_notification_email(
        podcast.owner,
        ugettext('Your podcast was tipped!'),
        ugettext('%s received a tip of $%0.2f from %s. You should send them an email '
                 'thanking them for their generosity.') % (
            podcast.name, float(amount) / 100, tip_user.email_address))

    NotificationHook.trigger_notification(
        podcast=podcast,
        trigger_type='tip',
        data={'tipper': tip_user.email_address,
              'amount': amount})

    return {'success': True}
Exemplo n.º 5
0
def ep_comment_box(req, podcast_slug, episode_id):
    pod = get_object_or_404(Podcast, slug=podcast_slug)
    if not UserSettings.user_meets_plan(pod.owner,
                                        plans.FEATURE_MIN_COMMENT_BOX):
        raise Http404()
    ep = get_object_or_404(PodcastEpisode, podcast=pod, id=episode_id)
    if not req.POST:
        return _pmrender(req, 'feedback/comment_episode.html', {
            'podcast': pod,
            'episode': ep
        })

    try:
        if not _validate_recaptcha(req):
            raise Exception('Invalid ReCAPTCHA')

        ip = analyze.get_request_ip(req)
        f = Feedback(podcast=pod,
                     episode=ep,
                     sender=req.POST.get('email'),
                     message=req.POST.get('message'),
                     sender_ip=ip)
        f.save()
        send_notification_email(
            pod.owner, ugettext('[Pinecast] You got some feedback!'),
            'Go check the Feedback page of %s--an episode on %s--to see what was written.\n\n'
            'https://pinecast.com%s' %
            (ep.title, pod.name,
             reverse('podcast_episode',
                     podcast_slug=podcast_slug,
                     episode_id=str(ep.id)) + '#tab-feedback'))
        NotificationHook.trigger_notification(podcast=pod,
                                              trigger_type='feedback',
                                              data={
                                                  'episode':
                                                  ep,
                                                  'content':
                                                  req.POST.get('message'),
                                                  'sender':
                                                  req.POST.get('email')
                                              })
    except Exception:
        return _pmrender(req, 'feedback/comment_episode.html', {
            'podcast': pod,
            'episode': ep,
            'error': True,
            'default': req.POST
        })

    return _pmrender(req, 'feedback/thanks.html', {'podcast': pod})
Exemplo n.º 6
0
def ep_comment_box(req, podcast_slug, episode_id):
    pod = get_object_or_404(Podcast, slug=podcast_slug)
    if not UserSettings.user_meets_plan(pod.owner, plans.FEATURE_MIN_COMMENT_BOX):
        raise Http404()
    ep = get_object_or_404(PodcastEpisode, podcast=pod, id=episode_id)
    if not req.POST:
        return _pmrender(req, 'feedback/comment_episode.html', {'podcast': pod, 'episode': ep})

    try:
        if not _validate_recaptcha(req):
            raise Exception('Invalid ReCAPTCHA')

        ip = analyze.get_request_ip(req)
        f = Feedback(
            podcast=pod,
            episode=ep,
            sender=req.POST.get('email'),
            message=req.POST.get('message'),
            sender_ip=ip
        )
        f.save()
        analytics_log.write('feedback', {
            'podcast': unicode(pod.id),
            'episode': unicode(ep.id),
            'profile': {
                'email': req.POST.get('email'),
                'email_host': req.POST.get('email').split('@')[1],
                'ip': ip,
                'ua': req.META.get('HTTP_USER_AGENT'),
            },
        }, req=req)
        send_notification_email(
            pod.owner,
            ugettext('[Pinecast] You got some feedback!'),
            'Go check the Feedback page of %s--an episode on %s--to see what was written.\n\n'
            'https://pinecast.com%s' %
                (ep.title,
                 pod.name,
                 reverse('podcast_episode',
                         podcast_slug=podcast_slug,
                         episode_id=str(ep.id)) +
                    '#tab-feedback')
        )
    except Exception:
        return _pmrender(req, 'feedback/comment_episode.html',
                         {'podcast': pod, 'episode': ep, 'error': True, 'default': req.POST})

    return _pmrender(req, 'feedback/thanks.html', {'podcast': pod})
Exemplo n.º 7
0
def user_settings_page_changepassword(req):
    if req.POST.get('new_password') != req.POST.get('confirm_password'):
        return redirect(reverse('dashboard') + '?error=pwc#settings')
    if not req.user.check_password(req.POST.get('old_password')):
        return redirect(reverse('dashboard') + '?error=pwo#settings')
    if len(req.POST.get('new_password')) < 8:
        return redirect(reverse('dashboard') + '?error=pwl#settings')

    req.user.set_password(req.POST.get('new_password'))
    req.user.save()

    send_notification_email(
        req.user, ugettext('[Pinecast] Password changed'),
        ugettext('''
Your Pinecast password has been updated. If you did not request this change,
please contact Pinecast support as soon as possible at
[email protected].
'''))
    return redirect(reverse('login'))
Exemplo n.º 8
0
def _handle_failed_subscription(body):
    customer = body['data']['object']['customer']
    try:
        us = UserSettings.objects.get(stripe_customer_id=customer)
    except UserSettings.DoesNotExist:
        rollbar.report_message('Unknown customer: %s' % customer, 'warn')
        return {'warning': 'customer unrecognized'}

    closed = body['data']['object']['closed']
    user = us.user
    if closed:
        us.set_plan(payment_plans.PLAN_DEMO)
        send_notification_email(
            user,
            ugettext('Your Pinecast subscription was cancelled.'),
            ugettext('Pinecast attempted to charge your payment card multiple '
                     'times, but was unable to collect payment. Your '
                     'account has been downgraded to a free Demo plan. Only '
                     'the ten most recent episodes from each of your podcasts '
                     'will be shown to your listeners. All recurring tip '
                     'subscriptions to your podcasts have also been '
                     'cancelled.\n\nNo content or settings have been deleted '
                     'from your account. If you wish to re-subscribe, you may '
                     'do so at any time at the URL below.\n\n%s') %
                (BASE_URL + reverse('upgrade')))

        return {'success': 'nastygram sent, account downgraded'}
    else:
        send_notification_email(
            user,
            ugettext('Payment failed for Pinecast subscription'),
            ugettext('Pinecast attempted to charge your payment card for your '
                     'current subscription, but was unable to collect payment. '
                     'If we fail to process your card three times, your '
                     'account will automatically be downgraded to a free Demo '
                     'plan.\n\n'
                     'No changes have currently been made to your account or '
                     'plan. Please update your payment information at the URL '
                     'below.\n\n%s') %
                (BASE_URL + reverse('dashboard') + '#settings,subscription'))
        return {'success': 'nastygram sent'}
Exemplo n.º 9
0
def user_settings_page_changepassword(req):
    if req.POST.get('new_password') != req.POST.get('confirm_password'):
        return redirect(reverse('user_settings') + '?error=pwc')
    if not req.user.check_password(req.POST.get('old_password')):
        return redirect(reverse('user_settings') + '?error=pwo')
    if len(req.POST.get('new_password')) < 8:
        return redirect(reverse('user_settings') + '?error=pwl')

    req.user.set_password(req.POST.get('new_password'))
    req.user.save()

    send_notification_email(
        req.user,
        ugettext('[Pinecast] Password changed'),
        ugettext('''
Your Pinecast password has been updated. If you did not request this change,
please contact Pinecast support as soon as possible at
[email protected].
''')
    )
    return redirect(reverse('login'))
Exemplo n.º 10
0
def _handle_failed_subscription(body):
    customer = body['data']['object']['customer']
    try:
        us = UserSettings.objects.get(stripe_customer_id=customer)
    except UserSettings.DoesNotExist:
        rollbar.report_message('Unknown customer: %s' % customer, 'warn')
        return {'warning': 'customer unrecognized'}

    closed = body['data']['object']['closed']
    user = us.user
    if closed:
        us.set_plan(payment_plans.PLAN_DEMO)
        send_notification_email(
            user, ugettext('Your Pinecast subscription was cancelled.'),
            ugettext('Pinecast attempted to charge your payment card multiple '
                     'times, but was unable to collect payment. Your '
                     'account has been downgraded to a free Demo plan. Only '
                     'the ten most recent episodes from each of your podcasts '
                     'will be shown to your listeners. All recurring tip '
                     'subscriptions to your podcasts have also been '
                     'cancelled.\n\nNo content or settings have been deleted '
                     'from your account. If you wish to re-subscribe, you may '
                     'do so at any time at the URL below.\n\n%s') %
            (BASE_URL + reverse('upgrade')))

        return {'success': 'nastygram sent, account downgraded'}
    else:
        send_notification_email(
            user, ugettext('Payment failed for Pinecast subscription'),
            ugettext(
                'Pinecast attempted to charge your payment card for your '
                'current subscription, but was unable to collect payment. '
                'If we fail to process your card three times, your '
                'account will automatically be downgraded to a free Demo '
                'plan.\n\n'
                'No changes have currently been made to your account or '
                'plan. Please update your payment information at the URL '
                'below.\n\n%s') %
            (BASE_URL + reverse('dashboard') + '#settings,subscription'))
        return {'success': 'nastygram sent'}
Exemplo n.º 11
0
def podcast_comment_box(req, podcast_slug):
    pod = get_object_or_404(Podcast, slug=podcast_slug)
    if not UserSettings.user_meets_plan(pod.owner, plans.FEATURE_MIN_COMMENT_BOX):
        raise Http404()
    if not req.POST:
        return _pmrender(req, 'feedback/comment_podcast.html', {'podcast': pod})

    try:
        if not _validate_recaptcha(req):
            raise Exception('Invalid ReCAPTCHA')

        ip = analyze.get_request_ip(req)
        f = Feedback(
            podcast=pod,
            sender=req.POST.get('email'),
            message=req.POST.get('message'),
            sender_ip=ip
        )
        f.save()
        send_notification_email(
            pod.owner,
            ugettext('[Pinecast] You got some feedback!'),
            'Go check the Feedback page of your podcast, %s, to see what was written.\n\n'
            'https://pinecast.com%s' %
            (pod.name,
             reverse('podcast_dashboard', podcast_slug=podcast_slug) + '#tab-feedback')
        )

        NotificationHook.trigger_notification(
            podcast=pod,
            trigger_type='feedback',
            data={'content': req.POST.get('message'), 'sender': req.POST.get('email')})
    except Exception:
        return _pmrender(req, 'feedback/comment_podcast.html',
                         {'podcast': pod, 'error': True, 'default': req.POST})

    return _pmrender(req, 'feedback/thanks.html', {'podcast': pod})
Exemplo n.º 12
0
    def set_plan(self, new_plan_val, coupon=None):
        orig_plan = self.plan
        customer = self.get_stripe_customer()
        if not customer:
            return False

        existing_subs = customer.subscriptions.all(limit=1)['data']

        # Handle downgrades to free
        if new_plan_val == payment_plans.PLAN_DEMO:
            if existing_subs:
                existing_sub = existing_subs[0]
                existing_sub.delete()

            for podcast in self.user.podcast_set.all():
                for tip in podcast.recurring_tips.all():
                    tip.cancel()

            self.plan = payment_plans.PLAN_DEMO
            if self.coupon_code:
                stripe.Coupon.retrieve(self.coupon_code).delete()
                self.coupon_code = None
            self.save()
            return True

        plan_stripe_id = payment_plans.STRIPE_PLANS[new_plan_val]

        was_upgrade = (
            payment_plans.PLAN_RANKS[orig_plan] <= payment_plans.PLAN_RANKS[new_plan_val])

        if existing_subs:
            existing_sub = existing_subs[0]
            existing_sub.plan = plan_stripe_id

            if was_upgrade and coupon:
                existing_sub.coupon = coupon

            try:
                existing_sub.save()
            except Exception as e:
                rollbar.report_message(str(e), 'error')
                return 'card_error'
        else:
            try:
                customer.subscriptions.create(
                    coupon=coupon,
                    plan=plan_stripe_id)
            except stripe.error.CardError:
                return 'card_error'
            except Exception as e:
                rollbar.report_message(str(e), 'error')
                return 'card_error'

        self.plan = new_plan_val
        self.save()

        send_notification_email(
            self.user,
            ugettext('Your account has been %s') %
                (ugettext('upgraded') if was_upgrade else ugettext('downgraded')),
            ugettext('Your Pinecast account has been updated successfully. '
                     'Your account is now marked as "%s".\n\n'
                     'Please contact Pinecast support if you have any '
                     'questions.') %
                payment_plans.PLANS_MAP[new_plan_val])
        return True
Exemplo n.º 13
0
    def set_plan(self, new_plan_val, coupon=None):
        orig_plan = self.plan
        user = self.user
        customer = self.get_stripe_customer()
        if not customer:
            return False

        was_upgrade = (payment_plans.PLAN_RANKS[orig_plan] <=
                       payment_plans.PLAN_RANKS[new_plan_val])

        # Handle pro downgrades
        if orig_plan == payment_plans.PLAN_PRO and not was_upgrade:
            from dashboard.models import Collaborator
            Collaborator.objects.filter(
                podcast__in=user.podcast_set.all()).delete()

        existing_subs = customer.subscriptions.all(limit=1)['data']

        # Handle downgrades to free
        if new_plan_val == payment_plans.PLAN_DEMO:
            if existing_subs:
                existing_sub = existing_subs[0]
                existing_sub.delete()

            for podcast in self.user.podcast_set.all():
                for tip in podcast.recurring_tips.all():
                    tip.cancel()

            self.plan = payment_plans.PLAN_DEMO
            if self.coupon_code:
                stripe.Coupon.retrieve(self.coupon_code).delete()
                self.coupon_code = None
            self.save()
            return True

        plan_stripe_id = payment_plans.STRIPE_PLANS[new_plan_val]

        if existing_subs:
            existing_sub = existing_subs[0]
            existing_sub.plan = plan_stripe_id

            if was_upgrade and coupon:
                existing_sub.coupon = coupon

            try:
                existing_sub.save()
            except Exception as e:
                rollbar.report_exc_info(sys.exc_info())
                return 'card_error'
        else:
            try:
                customer.subscriptions.create(coupon=coupon,
                                              plan=plan_stripe_id)
            except stripe.error.CardError:
                return 'card_error'
            except Exception as e:
                rollbar.report_exc_info(sys.exc_info())
                return 'card_error'

        self.plan = new_plan_val
        self.save()

        send_notification_email(
            user,
            ugettext('Your account has been %s') %
            (ugettext('upgraded') if was_upgrade else ugettext('downgraded')),
            ugettext('Your Pinecast account has been updated successfully. '
                     'Your account is now marked as "%s".\n\n'
                     'Please contact Pinecast support if you have any '
                     'questions.') % payment_plans.PLANS_MAP[new_plan_val])
        return True
Exemplo n.º 14
0
def _finish_sub(req, pod, amount, email, token):
    owner_us = UserSettings.get_from_user(pod.owner)
    if (not owner_us.stripe_payout_managed_account or
        owner_us.plan == PLAN_DEMO):
        raise Exception('invalid plan')

    tip_user = TipUser.tip_user_from(email_address=email, auto_save=False)
    if not tip_user.verified:
        tip_user.verified = True
        tip_user.save()

    req.session['pay_session'] = tip_user.id

    # If the user already has a subscription, update it instead of billing them
    # with a new one.
    try:
        sub = RecurringTip.objects.get(tipper=tip_user, podcast=pod, deactivated=False)
        if sub.amount == amount:
            return True

        # Update Stripe with the new amount
        sub_obj = sub.get_subscription()
        sub_obj.quantity = int(amount / 100)
        sub_obj.source = token
        sub_obj.save()

        # Update the DB with the new amount
        sub.amount = amount
        sub.save()

        # Updating total_tips is done with the web hook.

        send_notification_email(
            None,
            ugettext('Your subscription was updated.'),
            ugettext('Your subscription to %s was updated to $%0.2f. Thanks '
                     'for supporting your favorite content creators!') %
                    (pod.name, float(amount) / 100),
            email=email)
        return True
    except RecurringTip.DoesNotExist:
        pass

    managed_account = owner_us.stripe_payout_managed_account

    # Check that the tip sub plan exists
    plans = stripe.Plan.list(stripe_account=managed_account)
    if not plans.data:
        stripe.Plan.create(
            amount=100,
            currency='usd',
            id='tipsub',
            interval='month',
            name='Podcast Tip Jar',
            statement_descriptor='PODCAST TIP JAR',
            stripe_account=managed_account)

    # Create the customer associated with the managed account
    sub = RecurringTip(tipper=tip_user, podcast=pod, amount=amount)
    try:
        customer = stripe.Customer.create(
            email=email,
            plan='tipsub',
            quantity=amount / 100,
            source=token,
            stripe_account=managed_account)
    except stripe.error.InvalidRequestError:
        return True
    sub.stripe_customer_id = customer.id
    sub.stripe_subscription_id = customer.subscriptions.data[0].id
    sub.save()


    # We don't update total_tips or create a tip event here. That happens when
    # the web hook from Stripe tells us that the payment succeeded.

    send_notification_email(
        pod.owner,
        ugettext('Someone subscribed to your podcast!'),
        ugettext('%s will receive a tip of $%0.2f from %s. Their subscription '
                 'will pay out once every month. You should send them an email '
                 'thanking them for their generosity.') % (
            pod.name, float(amount) / 100, email))

    return True
Exemplo n.º 15
0
def hook(req):
    try:
        body = json.loads(req.body.decode('utf-8'))
    except Exception as e:
        rollbar.report_message('Error parsing Stripe hook JSON: %s' % str(e),
                               'warn')
        return HttpResponse(status=400)

    if not settings.DEBUG:
        try:
            # Validate the event
            stripe.Event.retrieve(body['id'],
                                  stripe_account=body.get('user_id'))
        except Exception as e:
            rollbar.report_message('Error fetching Stripe event: %s' % str(e),
                                   'warn')
            return HttpResponse(status=400)

    if body['type'] == 'invoice.payment_succeeded' and body.get('user_id'):
        sub = _get_tip_subscription(body)
        if not sub: return {'warning': 'subscription unrecognized'}

        amount = int(body['data']['object']['total'])
        pod = sub.podcast

        tip_event = TipEvent(tipper=sub.tipper,
                             podcast=pod,
                             amount=amount,
                             recurring_tip=sub)
        tip_event.save()

        Podcast.objects.filter(id=pod.id).update(total_tips=F('total_tips') +
                                                 amount)

        email = sub.tipper.email_address
        send_notification_email(
            None,
            ugettext('Thanks for leaving a tip!'),
            ugettext(
                'Your tip was sent: %s received $%0.2f. Thanks for supporting your '
                'favorite content creators!') %
            (pod.name, float(amount) / 100),
            email=email)
        send_notification_email(
            pod.owner,
            ugettext('%s received a tip of $%0.2f') %
            (pod.name, float(amount) / 100),
            ugettext(
                '%s received a tip of $%0.2f from %s as part of a monthly '
                'subscription to the show. You should send them an email '
                'thanking them for their generosity.') %
            (pod.name, float(amount) / 100, email))

        NotificationHook.trigger_notification(podcast=pod,
                                              trigger_type='tip',
                                              data={
                                                  'tipper':
                                                  sub.tipper.email_address,
                                                  'amount': amount
                                              })

        return {'success': 'emails sent, tip event processed'}

    elif body['type'] == 'invoice.payment_succeeded':
        sub = stripe.Subscription.retrieve(
            body['data']['object']['subscription'])
        if not sub.discount:
            return {'success': 'ignoring undiscounted subscription'}

        coupon_code = sub.discount.coupon.id
        try:
            coupon_owner = UserSettings.objects.get(coupon_code=coupon_code)
        except UserSettings.DoesNotExist:
            return {'success': 'coupon not owned by referrer'}

        if coupon_owner.plan == payment_plans.PLAN_DEMO or coupon_owner.plan == payment_plans.PLAN_COMMUNITY:
            return {'success': 'coupon owned by free user'}

        min_charge = float('inf')
        valid_charges = 0

        # We limit this to three. If it goes over or under, we can ignore the charge.
        invoices = stripe.Invoice.list(customer=sub.customer, limit=3)
        if len(invoices.data) < 2:
            return {'success': 'did not reach two invoices yet'}

        for invoice in invoices.data:
            if not invoice.paid:
                continue
            valid_charges += 1

            invoice_amount = 0
            for line in invoice.lines.data:
                invoice_amount += line.amount
            if invoice_amount < min_charge:
                min_charge = invoice_amount

        if valid_charges != 2:
            return {'success': 'did not have two successful invoices'}

        try:
            owner_cust = coupon_owner.get_stripe_customer()
        except Exception as e:
            rollbar.report_message(
                'Error fetching coupon owner stripe customer: %s' % str(e),
                'error')
            return {'success': 'coupon owner does not exist'}
        else:
            if not owner_cust:
                rollbar.report_message(
                    'Coupon owner did not have valid stripe customer', 'error')
                return {'success': 'coupon owner does not exist'}

        amount = min_charge * 2

        # Negative amounts are credits
        owner_cust.account_balance -= amount
        owner_cust.save()

        send_notification_email(
            coupon_owner.user, ugettext('You have referral credit!'),
            ugettext('One of your referrals has crossed their two-month mark '
                     'as a paying customer. Your account has been credited '
                     '$%.2f.') % (float(amount) / 100))

        return {'success': 'user credited'}

    elif body['type'] == 'invoice.payment_failed':
        if body.get('user_id'):
            return _handle_failed_tip_sub(body)
        else:
            return _handle_failed_subscription(body)

    return {'success': 'ignored'}
Exemplo n.º 16
0
def hook(req):
    try:
        body = json.loads(req.body.decode('utf-8'))
    except Exception as e:
        rollbar.report_message(
            'Error parsing Stripe hook JSON: %s' % str(e), 'warn')
        return HttpResponse(status=400)

    if not settings.DEBUG:
        try:
            # Validate the event
            stripe.Event.retrieve(
                body['id'], stripe_account=body.get('user_id'))
        except Exception as e:
            rollbar.report_message(
                'Error fetching Stripe event: %s' % str(e), 'warn')
            return HttpResponse(status=400)


    if body['type'] == 'invoice.payment_succeeded' and body.get('user_id'):
        sub = _get_tip_subscription(body)
        if not sub: return {'warning': 'subscription unrecognized'}

        amount = int(body['data']['object']['total'])
        pod = sub.podcast

        tip_event = TipEvent(
            tipper=sub.tipper,
            podcast=pod,
            amount=amount,
            recurring_tip=sub)
        tip_event.save()

        pod.total_tips += amount
        pod.save()

        email = sub.tipper.email_address
        send_notification_email(
            None,
            ugettext('Thanks for leaving a tip!'),
            ugettext('Your tip was sent: %s received $%0.2f. Thanks for supporting your '
                     'favorite content creators!') % (pod.name, float(amount) / 100),
            email=email)
        send_notification_email(
            pod.owner,
            ugettext('%s received a tip of $%0.2f') % (pod.name, float(amount) / 100),
            ugettext('%s received a tip of $%0.2f from %s as part of a monthly '
                     'subscription to the show. You should send them an email '
                     'thanking them for their generosity.') %
                (pod.name, float(amount) / 100, email))

        NotificationHook.trigger_notification(
            podcast=pod,
            trigger_type='tip',
            data={'tipper': sub.tipper.email_address,
                  'amount': amount})

        return {'success': 'emails sent, tip event processed'}

    elif body['type'] == 'invoice.payment_succeeded':
        sub = stripe.Subscription.retrieve(body['data']['object']['subscription'])
        if not sub.discount:
            return {'success': 'ignoring undiscounted subscription'}

        coupon_code = sub.discount.coupon.id
        try:
            coupon_owner = UserSettings.objects.get(coupon_code=coupon_code)
        except UserSettings.DoesNotExist:
            return {'success': 'coupon not owned by referrer'}

        if coupon_owner.plan == payment_plans.PLAN_DEMO or coupon_owner.plan == payment_plans.PLAN_COMMUNITY:
            return {'success': 'coupon owned by free user'}

        min_charge = float('inf')
        valid_charges = 0

        # We limit this to three. If it goes over or under, we can ignore the charge.
        invoices = stripe.Invoice.list(customer=sub.customer, limit=3)
        if len(invoices.data) < 2:
            return {'success': 'did not reach two invoices yet'}

        for invoice in invoices.data:
            if not invoice.paid:
                continue
            valid_charges += 1

            invoice_amount = 0
            for line in invoice.lines.data:
                invoice_amount += line.amount
            if invoice_amount < min_charge:
                min_charge = invoice_amount

        if valid_charges != 2:
            return {'success': 'did not have two successful invoices'}

        try:
            owner_cust = coupon_owner.get_stripe_customer()
        except Exception as e:
            rollbar.report_message('Error fetching coupon owner stripe customer: %s' % str(e), 'error')
            return {'success': 'coupon owner does not exist'}
        else:
            if not owner_cust:
                rollbar.report_message('Coupon owner did not have valid stripe customer', 'error')
                return {'success': 'coupon owner does not exist'}

        amount = min_charge * 2

        # Negative amounts are credits
        owner_cust.account_balance -= amount
        owner_cust.save()

        send_notification_email(
            coupon_owner.user,
            ugettext('You have referral credit!'),
            ugettext('One of your referrals has crossed their two-month mark '
                     'as a paying customer. Your account has been credited '
                     '$%.2f.') % (float(amount) / 100))

        return {'success': 'user credited'}

    elif body['type'] == 'invoice.payment_failed':
        if body.get('user_id'):
            return _handle_failed_tip_sub(body)
        else:
            return _handle_failed_subscription(body)

    return {'success': 'ignored'}
Exemplo n.º 17
0
def _finish_sub(req, pod, amount, email, token):
    owner_us = UserSettings.get_from_user(pod.owner)
    if (not owner_us.stripe_payout_managed_account or
        owner_us.plan == PLAN_DEMO):
        raise Exception('invalid plan')

    tip_user = TipUser.tip_user_from(email_address=email, auto_save=False)
    if not tip_user.verified:
        tip_user.verified = True
        tip_user.save()

    req.session['pay_session'] = tip_user.id

    # If the user already has a subscription, update it instead of billing them
    # with a new one.
    try:
        sub = RecurringTip.objects.get(tipper=tip_user, podcast=pod, deactivated=False)
        if sub.amount == amount:
            return True

        # Update Stripe with the new amount
        sub_obj = sub.get_subscription()
        sub_obj.quantity = int(amount / 100)
        sub_obj.source = token
        sub_obj.save()

        # Update the DB with the new amount
        old_amount = sub.amount
        sub.amount = amount
        sub.save()

        # Updating total_tips is done with the web hook.

        send_notification_email(
            None,
            ugettext('Your subscription was updated.'),
            ugettext('Your subscription to %s was updated to $%0.2f. Thanks '
                     'for supporting your favorite content creators!') %
                    (pod.name, float(amount) / 100),
            email=email)
        return True
    except RecurringTip.DoesNotExist:
        pass

    managed_account = owner_us.stripe_payout_managed_account

    # Check that the tip sub plan exists
    plans = stripe.Plan.list(stripe_account=managed_account)
    if not plans.data:
        stripe.Plan.create(
            amount=100,
            currency='usd',
            id='tipsub',
            interval='month',
            name='Podcast Tip Jar',
            statement_descriptor='PODCAST TIP JAR',
            stripe_account=managed_account)

    # Create the customer associated with the managed account
    sub = RecurringTip(tipper=tip_user, podcast=pod, amount=amount)
    try:
        customer = stripe.Customer.create(
            email=email,
            plan='tipsub',
            quantity=amount / 100,
            source=token,
            stripe_account=managed_account)
    except stripe.error.InvalidRequestError:
        return True
    sub.stripe_customer_id = customer.id
    sub.stripe_subscription_id = customer.subscriptions.data[0].id
    sub.save()


    # We don't update total_tips or create a tip event here. That happens when
    # the web hook from Stripe tells us that the payment succeeded.

    send_notification_email(
        pod.owner,
        ugettext('Someone subscribed to your podcast!'),
        ugettext('%s will receive a tip of $%0.2f from %s. Their subscription '
                 'will pay out once every month. You should send them an email '
                 'thanking them for their generosity.') % (
            pod.name, float(amount) / 100, email))

    return True