source: main/waeup.ikoba/trunk/src/waeup/ikoba/payments/payment.py @ 12789

Last change on this file since 12789 was 12788, checked in by uli, 10 years ago

pep8, fix doc string.

  • Property svn:keywords set to Id
File size: 8.0 KB
Line 
1## $Id: payment.py 12788 2015-03-18 12:45:09Z 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.payments.interfaces import (
31    IPayment, STATE_UNPAID, STATE_FAILED, STATE_PAID,
32    IPaymentGatewayService, IPayer, IPaymentItem, IPayee,
33    IPaymentGatewayServicesLister, IPayableFinder, IPayerFinder,
34    IPaymentWaitingForGatewayEvent, IPaymentFinishedEvent,
35    )
36
37
38def format_amount(amount, currency):
39    """Turn `amount`, `currency` into a readable string.
40    """
41    cncy_map = {'USD': u'US$', 'EUR': u'\u20ac', 'NGN': u'\u20a6'}
42    currency = cncy_map.get(currency, currency)
43    return '%s %s' % (currency, '{:,.2f}'.format(amount))
44
45
46def get_payment(payment_id):
47    """Get payment by payment id.
48
49    If no such payment can be found in catalog, return none.
50    """
51    cat = getUtility(ICatalog, name='payments_catalog')
52    result_set = [x for x in cat.searchResults(
53        payment_id=(payment_id, payment_id))]
54    if len(result_set):
55        return result_set[0]
56    return None
57
58
59def get_payments_from_payer_id(payer_id):
60    """Get payments by payer id.
61
62    If no such payment can be found in catalog, return none.
63    """
64    cat = getUtility(ICatalog, name='payments_catalog')
65    result_set = [x for x in cat.searchResults(
66        payer_id=(payer_id, payer_id))]
67    if len(result_set):
68        return result_set
69    return None
70
71
72def get_payments_from_payable_id(payable_id):
73    """Get payments by payer id.
74
75    If no such payment can be found in catalog, return none.
76    """
77    cat = getUtility(ICatalog, name='payments_catalog')
78    result_set = [x for x in cat.searchResults(
79        payable_id=(payable_id, payable_id))]
80    if len(result_set):
81        return result_set
82    return None
83
84
85def find_payable_from_payable_id(payable_id):
86    """Find a payable from its id.
87
88    Looks up all registered IPayableFinders and returns the first
89    positive result found.
90    """
91    for name, util in getUtilitiesFor(IPayableFinder):
92        result = util.get_payable_by_id(payable_id)
93        if result is not None:
94            return result
95    return None
96
97
98def find_payer_from_payer_id(payer_id):
99    """Find a payer from its id.
100
101    Looks up all registered IPayerFinders and returns the first
102    positive result found.
103    """
104    for name, util in getUtilitiesFor(IPayerFinder):
105        result = util.get_payer_by_id(payer_id)
106        if result is not None:
107            return result
108    return None
109
110
111def format_payment_item_values(payment_item_values, currency):
112    """Format tuples (description, currency, amount) for output.
113
114    `currency` passed in is the 'target' currency.
115
116    Returns a list of formatted values. Last item is total sum.
117    XXX: we do not really respect currency. If different items
118         have different currencies, we are choked.
119    """
120    result = []
121    total = decimal.Decimal("0.00")
122    for descr, item_currency, amount in payment_item_values:
123        total += amount
124        if item_currency != currency:
125            raise ValueError(
126                "Different currencies in payment items not supported.")
127        result.append((descr, '%s %0.2f' % (item_currency, amount)))
128    result.append((_('Total'), '%s %0.2f' % (currency, total)))
129    return result
130
131
132def get_payment_providers():
133    """Get all payment providers registered.
134    """
135    return dict(
136        getUtilitiesFor(IPaymentGatewayService)
137    )
138
139
140class PaymentWaitingForGatewayEvent(object):
141    grok.implements(IPaymentWaitingForGatewayEvent)
142
143    def __init__(self, obj):
144        self.object = obj
145
146
147class PaymentFinishedEvent(object):
148    grok.implements(IPaymentFinishedEvent)
149
150    def __init__(self, obj):
151        self.object = obj
152
153
154class PaymentGatewayServicesLister(grok.GlobalUtility):
155    grok.implements(IPaymentGatewayServicesLister)
156
157    def __call__(self):
158        """Get all services of payment gateways registered.
159        """
160        return get_payment_providers()
161
162
163class PaymentProviderServiceBase(grok.GlobalUtility):
164    """Base for IPaymentGatewayServices.
165    """
166    grok.baseclass()
167    grok.implements(IPaymentGatewayService)
168
169    title = u'Sample Credit Card Service'
170
171    def store(self, payment):
172        """Store `payment` in site.
173        """
174        site = grok.getSite()
175        payments = site['payments']
176        if payment.payment_id in payments:
177            del site['payments'][payment.payment_id]
178        site['payments'][payment.payment_id] = payment
179
180
181class Payment(grok.Model):
182    """This is a payment.
183    """
184    grok.implements(IPayment)
185    grok.provides(IPayment)
186
187    @property
188    def amount(self):
189        """Total amount.
190
191        Sum of all contained payment items.
192        """
193        amounts = [item.amount for item in self.payment_items]
194        return sum(amounts, decimal.Decimal("0.00"))
195
196    def __init__(self, payer, payable, payee=None):
197        super(Payment, self).__init__()
198        items = tuple([x for x in payable.payment_items])
199        self.payment_items = items
200        self.payer_id = payer.payer_id
201        self.payable_id = payable.payable_id
202        self.title = payable.title
203        self.creation_date = datetime.utcnow()
204        self.payment_date = None
205        self.payment_id = u'PAY_' + unicode(uuid.uuid4().hex)
206        self.state = STATE_UNPAID
207        self.currency = payable.currency
208        if payee is not None:
209            self.payee_id = payee.payee_id
210        return
211
212    def approve(self, payment_date=None):
213        """A payment was approved.
214
215        Successful ending; the payment is marked as payed.
216
217        If `payment_date` is given, it must be a datetime object
218        giving a datetime in UTC timezone.
219
220        Raises ObjectModifiedEvent.
221        """
222        if payment_date is None:
223            payment_date = datetime.utcnow()
224        self.payment_date = payment_date
225        self.state = STATE_PAID
226        notify(grok.ObjectModifiedEvent(self))
227
228    def mark_failed(self, reason=None):
229        """Mark payment as failed.
230
231        Raises ObjectModifiedEvent.
232        """
233        self.state = STATE_FAILED
234        notify(grok.ObjectModifiedEvent(self))
235
236Payment = attrs_to_fields(Payment, omit="amount")
237
238
239@attrs_to_fields
240class Payer(object):
241    """A Payer for testing.
242
243    It cannot be stored in ZODB.
244    """
245    grok.implements(IPayer)
246
247
248@attrs_to_fields
249class PaymentItem(object):
250
251    grok.implements(IPaymentItem)
252
253    def __init__(
254            self, item_id=u"0", title=u"", amount=decimal.Decimal("0.00")):
255        super(PaymentItem, self).__init__()
256        self.item_id = item_id
257        self.title = title
258        self.amount = amount
259
260    def to_string(self):
261        """A string representation that can be used in exports.
262
263        Returned is a unicode string of format
264        ``(u'<ITEM_ID>', u'<TITLE>', u'<AMOUNT>')``.
265        """
266        string = u"(u'%s', u'%s', u'%s')" % (
267            self.item_id, self.title, self.amount)
268        string = string.replace("u'None'", "None")
269        return string
270
271
272@attrs_to_fields
273class Payee(object):
274    """Someone being paid.
275
276    This is for testing only and cannot be stored in ZODB.
277    """
278    grok.implements(IPayee)
Note: See TracBrowser for help on using the repository browser.