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_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)
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)