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

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

Store payment items in payments.

  • Property svn:keywords set to Id
File size: 7.6 KB
Line 
1## $Id: payment.py 12774 2015-03-16 12:27:21Z 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
181@attrs_to_fields
182class Payment(grok.Model):
183    """This is a payment.
184    """
185    grok.implements(IPayment)
186    grok.provides(IPayment)
187
188    @property
189    def amount(self):
190        """Total amount.
191
192        Sum of all contained payment items.
193        """
194        amounts = [item.amount for item in self.payment_items]
195        return sum(amounts, decimal.Decimal("0.00"))
196
197    def __init__(self, payer, payable, payee=None):
198        super(Payment, self).__init__()
199        items = tuple([x for x in payable.payment_items])
200        self.payment_items = items
201        self.payer_id = payer.payer_id
202        self.payable_id = payable.payable_id
203        self.title = payable.title
204        self.creation_date = datetime.utcnow()
205        self.payment_date = None
206        self.payment_id = u'PAY_' + unicode(uuid.uuid4().hex)
207        self.state = STATE_UNPAID
208        self.currency = payable.currency
209        if payee is not None:
210            self.payee_id = payee.payee_id
211        return
212
213    def approve(self, payment_date=None):
214        """A payment was approved.
215
216        Successful ending; the payment is marked as payed.
217
218        If `payment_date` is given, it must be a datetime object
219        giving a datetime in UTC timezone.
220
221        Raises ObjectModifiedEvent.
222        """
223        if payment_date is None:
224            payment_date = datetime.utcnow()
225        self.payment_date = payment_date
226        self.state = STATE_PAID
227        notify(grok.ObjectModifiedEvent(self))
228
229    def mark_failed(self, reason=None):
230        """Mark payment as failed.
231
232        Raises ObjectModifiedEvent.
233        """
234        self.state = STATE_FAILED
235        notify(grok.ObjectModifiedEvent(self))
236
237
238@attrs_to_fields
239class Payer(object):
240    """A Payer for testing.
241
242    It cannot be stored in ZODB.
243    """
244    grok.implements(IPayer)
245
246
247@attrs_to_fields
248class PaymentItem(object):
249
250    grok.implements(IPaymentItem)
251
252    def __init__(
253            self, item_id=u"0", title=u"", amount=decimal.Decimal("0.00")):
254        super(PaymentItem, self).__init__()
255        self.item_id = item_id
256        self.title = title
257        self.amount = amount
258
259
260@attrs_to_fields
261class Payee(object):
262    """Someone being paid.
263
264    This is for testing only and cannot be stored in ZODB.
265    """
266    grok.implements(IPayee)
Note: See TracBrowser for help on using the repository browser.