## $Id: payment.py 12721 2015-03-10 16:02:49Z uli $ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """ These are the payment tickets. """ import decimal import grok import uuid from datetime import datetime from zope.catalog.interfaces import ICatalog from zope.component import getUtilitiesFor, getUtility from zope.event import notify from waeup.ikoba.interfaces import MessageFactory as _ from waeup.ikoba.utils.helpers import attrs_to_fields from waeup.ikoba.utils.logger import Logger from waeup.ikoba.payments.interfaces import ( IPayment, STATE_UNPAID, STATE_FAILED, STATE_PAID, IPaymentGatewayService, IPayer, IPaymentItem, IPayee, IPaymentGatewayServicesLister, IPayableFinder, ) def get_payment(payment_id): """Get payment by payment id. If no such payment can be found in catalog, return none. """ cat = getUtility(ICatalog, name='payments_catalog') result_set = [x for x in cat.searchResults( payment_id=(payment_id, payment_id))] if len(result_set): return result_set[0] return None def find_payable_from_payable_id(payable_id): """Find a payable from its id. Looks up all registered IPayableFinders and returns the first positive result found. """ for name, util in getUtilitiesFor(IPayableFinder): result = util.get_payable_by_id(payable_id) if result is not None: return result return None def format_payment_item_values(payment_item_values, currency): """Format tuples (description, currency, amount) for output. `currency` passed in is the 'target' currency. Returns a list of formated values. Last item is total sum. XXX: we do not really respect currency. If different items have different currencies, we are choked. """ result = [] total = decimal.Decimal("0.00") for descr, item_currency, amount in payment_item_values: total += amount if item_currency != currency: raise ValueError( "Different currencies in payment items not supported.") result.append((descr, '%s %0.2f' % (item_currency, amount))) result.append((_('Total'), '%s %0.2f' % (currency, total))) return result def get_payment_providers(): """Get all payment providers registered. """ return dict( getUtilitiesFor(IPaymentGatewayService) ) class PaymentGatewayServicesLister(grok.GlobalUtility): grok.implements(IPaymentGatewayServicesLister) def __call__(self): """Get all services of payment gateways registered. """ return get_payment_providers() class PaymentProviderServiceBase(grok.GlobalUtility): grok.baseclass() grok.implements(IPaymentGatewayService) title = u'Sample Credit Card Service' @attrs_to_fields class Payment(grok.Container, Logger): """This is a payment. """ grok.implements(IPayment) grok.provides(IPayment) logger_name = 'waeup.ikoba.${sitename}.payments' logger_filename = 'payments.log' logger_format_str = '"%(asctime)s","%(user)s",%(message)s' @property def amount(self): """The amount of a payment. Equals the sum of items contained. """ return sum( [item.amount for item in self.values()], decimal.Decimal("0.00") # default value ) def __init__(self): super(Payment, self).__init__() self.creation_date = datetime.utcnow() self.payment_date = None self.payment_id = u'PAY_' + unicode(uuid.uuid4().hex) self.state = STATE_UNPAID return def approve(self, payment_date=None): """A payment was approved. Successful ending; the payment is marked as payed. If `payment_date` is given, it must be a datetime object giving a datetime in UTC timezone. Raises ObjectModifiedEvent. """ if payment_date is None: payment_date = datetime.utcnow() self.payment_date = payment_date self.state = STATE_PAID notify(grok.ObjectModifiedEvent(self)) def mark_failed(self, reason=None): """Mark payment as failed. Raises ObjectModifiedEvent. """ self.state = STATE_FAILED notify(grok.ObjectModifiedEvent(self)) def add_payment_item(self, item): """Add `item` Returns the key under which the `item` was stored. Please do not make anby assumptions about the key. It will be a string. That is all we can tell. """ cnt = 0 while str(cnt) in self: cnt += 1 self[str(cnt)] = item return str(cnt) @attrs_to_fields class Payer(object): """A Payment is for testing. It cannot be stored in ZODB. """ grok.implements(IPayer) @attrs_to_fields class PaymentItem(grok.Model): grok.implements(IPaymentItem) def __init__(self): super(PaymentItem, self).__init__() @attrs_to_fields class Payee(object): """Someone being paid. This is for testing only and cannot be stored in ZODB. """ grok.implements(IPayee)