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

Last change on this file since 12770 was 12766, checked in by Henrik Bettermann, 10 years ago

Add browser tests and set up logger properly.

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