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

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

Add helper function get_payments_from_payer_id.

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