def _process_invoices(self, invoices):
        invoices_to_skip = [
            '105756939333',  # Jeremy's initial test.
            '205811359970',  # Adrian refunded this. Code to handle refunds is not yet written.
        ]
        for invoice in invoices:

            if invoice.invoice_id in invoices_to_skip:
                continue

            paid_status = invoice.payout_status.startswith("Paid")
            captured_status = invoice.payout_status.startswith("Captured")
            if not (paid_status or captured_status):
                # See http://help.2checkout.com/articles/Knowledge_Article/Payout-Status
                continue

            new_sale = Sale()

            new_sale.payer_name             = self.payer_name
            new_sale.payer_email            = self.payer_email
            new_sale.processing_fee         = Decimal(invoice.fees_2co)
            new_sale.sale_date              = parse(invoice.date_placed).date()
            new_sale.payment_method         = Sale.PAID_BY_2CO
            new_sale.method_detail          = self.method_detail
            new_sale.ctrlid                 = "2CO:"+invoice.invoice_id
            new_sale.total_paid_by_customer = invoice.customer_total

            if invoice.payout_status.startswith("Paid "):
                depdate = invoice.payout_status[5:]
                new_sale.deposit_date = parse(depdate).date()

            djgo_sale = self.upsert(new_sale)
            self._process_lineitems(djgo_sale, invoice.lineitems)
    def _process_payments(self, payments):

        for payment in payments:

            # TODO: Clean out any existing sale & line items in case of refund, and then skip.
            # Refund sensing logic will be something like this:
            #    if len(payment.refunds)>0
            #      and payment.refunds[0].payment_id == payment.id
            #      and payment.refunds[0].type == "FULL"

            if payment['tender'][0]['type'] == "NO_SALE":
                continue

            if payment['id'] in self.SALES_TO_SKIP: continue

            if len(payment["tender"]) != 1:
                print("Code doesn't handle multiple tenders as in {}. Skipping.".format(payment['id']))
                continue

            sale = Sale()
            sale.sale_date = parse(payment["created_at"]).date()
            sale.payer_name = self.get_name_from_receipt(payment['receipt_url'])
            sale.payer_email = ""  # Annoyingly, not provided by Square.
            sale.payment_method = Sale.PAID_BY_SQUARE
            sale.method_detail = self._get_tender_type(payment)
            sale.total_paid_by_customer = Decimal(payment["tender"][0]["total_money"]["amount"]) / Decimal(100)
            sale.processing_fee = abs(Decimal(payment["processing_fee_money"]["amount"])) / Decimal(100)
            sale.ctrlid = "SQ:" + payment["id"]
            #sale.payer_notes = payment.get("notes", "").strip()

            if payment['id'] == "ixStxgstn56QI8jnJtcCtzMF":
                sale.payer_name = sale.payer_name.replace("M ", "MIKE ")

            django_sale = self.upsert(sale)

            if payment['id'] == "7cQ69ctaeYok1Ry3KOTFbyMF":
                # Person wanted to pay for two weeks while he was in town.
                # I added an item for this but don't like the way it worked out.
                # So I'm treating this as a special case.
                self._special_case_7cQ69ctaeYok1Ry3KOTFbyMF(django_sale)

            elif payment['id'] == "0JFN0loJ0kcy8DXCvuDVwwMF":
                # This is a work trade payment that was erroneously entered as a custom payment.
                # So we do this special processing to ingest it as a 1 month Work Trade membership.
                self._special_case_0JFN0loJ0kcy8DXCvuDVwwMF(django_sale)

            elif payment['id'] == "ixStxgstn56QI8jnJtcCtzMF":
                # This is a six month membership that was erroneously entered as a custom payment.
                # So we do this special processing to ingest it as a 6 month membership.
                self._special_case_ixStxgstn56QI8jnJtcCtzMF(django_sale)

            else:
                if django_sale["protected"] == True:
                    # If the sale is protected then all details are also protected.
                    # Otherwise there's no way to protect a deletion.
                    return
                itemizations = payment['itemizations']
                self._process_itemizations(itemizations, django_sale)
Exemple #3
0
    def _process_subscription_charges(self, charges, subscription, family):
        for charge in charges:

            state = charge["state"]
            if state == "refunded":
                refund = True
            elif state == "captured":
                refund = False
            elif state == "failed":
                continue
            else:
                print("State not handled: " + state)
                continue

            sale = Sale()
            sale.payer_name = subscription['payer_name']
            sale.payer_email = subscription['payer_email']
            if subscription['fee_payer'] == "payer":
                print(
                    "Fee is paid by payer. Situation has not yet been analyzed."
                )
            sale.payment_method = Sale.PAID_BY_WEPAY
            sale.sale_date = date.fromtimestamp(int(
                charge['create_time']))  # TODO: This is UTC timezone.
            sale.total_paid_by_customer = charge["amount"]
            sale.processing_fee = charge["fee"]
            sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX,
                                         charge['subscription_charge_id'])
            django_sale = self.upsert(sale)

            mship = Membership()
            mship.sale = Sale(id=django_sale['id'])
            mship.sale_price = sale.total_paid_by_customer
            if subscription['fee_payer'] == 'payer':
                mship.sale_price -= sale.processing_fee
            if family > 0: mship.sale_price -= Decimal(10.00) * Decimal(family)
            mship.ctrlid = "{}:{}".format(self.CTRLID_PREFIX,
                                          charge['subscription_charge_id'])
            mship.start_date = sale.sale_date
            mship.end_date = mship.start_date + relativedelta(months=1,
                                                              days=-1)
            self.upsert(mship)

            for n in range(family):
                fam = Membership()
                fam.sale = mship.sale
                fam.sale_price = 10.00
                fam.membership_type = Membership.MT_FAMILY
                fam.start_date = mship.start_date
                fam.end_date = mship.end_date
                fam.ctrlid = "{}:{}:{}".format(self.CTRLID_PREFIX,
                                               mship.ctrlid, n)
                self.upsert(fam)
    def _process_recurring_payment(self, agreement: sdk.BillingAgreement,
                                   transaction):
        when_datetime = parse(transaction["time_stamp"])  # type: datetime
        when_local_date = localtime(when_datetime).date()

        payer_name = transaction["payer_name"]
        payer_email = transaction["payer_email"]
        paid_amount = transaction["amount"]["value"]
        net_amount = transaction["net_amount"]["value"]
        fee_amount = str(
            Decimal(-1.0) * Decimal(transaction["fee_amount"]["value"]))
        trans_id = transaction["transaction_id"]
        fam_count = int((float(paid_amount) - 50.00) / 10.00)

        assert transaction["net_amount"]["currency"] == "USD"
        assert transaction["fee_amount"]["currency"] == "USD"
        assert transaction["amount"]["currency"] == "USD"
        assert transaction["time_zone"] == "GMT"
        assert float(paid_amount) >= 50.00

        sale = Sale()
        sale.payment_method = Sale.PAID_BY_PAYPAL
        sale.payer_email = payer_email
        sale.payer_name = payer_name
        sale.sale_date = when_local_date
        sale.total_paid_by_customer = Decimal(
            paid_amount
        )  # The full amount paid by the person, including payment processing fee IF CUSTOMER PAID IT.
        sale.processing_fee = Decimal(fee_amount)
        sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX, trans_id)
        django_sale = self.upsert(sale)
        sale.id = django_sale['id']

        if django_sale["protected"]:
            # If the sale is protected then all details are also protected.
            # Otherwise there's no way to protect a deletion.
            return

        self._member_and_family(sale, 1)
    def _process_subscription_charges(self, charges, subscription, family):
        for charge in charges:

            if not charge["state"].startswith("captured"):
                if not charge["state"] == "failed": print(charge["state"])
                continue

            sale = Sale()
            sale.payer_name = subscription['payer_name']
            sale.payer_email = subscription['payer_email']
            if subscription['fee_payer'] == "payer":
                print("Fee is paid by payer. Situation has not yet been analyzed.")
            sale.payment_method = Sale.PAID_BY_WEPAY
            sale.sale_date = date.fromtimestamp(int(charge['create_time']))  # TODO: This is UTC timezone.
            sale.total_paid_by_customer = charge["amount"]
            sale.processing_fee = charge["fee"]
            sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX, charge['subscription_charge_id'])
            django_sale = self.upsert(sale)

            mship = Membership()
            mship.sale = Sale(id=django_sale['id'])
            mship.sale_price = sale.total_paid_by_customer
            if subscription['fee_payer'] == 'payer': mship.sale_price -= sale.processing_fee
            if family > 0: mship.sale_price -= Decimal(10.00) * Decimal(family)
            mship.ctrlid = "{}:{}".format(self.CTRLID_PREFIX, charge['subscription_charge_id'])
            mship.start_date = sale.sale_date
            mship.end_date = mship.start_date + relativedelta(months=1, days=-1)
            self.upsert(mship)

            for n in range(family):
                fam = Membership()
                fam.sale            = mship.sale
                fam.sale_price      = 10.00
                fam.membership_type = Membership.MT_FAMILY
                fam.start_date      = mship.start_date
                fam.end_date        = mship.end_date
                fam.ctrlid          = "{}:{}:{}".format(self.CTRLID_PREFIX, mship.ctrlid, n)
                self.upsert(fam)
Exemple #6
0
    def _process_checkouts(self, checkouts):
        assert len(checkouts) < self.limit
        for checkout in checkouts:

            # Filter out questionable checkouts

            if not checkout['state'].startswith("captured"):
                continue

            # At this point we know we'll be creating an IncomeTransaction (currently called Sale)

            sale = Sale()
            sale.payment_method = Sale.PAID_BY_WEPAY
            sale.payer_email = checkout['payer_email']
            sale.payer_name = checkout['payer_name']
            sale.sale_date = date.fromtimestamp(int(
                checkout['create_time']))  # TODO: This is UTC timezone.
            sale.total_paid_by_customer = Decimal(
                checkout['gross']
            )  # Docs: "The total dollar amount paid by the payer"
            sale.processing_fee = Decimal(checkout['fee']) + Decimal(
                checkout['app_fee'])
            if checkout['fee_payer'] == 'payer':
                sale.fee_payer = Sale.FEE_PAID_BY_CUSTOMER
            else:
                sale.fee_payer = Sale.FEE_PAID_BY_US
            sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX,
                                         checkout['checkout_id'])
            django_sale = self.upsert(sale)
            sale.id = django_sale['id']

            desc = checkout['short_description']

            if checkout['checkout_id'] in [1877931854, 390559320]:
                # These are membership purchases that were erroneously entered as donations.
                self._process_membership_sale(sale, checkout, 6, 0)
                continue

            if checkout['amount'] == 20 \
             and desc == "Recurring Payment to Donation" \
             and md5(checkout['payer_name'].encode('utf-8')).hexdigest() == "95c53a5e254c1847ad8526b625862294":
                # Dale says that this recurring donation should be treated as a grandfathered membership.
                # I'm checking against md5 of the payer-name so I don't have personal info in source.
                self._process_membership_sale(sale, checkout, 1, 0)
                continue

            months = None
            if desc.startswith("One Month Membership"): months = 1
            elif desc.startswith("Three Month Membership"): months = 3
            elif desc.startswith("Six Month Membership"): months = 6
            elif desc.startswith("Recurring Payment to Dues-Paying Member"):
                months = 1
            elif desc.startswith("Payment to Dues-Paying Member ONE-TIME"):
                months = 1
            if months is not None:
                if desc.endswith("+ 1 family member"): family = 1
                elif desc.endswith("+ 2 family member"): family = 2
                elif desc.endswith("+ 3 family member"): family = 3
                elif desc.endswith("+ 4 family member"): family = 4
                elif desc.endswith("+ 5 family member"): family = 5
                elif desc.endswith("+ 6 family member"): family = 6
                else: family = 0
                self._process_membership_sale(sale, checkout, months, family)
                continue

            if desc in [
                    "Xerocraft KMKR Radio Donation",
                    "Single Maketopolis Ticket Purchase"
            ]:
                # The Maketopolis tickets were for KMKR's musical portion of the event.
                self._process_donation(sale, checkout, ACCT_KMKR_CAMPAIGN)
                continue

            if desc.endswith("Event Payment") \
             or desc.startswith("Recurring Payment to Donation") \
             or desc.startswith("Payment to Donation at"):
                self._process_donation(sale, checkout)
                continue

            print("Didn't recognize: " + desc)
Exemple #7
0
    def _process_payments(self, payments):

        for payment in payments:

            # TODO: Clean out any existing sale & line items in case of refund, and then skip.
            # Refund sensing logic will be something like this:
            #    if len(payment.refunds)>0
            #      and payment.refunds[0].payment_id == payment.id
            #      and payment.refunds[0].type == "FULL"

            if payment['tender'][0]['type'] == "NO_SALE":
                continue

            if payment['id'] in self.SALES_TO_SKIP: continue

            if len(payment["tender"]) != 1:
                print(
                    "Code doesn't handle multiple tenders as in {}. Skipping.".
                    format(payment['id']))
                continue

            sale = Sale()
            sale.sale_date = parse(payment["created_at"]).date()
            sale.payer_name = self.get_name_from_receipt(
                payment['receipt_url'])
            sale.payer_email = ""  # Annoyingly, not provided by Square.
            sale.payment_method = Sale.PAID_BY_SQUARE
            sale.method_detail = self._get_tender_type(payment)
            sale.total_paid_by_customer = Decimal(
                payment["tender"][0]["total_money"]["amount"]) / Decimal(100)
            sale.processing_fee = abs(
                Decimal(
                    payment["processing_fee_money"]["amount"])) / Decimal(100)
            sale.ctrlid = "SQ:" + payment["id"]
            #sale.payer_notes = payment.get("notes", "").strip()

            if payment['id'] == "ixStxgstn56QI8jnJtcCtzMF":
                sale.payer_name = sale.payer_name.replace("M ", "MIKE ")

            django_sale = self.upsert(sale)

            if payment['id'] == "7cQ69ctaeYok1Ry3KOTFbyMF":
                # Person wanted to pay for two weeks while he was in town.
                # I added an item for this but don't like the way it worked out.
                # So I'm treating this as a special case.
                self._special_case_7cQ69ctaeYok1Ry3KOTFbyMF(django_sale)

            elif payment['id'] == "0JFN0loJ0kcy8DXCvuDVwwMF":
                # This is a work trade payment that was erroneously entered as a custom payment.
                # So we do this special processing to ingest it as a 1 month Work Trade membership.
                self._special_case_0JFN0loJ0kcy8DXCvuDVwwMF(django_sale)

            elif payment['id'] == "ixStxgstn56QI8jnJtcCtzMF":
                # This is a six month membership that was erroneously entered as a custom payment.
                # So we do this special processing to ingest it as a 6 month membership.
                self._special_case_ixStxgstn56QI8jnJtcCtzMF(django_sale)

            else:
                if django_sale["protected"] == True:
                    # If the sale is protected then all details are also protected.
                    # Otherwise there's no way to protect a deletion.
                    return
                itemizations = payment['itemizations']
                self._process_itemizations(itemizations, django_sale)
    def _process_checkouts(self, checkouts):
        assert len(checkouts) < self.limit
        for checkout in checkouts:

            # Filter out questionable checkouts

            if not checkout['state'].startswith("captured"):
                continue

            # At this point we know we'll be creating an IncomeTransaction (currently called Sale)

            sale = Sale()
            sale.payment_method = Sale.PAID_BY_WEPAY
            sale.payer_email = checkout['payer_email']
            sale.payer_name = checkout['payer_name']
            sale.sale_date = date.fromtimestamp(int(checkout['create_time']))  # TODO: This is UTC timezone.
            sale.total_paid_by_customer = Decimal(checkout['gross'])  # Docs: "The total dollar amount paid by the payer"
            sale.processing_fee = Decimal(checkout['fee']) + Decimal(checkout['app_fee'])
            sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX, checkout['checkout_id'])
            django_sale = self.upsert(sale)
            sale.id = django_sale['id']

            desc = checkout['short_description']

            if checkout['checkout_id'] in [1877931854, 390559320]:
                # These are membership purchases that were erroneously entered as donations.
                self._process_membership_sale(sale, checkout, 6, 0)
                continue

            if checkout['amount'] == 20 \
             and desc == "Recurring Payment to Donation" \
             and md5(checkout['payer_name'].encode('utf-8')).hexdigest() == "95c53a5e254c1847ad8526b625862294":
                # Dale says that this recurring donation should be treated as a grandfathered membership.
                # I'm checking against md5 of the payer-name so I don't have personal info in source.
                self._process_membership_sale(sale, checkout, 1, 0)
                continue

            months = None
            if desc.startswith("One Month Membership"): months = 1
            elif desc.startswith("Three Month Membership"): months = 3
            elif desc.startswith("Six Month Membership"): months = 6
            elif desc.startswith("Recurring Payment to Dues-Paying Member"): months = 1
            elif desc.startswith("Payment to Dues-Paying Member ONE-TIME"): months = 1
            if months is not None:
                if desc.endswith("+ 1 family member"): family = 1
                elif desc.endswith("+ 2 family member"): family = 2
                elif desc.endswith("+ 3 family member"): family = 3
                elif desc.endswith("+ 4 family member"): family = 4
                elif desc.endswith("+ 5 family member"): family = 5
                elif desc.endswith("+ 6 family member"): family = 6
                else: family = 0
                self._process_membership_sale(sale, checkout, months, family)
                continue

            if desc.endswith("Event Payment") \
             or desc.startswith("Recurring Payment to Donation") \
             or desc.startswith("Payment to Donation at"):
                self._process_donation(sale, checkout)
                continue

            print("Didn't recognize: "+desc)
    def _process_payment(self, payment: sdk.Payment):
        payment_id = payment['id']
        if payment_id in self.PAYMENTS_TO_IGNORE:
            return
        state = payment['state']  # e.g. 'approved'
        when_datetime = parse(payment['create_time'])  # type: datetime
        when_local_date = localtime(when_datetime).date()
        if 'payer' not in payment:
            print(payment)
            return
        if 'payer_info' not in payment['payer']:
            print(payment)
            return
        who_fname = payment['payer']['payer_info']['first_name']
        who_lname = payment['payer']['payer_info']['last_name']
        try:
            who_email = payment['payer']['payer_info']['email']
        except:
            who_email = ""

        assert len(
            payment['transactions']
        ) == 1, "Code can't yet deal with multiple transactions payment."
        transaction = payment['transactions'][0]
        payment_amount = transaction['amount']['total']
        if 'custom' in transaction:  # Xerocraft.org puts an indication of what was paid for in "custom"
            what = transaction['custom']
        else:
            what = None

        resources = transaction['related_resources']
        sale_amt = None
        refund_amt = None
        if len(resources) == 0:
            # This has only been observed in a test transaction generated by Kyle
            print("K", end="")
            return
        for resource in resources:
            if 'sale' in resource:
                sale = resource['sale']
                if sale_amt is not None:
                    print(
                        "WARNING (not handled): Multiple sale resources in sale to "
                        + who_email + " on " + str(when_local_date))
                sale_amt = float(sale['amount']['total'])
                pay_mode = sale['payment_mode']  # e.g. 'INSTANT_TRANSFER'
                trans_fee = float(sale['transaction_fee']['value'])
            if 'refund' in resource:
                refund = resource['refund']
                if refund['state'] is "failed":
                    continue
                if refund_amt is not None:
                    print(
                        "WARNING (not handled): Multiple refunds in sale to " +
                        who_email + " on " + str(when_local_date))
                refund_amt = float(refund['amount']['total'])
            if sale_amt is not None and refund_amt is not None:
                # NOTE: Refunds will require manual processing. Adjusted transactions should be marked "protected".
                if sale_amt == refund_amt:
                    print("R", end="")
                    return
                else:
                    print(
                        "Code doesn't yet deal with partial refunds. Sale was to "
                        + who_email + " on " + str(when_local_date))
                    return

        sale = Sale()
        sale.payment_method = Sale.PAID_BY_PAYPAL
        sale.payer_email = who_email
        sale.payer_name = "{} {}".format(who_fname, who_lname)
        sale.sale_date = when_local_date
        sale.total_paid_by_customer = Decimal(
            sale_amt
        )  # The full amount paid by the person, including payment processing fee IF CUSTOMER PAID IT.
        sale.processing_fee = Decimal(trans_fee)
        sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX, payment_id)
        django_sale = self.upsert(sale)
        sale.id = django_sale['id']

        if django_sale["protected"] == True:
            # If the sale is protected then all details are also protected.
            # Otherwise there's no way to protect a deletion.
            return

        if what is None:
            self._process_unknown_item(sale)
        elif what.startswith("DON_"):
            self._process_donation_item(sale, what)
        elif what.startswith("MSHIP_"):
            self._process_non_recurring_membership_item(sale, what)
        else:
            print("Unkown item: " + what)