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

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

Prepare contract payment receipt.

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