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

Last change on this file since 12737 was 12734, checked in by uli, 10 years ago

Restructure things a bit.

  • Property svn:keywords set to Id
File size: 6.0 KB
Line 
1## $Id: payment.py 12734 2015-03-11 17:08:34Z 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    """Base for IPaymentGatewayServices.
104    """
105    grok.baseclass()
106    grok.implements(IPaymentGatewayService)
107
108    title = u'Sample Credit Card Service'
109
110    def store(self, payment):
111        """Store `payment` in site.
112        """
113        site = grok.getSite()
114        payments = site['payments']
115        if payment.payment_id in payments:
116            del site['payments'][payment.payment_id]
117        site['payments'][payment.payment_id] = payment
118
119
120@attrs_to_fields
121class Payment(grok.Model, Logger):
122    """This is a payment.
123    """
124    grok.implements(IPayment)
125    grok.provides(IPayment)
126
127    logger_name = 'waeup.ikoba.${sitename}.payments'
128    logger_filename = 'payments.log'
129    logger_format_str = '"%(asctime)s","%(user)s",%(message)s'
130
131    def __init__(self, payer, payable, payee=None):
132        super(Payment, self).__init__()
133        item_amounts = [decimal.Decimal("0.00"), ]
134        item_amounts += [item.amount for item in payable.payment_items]
135        self.amount = sum(item_amounts)
136        self.payer_id = payer.payer_id
137        self.payable_id = payable.payable_id
138        self.title = payable.title
139        self.creation_date = datetime.utcnow()
140        self.payment_date = None
141        self.payment_id = u'PAY_' + unicode(uuid.uuid4().hex)
142        self.state = STATE_UNPAID
143        self.currency = payable.currency
144        if payee is not None:
145            self.payee_id = payee.payee_id
146        return
147
148    def approve(self, payment_date=None):
149        """A payment was approved.
150
151        Successful ending; the payment is marked as payed.
152
153        If `payment_date` is given, it must be a datetime object
154        giving a datetime in UTC timezone.
155
156        Raises ObjectModifiedEvent.
157        """
158        if payment_date is None:
159            payment_date = datetime.utcnow()
160        self.payment_date = payment_date
161        self.state = STATE_PAID
162        notify(grok.ObjectModifiedEvent(self))
163
164    def mark_failed(self, reason=None):
165        """Mark payment as failed.
166
167        Raises ObjectModifiedEvent.
168        """
169        self.state = STATE_FAILED
170        notify(grok.ObjectModifiedEvent(self))
171
172
173@attrs_to_fields
174class Payer(object):
175    """A Payment is for testing.
176
177    It cannot be stored in ZODB.
178    """
179    grok.implements(IPayer)
180
181
182@attrs_to_fields
183class PaymentItem(object):
184
185    grok.implements(IPaymentItem)
186
187    def __init__(
188            self, item_id=u"0", title=u"", amount=decimal.Decimal("0.00")):
189        super(PaymentItem, self).__init__()
190        self.item_id = item_id
191        self.title = title
192        self.amount = amount
193
194
195@attrs_to_fields
196class Payee(object):
197    """Someone being paid.
198
199    This is for testing only and cannot be stored in ZODB.
200    """
201    grok.implements(IPayee)
Note: See TracBrowser for help on using the repository browser.