示例#1
0
文件: tasks.py 项目: pjsier/muckrock
def send_invoice_receipt(invoice_id):
    """Send out a receipt for an invoiced charge"""
    invoice = stripe_retry_on_error(
        stripe.Invoice.retrieve,
        invoice_id,
    )
    try:
        charge = stripe_retry_on_error(
            stripe.Charge.retrieve,
            invoice.charge,
        )
    except stripe.error.InvalidRequestError:
        # a free subscription has no charge attached
        # maybe send a notification about the renewal
        # but for now just handle the error
        return
    profile = Profile.objects.get(customer_id=invoice.customer)
    # send a receipt based on the plan
    customer = profile.customer()
    subscription = customer.subscriptions.retrieve(invoice.subscription)
    try:
        receipt_functions = {
            'pro': receipts.pro_subscription_receipt,
            'org': receipts.org_subscription_receipt
        }
        receipt_function = receipt_functions[subscription.plan.id]
    except KeyError:
        logger.warning('Invoice charged for unrecognized plan: %s',
                       subscription.plan.name)
        receipt_function = receipts.generic_receipt
    receipt = receipt_function(profile.user, charge)
    receipt.send(fail_silently=False)
示例#2
0
 def cancel_pro_subscription(self):
     """Unsubscribe this profile from a professional plan. Return the cancelled subscription."""
     customer = self.customer()
     subscription = None
     # subscription reference either exists as a saved field or inside the Stripe customer
     # if it isn't, then they probably don't have a subscription. in that case, just make
     # sure that we demote their account and reset them back to basic.
     try:
         if not self.subscription_id and not len(customer.subscriptions.data) > 0:
             raise AttributeError('There is no subscription to cancel.')
         if self.subscription_id:
             subscription_id = self.subscription_id
         else:
             subscription_id = customer.subscriptions.data[0].id
         subscription = stripe_retry_on_error(
                 customer.subscriptions.retrieve,
                 subscription_id,
                 )
         subscription = subscription.delete()
         customer = stripe_retry_on_error(customer.save, idempotency_key=True)
     except AttributeError as exception:
         logger.warn(exception)
     except stripe.error.StripeError as exception:
         logger.warn(exception)
     self.subscription_id = ''
     self.acct_type = 'basic'
     self.monthly_requests = settings.MONTHLY_REQUESTS.get('basic', 0)
     self.payment_failed = False
     self.save()
     return subscription
示例#3
0
def buy_requests(request, username=None):
    """A purchaser buys requests for a recipient. The recipient can even be themselves!"""
    url_redirect = request.GET.get('next', 'acct-my-profile')
    bundles = int(request.POST.get('bundles', 1))
    recipient = get_object_or_404(User, username=username)
    purchaser = request.user
    request_price = bundles * 2000
    if purchaser.is_authenticated():
        request_count = bundles * purchaser.profile.bundled_requests()
    else:
        request_count = bundles * 4
    try:
        if request.POST:
            stripe_token = request.POST.get('stripe_token')
            stripe_email = request.POST.get('stripe_email')
            stripe_email = validate_stripe_email(stripe_email)
            if not stripe_token or not stripe_email:
                raise KeyError('Missing Stripe payment data.')
            # take from the purchaser
            stripe_retry_on_error(
                    stripe.Charge.create,
                    amount=request_price,
                    currency='usd',
                    source=stripe_token,
                    metadata={
                        'email': stripe_email,
                        'action': 'request-purchase',
                        },
                    idempotency_key=True,
                    )
            # and give to the recipient
            recipient.profile.num_requests += request_count
            recipient.profile.save()
            # record the purchase
            request.session['ga'] = 'request_purchase'
            msg = 'Purchase successful. '
            if recipient == purchaser:
                msg += '%d requests have been added to your account.' % request_count
            else:
                msg += '%d requests have been gifted to %s' % (request_count, recipient.first_name)
                gift_description = '%d requests' % request_count
                # notify the recipient with an email
                gift.delay(recipient, purchaser, gift_description)
            messages.success(request, msg)
            logger.info('%s purchased %d requests', purchaser.username, request_count)
    except KeyError as exception:
        msg = 'Payment error: %s' % exception
        messages.error(request, msg)
        logger.warn('Payment error: %s', exception, exc_info=sys.exc_info())
    except stripe.CardError as exception:
        msg = 'Payment error: %s Your card has not been charged.' % exception
        messages.error(request, msg)
        logger.warn('Payment error: %s', exception, exc_info=sys.exc_info())
    except (stripe.error.InvalidRequestError, stripe.error.APIError) as exc:
        msg = 'Payment error: Your card has not been charged.'
        messages.error(request, msg)
        logger.warn('Payment error: %s', exc, exc_info=sys.exc_info())
    return redirect(url_redirect)
示例#4
0
文件: models.py 项目: pjsier/muckrock
 def make_payment(self, token, email, amount, show=False, user=None):
     """Creates a payment for the crowdfund"""
     # pylint: disable=too-many-arguments
     amount = Decimal(amount)
     if self.payment_capped and amount > self.amount_remaining():
         amount = self.amount_remaining()
     # Try processing the payment using Stripe.
     # If the payment fails, do not catch the error.
     # Stripe represents currency as smallest-unit integers.
     stripe_amount = int(float(amount) * 100)
     charge = stripe_retry_on_error(
         stripe.Charge.create,
         amount=stripe_amount,
         source=token,
         currency='usd',
         metadata={
             'email': email,
             'action': 'crowdfund-payment',
             'crowdfund_id': self.id,
             'crowdfund_name': self.name
         },
         idempotency_key=True,
     )
     payment = CrowdfundPayment.objects.create(amount=amount,
                                               crowdfund=self,
                                               user=user,
                                               show=show,
                                               charge_id=charge.id)
     cache.delete('cf:%s:crowdfund_widget_data' % self.pk)
     logging.info(payment)
     self.update_payment_received()
     return payment
示例#5
0
文件: models.py 项目: benlk/muckrock
 def pay(self, token, amount, metadata, fee=PAYMENT_FEE):
     """
     Creates a Stripe charge for the user.
     Should always expect a 1-cent based integer (e.g. $1.00 = 100)
     Should apply a baseline fee (5%) to all payments.
     """
     # pylint: disable=no-self-use
     modified_amount = int(amount + (amount * fee))
     if not metadata.get('email') or not metadata.get('action'):
         raise ValueError('The charge metadata is malformed.')
     stripe_retry_on_error(
             stripe.Charge.create,
             amount=modified_amount,
             currency='usd',
             source=token,
             metadata=metadata
             )
示例#6
0
文件: models.py 项目: benlk/muckrock
 def customer(self):
     """Retrieve the customer from Stripe or create one if it doesn't exist. Then return it."""
     # pylint: disable=redefined-variable-type
     try:
         if not self.customer_id:
             raise AttributeError('No Stripe ID')
         customer = stripe_retry_on_error(
                 stripe.Customer.retrieve,
                 self.customer_id,
                 )
     except (AttributeError, stripe.InvalidRequestError):
         customer = stripe_retry_on_error(
                 stripe.Customer.create,
                 description=self.user.username,
                 email=self.user.email
                 )
         self.customer_id = customer.id
         self.save()
     return customer
示例#7
0
 def card(self):
     """Retrieve the default credit card from Stripe, if one exists."""
     card = None
     customer = self.customer()
     if customer.default_source:
         card = stripe_retry_on_error(
                 customer.sources.retrieve,
                 customer.default_source,
                 )
     return card
示例#8
0
 def start_pro_subscription(self, token=None):
     """Subscribe this profile to a professional plan. Return the subscription."""
     # create the stripe subscription
     customer = self.customer()
     if self.subscription_id:
         raise AttributeError('Only allowed one active subscription at a time.')
     if not token and not customer.default_source:
         raise AttributeError('No payment method provided for this subscription.')
     subscription = stripe_retry_on_error(
             customer.subscriptions.create,
             plan='pro',
             source=token,
             idempotency_key=True,
             )
     stripe_retry_on_error(customer.save, idempotency_key=True)
     # modify the profile object (should this be part of a webhook callback?)
     self.subscription_id = subscription.id
     self.acct_type = 'pro'
     self.date_update = date.today()
     self.monthly_requests = settings.MONTHLY_REQUESTS.get('pro', 0)
     self.save()
     return subscription
示例#9
0
文件: views.py 项目: pjsier/muckrock
 def make_charge(self, token, amount, email):
     """Make a Stripe charge and catch any errors."""
     charge = None
     error_msg = None
     try:
         charge = stripe_retry_on_error(
             stripe.Charge.create,
             amount=amount,
             currency='usd',
             source=token,
             description='Donation from %s' % email,
             metadata={
                 'email': email,
                 'action': 'donation'
             },
             idempotency_key=True,
         )
     except stripe.error.CardError:
         # card declined
         logger.warn('Card was declined.')
         error_msg = 'Your card was declined'
     except (
             stripe.error.InvalidRequestError,
             # Invalid parameters were supplied to Stripe's API
             stripe.error.AuthenticationError,
             # Authentication with Stripe's API failed
             stripe.error.APIConnectionError,
             # Network communication with Stripe failed
             stripe.error.StripeError,
             # Generic error
     ) as exception:
         logger.error(exception, exc_info=sys.exc_info())
         error_msg = ('Oops, something went wrong on our end.'
                      ' Sorry about that!')
     finally:
         if error_msg:
             messages.error(self.request, error_msg)
         else:
             self.request.session['donated'] = amount
             self.request.session['ga'] = 'donation'
     return charge
示例#10
0
文件: tasks.py 项目: pjsier/muckrock
def failed_payment(invoice_id):
    """Notify a customer about a failed subscription invoice."""
    invoice = stripe_retry_on_error(
        stripe.Invoice.retrieve,
        invoice_id,
    )
    attempt = invoice.attempt_count
    subscription_type = get_subscription_type(invoice)
    profile = Profile.objects.get(customer_id=invoice.customer)
    user = profile.user
    # raise the failed payment flag on the profile
    profile.payment_failed = True
    profile.save()
    subject = u'Your payment has failed'
    org = None
    if subscription_type == 'org':
        org = Organization.objects.get(owner=user)
    if attempt == 4:
        # on last attempt, cancel the user's subscription and lower the failed payment flag
        if subscription_type == 'pro':
            profile.cancel_pro_subscription()
        elif subscription_type == 'org':
            org.cancel_subscription()
        profile.payment_failed = False
        profile.save()
        logger.info('%s subscription has been cancelled due to failed payment',
                    user.username)
        subject = u'Your %s subscription has been cancelled' % subscription_type
        context = {'attempt': 'final', 'type': subscription_type, 'org': org}
    else:
        logger.info('Failed payment by %s, attempt %s', user.username, attempt)
        context = {'attempt': attempt, 'type': subscription_type, 'org': org}
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template='message/notification/failed_payment.txt',
        html_template='message/notification/failed_payment.html',
        subject=subject,
    )
    notification.send(fail_silently=False)
示例#11
0
文件: tasks.py 项目: pjsier/muckrock
def send_charge_receipt(charge_id):
    """Send out a receipt for a charge"""
    charge = stripe_retry_on_error(
        stripe.Charge.retrieve,
        charge_id,
    )
    # if the charge was generated by an invoice, let the invoice handler send the receipt
    if charge.invoice:
        return
    # we should expect charges to have metadata attached when they are made
    try:
        user_email = charge.metadata['email']
        user_action = charge.metadata['action']
    except KeyError:
        logger.warning('Malformed charge metadata, no receipt sent: %s',
                       charge)
        return
    # try getting the user based on the provided email
    # we know from Checkout purchases that logged in users have their email autofilled
    try:
        user = User.objects.get(email=user_email)
    except User.DoesNotExist:
        user = None
    try:
        receipt_functions = {
            'request-purchase': receipts.request_purchase_receipt,
            'request-fee': receipts.request_fee_receipt,
            'crowdfund-payment': receipts.crowdfund_payment_receipt,
            'donation': receipts.donation_receipt,
        }
        receipt_function = receipt_functions[user_action]
    except KeyError:
        logger.warning('Unrecognized charge: %s', user_action)
        receipt_function = receipts.generic_receipt
    receipt = receipt_function(user, charge)
    receipt.send(fail_silently=False)