예제 #1
0
 def after_model_change(self, form, new_donation, is_created):
     if is_created:
         send_receipt(
             email=new_donation.email,
             date=new_donation.payment_date,
             amount=new_donation.amount,
             name='%s %s' % (new_donation.first_name, new_donation.last_name),
             is_donation=new_donation.is_donation,
             editor_name=new_donation.editor_name,
         )
예제 #2
0
 def after_model_change(self, form, new_donation, is_created):
     if is_created:
         send_receipt(
             email=new_donation.email,
             date=new_donation.payment_date,
             amount=new_donation.amount,
             name='%s %s' % (new_donation.first_name, new_donation.last_name),
             is_donation=new_donation.is_donation,
             editor_name=new_donation.editor_name,
         )
예제 #3
0
    def log_stripe_charge(cls, charge):
        """Log successful Stripe charge.

        Args:
            charge: The charge object from Stripe. More information about it is
                available at https://stripe.com/docs/api/python#charge_object.
        """
        logging.debug('Processing Stripe charge...')

        bt = stripe.BalanceTransaction.retrieve(charge.balance_transaction)

        if bt.currency.lower() not in SUPPORTED_CURRENCIES:
            logging.warning("Unsupported currency: ", bt.currency)
            return

        new_donation = cls(
            first_name=charge.source.name,
            last_name='',
            amount=bt.net / 100,  # cents should be converted
            fee=bt.fee / 100,  # cents should be converted
            currency=bt.currency.lower(),
            transaction_id=charge.id,
            payment_method=PAYMENT_METHOD_STRIPE,

            is_donation=charge.metadata.is_donation,

            email=charge.metadata.email,
            address_street=charge.source.address_line1,
            address_city=charge.source.address_city,
            address_state=charge.source.address_state,
            address_postcode=charge.source.address_zip,
            address_country=charge.source.address_country,
        )

        if charge.metadata.is_donation is True or charge.metadata.is_donation == "True":
            new_donation.can_contact = charge.metadata.can_contact == u'True'
            new_donation.anonymous = charge.metadata.anonymous == u'True'
            if 'editor' in charge.metadata:
                new_donation.editor_name = charge.metadata.editor
        else:  # Organization payment
            new_donation.invoice_number = charge.metadata.invoice_number

        db.session.add(new_donation)
        db.session.commit()
        logging.info('Stripe: Payment added. ID: %s.', new_donation.id)

        send_receipt(
            email=new_donation.email,
            date=new_donation.payment_date,
            amount=new_donation.amount,
            name=new_donation.first_name,  # Last name is not used with Stripe
            is_donation=new_donation.is_donation,
            editor_name=new_donation.editor_name,
        )
예제 #4
0
    def log_stripe_charge(cls, charge):
        """Log successful Stripe charge.

        Args:
            charge: The charge object from Stripe. More information about it is
                available at https://stripe.com/docs/api/python#charge_object.
        """
        logging.debug('Processing Stripe charge...')

        bt = stripe.BalanceTransaction.retrieve(charge.balance_transaction)

        new_donation = cls(
            first_name=charge.source.name,
            last_name='',
            amount=bt.net / 100,  # cents should be converted
            fee=bt.fee / 100,  # cents should be converted
            transaction_id=charge.id,
            payment_method=PAYMENT_METHOD_STRIPE,

            is_donation=charge.metadata.is_donation,

            email=charge.metadata.email,
            address_street=charge.source.address_line1,
            address_city=charge.source.address_city,
            address_state=charge.source.address_state,
            address_postcode=charge.source.address_zip,
            address_country=charge.source.address_country,
        )

        if charge.metadata.is_donation is True or charge.metadata.is_donation == "True":
            new_donation.can_contact = charge.metadata.can_contact == u'True'
            new_donation.anonymous = charge.metadata.anonymous == u'True'
            if 'editor' in charge.metadata:
                new_donation.editor_name = charge.metadata.editor
        else:  # Organization payment
            new_donation.invoice_number = charge.metadata.invoice_number

        db.session.add(new_donation)
        db.session.commit()
        logging.info('Stripe: Payment added. ID: %s.', new_donation.id)

        send_receipt(
            email=new_donation.email,
            date=new_donation.payment_date,
            amount=new_donation.amount,
            name=new_donation.first_name,  # Last name is not used with Stripe
            is_donation=new_donation.is_donation,
            editor_name=new_donation.editor_name,
        )
예제 #5
0
    def verify_and_log_wepay_checkout(cls,
                                      checkout_id,
                                      is_donation,
                                      editor=None,
                                      anonymous=None,
                                      can_contact=None,
                                      invoice_number=None):
        logging.debug('Processing WePay checkout...')

        # Looking up updated information about the object
        wepay = WePay(production=current_app.config['PAYMENT_PRODUCTION'],
                      access_token=current_app.config['WEPAY_ACCESS_TOKEN'])
        details = wepay.call('/checkout', {'checkout_id': checkout_id})

        if 'error' in details:
            logging.warning('WePay: Error: %s', details['error_description'])
            return False

        if 'gross' not in details:
            logging.warning('WePay: The total dollar amount paid is missing')
            return False

        if details['gross'] < 0.50:
            # Tiny donation
            logging.info('WePay: Tiny payment ($%s).', details['gross'])
            return True

        if details['state'] in ['settled', 'captured']:
            # Payment has been received

            # Checking that txn_id has not been previously processed
            if cls.get_by_transaction_id(details['checkout_id']) is not None:
                logging.info('WePay: Transaction ID %s has been used before.',
                             details['checkout_id'])
                return

            new_payment = cls(
                is_donation=is_donation,
                first_name=details['payer_name'],
                last_name='',
                email=details['payer_email'],
                amount=details['gross'] - details['fee'],
                fee=details['fee'],
                transaction_id=checkout_id,
                payment_method=PAYMENT_METHOD_WEPAY,
            )

            if is_donation:
                new_payment.editor_name = editor
                new_payment.can_contact = can_contact
                new_payment.anonymous = anonymous
            else:
                new_payment.invoice_number = invoice_number

            if 'shipping_address' in details:
                address = details['shipping_address']
                new_payment.address_street = "%s\n%s" % (address['address1'],
                                                         address['address2'])
                new_payment.address_city = address['city']
                if 'state' in address:  # US address
                    new_payment.address_state = address['state']
                else:
                    new_payment.address_state = address['region']
                if 'zip' in address:  # US address
                    new_payment.address_postcode = address['zip']
                else:
                    new_payment.address_postcode = address['postcode']

            db.session.add(new_payment)
            db.session.commit()
            logging.info('WePay: Payment added. ID: %s.', new_payment.id)

            send_receipt(
                email=new_payment.email,
                date=new_payment.payment_date,
                amount=new_payment.amount,
                name='%s %s' % (new_payment.first_name, new_payment.last_name),
                is_donation=new_payment.is_donation,
                editor_name=new_payment.editor_name,
            )

        elif details['state'] in ['authorized', 'reserved']:
            # Payment is pending
            logging.info('WePay: Payment is pending. State: "%s".',
                         details['state'])
            pass

        elif details['state'] in [
                'expired', 'cancelled', 'failed', 'refunded', 'chargeback'
        ]:
            # Payment has failed
            logging.warning('WePay: Payment has failed. State: "%s".',
                            details['state'])
            pass

        else:
            # Unknown status
            logging.warning('WePay: Unknown status.')
            return False

        return True
예제 #6
0
    def process_paypal_ipn(cls, form):
        """Processor for PayPal IPNs (Instant Payment Notifications).

        Should be used only after IPN request is verified. See PayPal documentation for
        more info about the process.

        Args:
            form: The form parameters from IPN request that contains IPN variables.
                See https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables/
                for more info about them.
        """
        logging.debug('Processing PayPal IPN...')

        # Only processing completed donations
        if form['payment_status'] != 'Completed':
            logging.info('PayPal: Payment not completed. Status: "%s".',
                         form['payment_status'])
            return

        # We shouldn't process transactions to address for payments
        # TODO: Clarify what this address is
        if form['business'] == current_app.config['PAYPAL_BUSINESS']:
            logging.info('PayPal: Recieved payment to address for payments.')
            return

        if form['receiver_email'] != current_app.config['PAYPAL_PRIMARY_EMAIL']:
            logging.warning('PayPal: Not primary email. Got "%s".',
                            form['receiver_email'])
            return
        if float(form['mc_gross']) < 0.50:
            # Tiny donation
            logging.info('PayPal: Tiny donation ($%s).', form['mc_gross'])
            return

        # Checking that txn_id has not been previously processed
        if cls.get_by_transaction_id(form['txn_id']) is not None:
            logging.info('PayPal: Transaction ID %s has been used before.',
                         form['txn_id'])
            return

        if 'option_name3' in form and form['option_name3'] == "is_donation" \
                and form['option_selection3'] != 'yes':
            is_donation = False
        else:
            # If third option (whether it is donation or not) is not specified, assuming
            # that it is donation. This is done to support old IPN messages.
            is_donation = True

        new_payment = cls(
            is_donation=is_donation,
            first_name=form['first_name'],
            last_name=form['last_name'],
            email=form['payer_email'],
            address_street=form.get('address_street'),
            address_city=form.get('address_city'),
            address_state=form.get('address_state'),
            address_postcode=form.get('address_zip'),
            address_country=form.get('address_country'),
            amount=float(form['mc_gross']) - float(form['mc_fee']),
            fee=float(form['mc_fee']),
            transaction_id=form['txn_id'],
            payment_method=PAYMENT_METHOD_PAYPAL,
        )

        if is_donation:
            new_payment.editor_name = form.get('custom')
            if 'option_name1' in form and 'option_name2' in form:
                if (form['option_name1'] == 'anonymous' and form['option_selection1'] == 'yes') or \
                        (form['option_name2'] == 'anonymous' and form['option_selection2'] == 'yes') or \
                                form['option_name2'] == 'yes':
                    new_payment.anonymous = True
                if (form['option_name1'] == 'contact' and form['option_selection1'] == 'yes') or \
                        (form['option_name2'] == 'contact' and form['option_selection2'] == 'yes') or \
                                form['option_name2'] == 'yes':
                    new_payment.can_contact = True
        else:
            if 'option_name4' in form and form[
                    'option_name4'] == 'invoice_number':
                new_payment.invoice_number = int(form.get("option_selection4"))
            else:
                logging.warning(
                    "PayPal: Can't find invoice number if organization payment."
                )

        db.session.add(new_payment)
        db.session.commit()
        logging.info('PayPal: Payment added. ID: %s.', new_payment.id)

        send_receipt(
            email=new_payment.email,
            date=new_payment.payment_date,
            amount=new_payment.amount,
            name='%s %s' % (new_payment.first_name, new_payment.last_name),
            is_donation=new_payment.is_donation,
            editor_name=new_payment.editor_name,
        )
예제 #7
0
    def verify_and_log_wepay_checkout(cls, checkout_id, is_donation,
                                      editor=None, anonymous=None, can_contact=None,
                                      invoice_number=None):
        logging.debug('Processing WePay checkout...')

        # Looking up updated information about the object
        wepay = WePay(production=current_app.config['PAYMENT_PRODUCTION'],
                      access_token=current_app.config['WEPAY_ACCESS_TOKEN'])
        details = wepay.call('/checkout', {'checkout_id': checkout_id})

        if 'error' in details:
            logging.warning('WePay: Error: %s', details['error_description'])
            return False

        if 'gross' not in details:
            logging.warning('WePay: The total dollar amount paid is missing')
            return False

        if details['gross'] < 0.50:
            # Tiny donation
            logging.info('WePay: Tiny payment ($%s).', details['gross'])
            return True

        if details['state'] in ['settled', 'captured']:
            # Payment has been received

            # Checking that txn_id has not been previously processed
            if cls.get_by_transaction_id(details['checkout_id']) is not None:
                logging.info('WePay: Transaction ID %s has been used before.', details['checkout_id'])
                return

            new_payment = cls(
                is_donation=is_donation,
                first_name=details['payer_name'],
                last_name='',
                email=details['payer_email'],
                amount=details['gross'] - details['fee'],
                fee=details['fee'],
                transaction_id=checkout_id,
                payment_method=PAYMENT_METHOD_WEPAY,
            )

            if is_donation:
                new_payment.editor_name = editor
                new_payment.can_contact = can_contact
                new_payment.anonymous = anonymous
            else:
                new_payment.invoice_number = invoice_number

            if 'shipping_address' in details:
                address = details['shipping_address']
                new_payment.address_street = "%s\n%s" % (address['address1'], address['address2'])
                new_payment.address_city = address['city']
                if 'state' in address:  # US address
                    new_payment.address_state = address['state']
                else:
                    new_payment.address_state = address['region']
                if 'zip' in address:  # US address
                    new_payment.address_postcode = address['zip']
                else:
                    new_payment.address_postcode = address['postcode']

            db.session.add(new_payment)
            db.session.commit()
            logging.info('WePay: Payment added. ID: %s.', new_payment.id)

            send_receipt(
                email=new_payment.email,
                date=new_payment.payment_date,
                amount=new_payment.amount,
                name='%s %s' % (new_payment.first_name, new_payment.last_name),
                is_donation=new_payment.is_donation,
                editor_name=new_payment.editor_name,
            )

        elif details['state'] in ['authorized', 'reserved']:
            # Payment is pending
            logging.info('WePay: Payment is pending. State: "%s".', details['state'])
            pass

        elif details['state'] in ['expired', 'cancelled', 'failed', 'refunded', 'chargeback']:
            # Payment has failed
            logging.warning('WePay: Payment has failed. State: "%s".', details['state'])
            pass

        else:
            # Unknown status
            logging.warning('WePay: Unknown status.')
            return False

        return True
예제 #8
0
    def process_paypal_ipn(cls, form):
        """Processor for PayPal IPNs (Instant Payment Notifications).

        Should be used only after IPN request is verified. See PayPal documentation for
        more info about the process.

        Args:
            form: The form parameters from IPN request that contains IPN variables.
                See https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables/
                for more info about them.
        """
        logging.debug('Processing PayPal IPN...')

        # Only processing completed donations
        if form['payment_status'] != 'Completed':
            logging.info('PayPal: Payment not completed. Status: "%s".', form['payment_status'])
            return

        # We shouldn't process transactions to address for payments
        # TODO: Clarify what this address is
        if form['business'] == current_app.config['PAYPAL_BUSINESS']:
            logging.info('PayPal: Recieved payment to address for payments.')
            return

        if form['receiver_email'] != current_app.config['PAYPAL_PRIMARY_EMAIL']:
            logging.warning('PayPal: Not primary email. Got "%s".', form['receiver_email'])
            return
        if float(form['mc_gross']) < 0.50:
            # Tiny donation
            logging.info('PayPal: Tiny donation ($%s).', form['mc_gross'])
            return

        # Checking that txn_id has not been previously processed
        if cls.get_by_transaction_id(form['txn_id']) is not None:
            logging.info('PayPal: Transaction ID %s has been used before.', form['txn_id'])
            return

        if 'option_name3' in form and form['option_name3'] == "is_donation" \
                and form['option_selection3'] != 'yes':
            is_donation = False
        else:
            # If third option (whether it is donation or not) is not specified, assuming
            # that it is donation. This is done to support old IPN messages.
            is_donation = True

        new_payment = cls(
            is_donation=is_donation,
            first_name=form['first_name'],
            last_name=form['last_name'],
            email=form['payer_email'],
            address_street=form.get('address_street'),
            address_city=form.get('address_city'),
            address_state=form.get('address_state'),
            address_postcode=form.get('address_zip'),
            address_country=form.get('address_country'),
            amount=float(form['mc_gross']) - float(form['mc_fee']),
            fee=float(form['mc_fee']),
            transaction_id=form['txn_id'],
            payment_method=PAYMENT_METHOD_PAYPAL,
        )

        if is_donation:
            new_payment.editor_name = form.get('custom')
            if 'option_name1' in form and 'option_name2' in form:
                if (form['option_name1'] == 'anonymous' and form['option_selection1'] == 'yes') or \
                        (form['option_name2'] == 'anonymous' and form['option_selection2'] == 'yes') or \
                                form['option_name2'] == 'yes':
                    new_payment.anonymous = True
                if (form['option_name1'] == 'contact' and form['option_selection1'] == 'yes') or \
                        (form['option_name2'] == 'contact' and form['option_selection2'] == 'yes') or \
                                form['option_name2'] == 'yes':
                    new_payment.can_contact = True
        else:
            if 'option_name4' in form and form['option_name4'] == 'invoice_number':
                new_payment.invoice_number = int(form.get("option_selection4"))
            else:
                logging.warning("PayPal: Can't find invoice number if organization payment.")

        db.session.add(new_payment)
        db.session.commit()
        logging.info('PayPal: Payment added. ID: %s.', new_payment.id)

        send_receipt(
            email=new_payment.email,
            date=new_payment.payment_date,
            amount=new_payment.amount,
            name='%s %s' % (new_payment.first_name, new_payment.last_name),
            is_donation=new_payment.is_donation,
            editor_name=new_payment.editor_name,
        )
예제 #9
0
    def process_paypal_ipn(cls, form):
        """Processor for PayPal IPNs (Instant Payment Notifications).

        Should be used only after IPN request is verified. See PayPal documentation for
        more info about the process.

        Args:
            form: The form parameters from IPN request that contains IPN variables.
                See https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables/
                for more info about them.
        """
        logging.debug('Processing PayPal IPN...')

        # Only processing completed donations
        if form['payment_status'] != 'Completed':
            # TODO(roman): Convert to regular `logging.info` call when such detailed logs
            # are no longer necessary to capture.
            get_sentry_client().captureMessage(
                "PayPal: Payment is not completed",
                level=logging.INFO,
                extra={"ipn_content": form})
            return

        account_ids = current_app.config[
            'PAYPAL_ACCOUNT_IDS']  # "currency => account" mapping

        if form['mc_currency'].lower() not in SUPPORTED_CURRENCIES:
            logging.warning("PayPal IPN: Unsupported currency",
                            extra={"ipn_content": form})
            return

        # We shouldn't process transactions to address for payments
        # TODO: Clarify what this address is
        if form['business'] == current_app.config['PAYPAL_BUSINESS']:
            logging.info('PayPal: Received payment to address for payments.',
                         extra={"ipn_content": form})
            return

        # Checking if payment was sent to the right account depending on the currency
        if form['mc_currency'].upper() in account_ids:
            receiver_email_expected = current_app.config['PAYPAL_ACCOUNT_IDS'][
                form['mc_currency'].upper()]
            if receiver_email_expected != form['receiver_email']:
                logging.warning("Received payment to an unexpected address",
                                extra={
                                    "currency": form['mc_currency'],
                                    "received_to_address":
                                    form['receiver_email'],
                                    "expected_address":
                                    receiver_email_expected,
                                    "ipn_content": form,
                                })
        if form['receiver_email'] not in account_ids.values():
            logging.warning('PayPal: Unexpected receiver email',
                            extra={
                                "received_to_address": form['receiver_email'],
                                "ipn_content": form,
                            })

        if float(form['mc_gross']) < 0.50:
            # Tiny donation
            logging.info('PayPal: Tiny donation', extra={"ipn_content": form})
            return

        # Checking that txn_id has not been previously processed
        if cls.get_by_transaction_id(form['txn_id']) is not None:
            logging.info('PayPal: Transaction ID has been used before',
                         extra={
                             "transaction_id": form['txn_id'],
                             "ipn_content": form,
                         })
            return

        options = cls._extract_paypal_ipn_options(form)

        # If donation option (whether it is donation or not) is not specified, assuming
        # that this payment is donation. This is done to support old IPN messages.
        is_donation = options.get("is_donation", "yes") == "yes"

        new_payment = cls(
            is_donation=is_donation,
            first_name=form['first_name'],
            last_name=form['last_name'],
            email=form['payer_email'],
            address_street=form.get('address_street'),
            address_city=form.get('address_city'),
            address_state=form.get('address_state'),
            address_postcode=form.get('address_zip'),
            address_country=form.get('address_country'),
            amount=float(form['mc_gross']) - float(form['mc_fee']),
            fee=float(form['mc_fee']),
            transaction_id=form['txn_id'],
            currency=form['mc_currency'].lower(),
            payment_method=PAYMENT_METHOD_PAYPAL,
        )

        if is_donation:
            new_payment.editor_name = form.get('custom')

            anonymous_opt = options.get("anonymous")
            if anonymous_opt is None:
                logging.warning("PayPal: Anonymity option is missing",
                                extra={"ipn_content": form})
            else:
                new_payment.anonymous = anonymous_opt == "yes"

            contact_opt = options.get("contact")
            if contact_opt is None:
                logging.warning("PayPal: Contact option is missing",
                                extra={"ipn_content": form})
            else:
                new_payment.can_contact = contact_opt == "yes"

        else:
            invoice_num_opt = options.get("invoice_number")
            if invoice_num_opt is None:
                logging.warning(
                    "PayPal: Invoice number is missing from org payment",
                    extra={"ipn_content": form})
            else:
                new_payment.invoice_number = int(invoice_num_opt)

        db.session.add(new_payment)
        db.session.commit()
        logging.info('PayPal: Payment added. ID: %s.', new_payment.id)

        send_receipt(
            email=new_payment.email,
            date=new_payment.payment_date,
            amount=new_payment.amount,
            name='%s %s' % (new_payment.first_name, new_payment.last_name),
            is_donation=new_payment.is_donation,
            editor_name=new_payment.editor_name,
        )