source: main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/payments/payment.py @ 12731

Last change on this file since 12731 was 12721, checked in by uli, 10 years ago

Implement a general payable finder.

  • Property svn:keywords set to Id
File size: 5.7 KB
Line 
1## $Id: payment.py 12721 2015-03-10 16:02:49Z uli $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19These are the payment tickets.
20"""
21import decimal
22import grok
23import uuid
24from datetime import datetime
25from zope.catalog.interfaces import ICatalog
26from zope.component import getUtilitiesFor, getUtility
27from zope.event import notify
28from waeup.ikoba.interfaces import MessageFactory as _
29from waeup.ikoba.utils.helpers import attrs_to_fields
30from waeup.ikoba.utils.logger import Logger
31from waeup.ikoba.payments.interfaces import (
32    IPayment, STATE_UNPAID, STATE_FAILED, STATE_PAID,
33    IPaymentGatewayService, IPayer, IPaymentItem, IPayee,
34    IPaymentGatewayServicesLister, IPayableFinder,
35    )
36
37
38def get_payment(payment_id):
39    """Get payment by payment id.
40
41    If no such payment can be found in catalog, return none.
42    """
43    cat = getUtility(ICatalog, name='payments_catalog')
44    result_set = [x for x in cat.searchResults(
45        payment_id=(payment_id, payment_id))]
46    if len(result_set):
47        return result_set[0]
48    return None
49
50
51def find_payable_from_payable_id(payable_id):
52    """Find a payable from its id.
53
54    Looks up all registered IPayableFinders and returns the first
55    positive result found.
56    """
57    for name, util in getUtilitiesFor(IPayableFinder):
58        result = util.get_payable_by_id(payable_id)
59        if result is not None:
60            return result
61    return None
62
63
64def format_payment_item_values(payment_item_values, currency):
65    """Format tuples (description, currency, amount) for output.
66
67    `currency` passed in is the 'target' currency.
68
69    Returns a list of formated values. Last item is total sum.
70    XXX: we do not really respect currency. If different items
71         have different currencies, we are choked.
72    """
73    result = []
74    total = decimal.Decimal("0.00")
75    for descr, item_currency, amount in payment_item_values:
76        total += amount
77        if item_currency != currency:
78            raise ValueError(
79                "Different currencies in payment items not supported.")
80        result.append((descr, '%s %0.2f' % (item_currency, amount)))
81    result.append((_('Total'), '%s %0.2f' % (currency, total)))
82    return result
83
84
85def get_payment_providers():
86    """Get all payment providers registered.
87    """
88    return dict(
89        getUtilitiesFor(IPaymentGatewayService)
90    )
91
92
93class PaymentGatewayServicesLister(grok.GlobalUtility):
94    grok.implements(IPaymentGatewayServicesLister)
95
96    def __call__(self):
97        """Get all services of payment gateways registered.
98        """
99        return get_payment_providers()
100
101
102class PaymentProviderServiceBase(grok.GlobalUtility):
103
104    grok.baseclass()
105    grok.implements(IPaymentGatewayService)
106
107    title = u'Sample Credit Card Service'
108
109
110@attrs_to_fields
111class Payment(grok.Container, Logger):
112    """This is a payment.
113    """
114    grok.implements(IPayment)
115    grok.provides(IPayment)
116
117    logger_name = 'waeup.ikoba.${sitename}.payments'
118    logger_filename = 'payments.log'
119    logger_format_str = '"%(asctime)s","%(user)s",%(message)s'
120
121    @property
122    def amount(self):
123        """The amount of a payment.
124
125        Equals the sum of items contained.
126        """
127        return sum(
128            [item.amount for item in self.values()],
129            decimal.Decimal("0.00")  # default value
130        )
131
132    def __init__(self):
133        super(Payment, self).__init__()
134        self.creation_date = datetime.utcnow()
135        self.payment_date = None
136        self.payment_id = u'PAY_' + unicode(uuid.uuid4().hex)
137        self.state = STATE_UNPAID
138        return
139
140    def approve(self, payment_date=None):
141        """A payment was approved.
142
143        Successful ending; the payment is marked as payed.
144
145        If `payment_date` is given, it must be a datetime object
146        giving a datetime in UTC timezone.
147
148        Raises ObjectModifiedEvent.
149        """
150        if payment_date is None:
151            payment_date = datetime.utcnow()
152        self.payment_date = payment_date
153        self.state = STATE_PAID
154        notify(grok.ObjectModifiedEvent(self))
155
156    def mark_failed(self, reason=None):
157        """Mark payment as failed.
158
159        Raises ObjectModifiedEvent.
160        """
161        self.state = STATE_FAILED
162        notify(grok.ObjectModifiedEvent(self))
163
164    def add_payment_item(self, item):
165        """Add `item`
166
167        Returns the key under which the `item` was stored. Please do
168        not make anby assumptions about the key. It will be a
169        string. That is all we can tell.
170
171        """
172        cnt = 0
173        while str(cnt) in self:
174            cnt += 1
175        self[str(cnt)] = item
176        return str(cnt)
177
178
179@attrs_to_fields
180class Payer(object):
181    """A Payment is for testing.
182
183    It cannot be stored in ZODB.
184    """
185    grok.implements(IPayer)
186
187
188@attrs_to_fields
189class PaymentItem(grok.Model):
190
191    grok.implements(IPaymentItem)
192
193    def __init__(self):
194        super(PaymentItem, self).__init__()
195
196
197@attrs_to_fields
198class Payee(object):
199    """Someone being paid.
200
201    This is for testing only and cannot be stored in ZODB.
202    """
203    grok.implements(IPayee)
Note: See TracBrowser for help on using the repository browser.