source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/contrib/paymentech.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 11.4 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# This module provides a simple API for Paymentech(c) payments
5# The original code was taken from this web2py issue post
6# http://code.google.com/p/web2py/issues/detail?id=1170 by Adnan Smajlovic
7#
8#    Copyright (C) <2012>  Alan Etkin <spametki@gmail.com>
9#    License: BSD
10#
11
12import sys, httplib, urllib, urllib2
13from xml.dom.minidom import parseString
14
15# TODO: input validation, test, debugging output
16
17class PaymenTech(object):
18    """
19    The base class for connecting to the Paymentech service
20
21    Format notes
22    ============
23
24    - Credit card expiration date (exp argument) must be of mmyyyy form
25    - The amount is an all integers string with two decimal places:
26      For example, $2.15 must be formatted as "215"
27
28    Point of sale and service options (to be passed on initialization)
29    ==================================================================
30
31    user
32    password
33    industry
34    message
35    bin_code
36    merchant
37    terminal
38
39    (WARNING!: this is False by default)
40    development <bool>
41
42    (the following arguments have default values)
43    target
44    host
45    api_url
46
47
48    Testing
49    =======
50
51    As this module consumes webservice methods, it should be tested
52    with particular user data with the paymentech development environment
53
54    The simplest test would be running something like the following:
55
56    from paymentech import PaymenTech
57    # Read the basic point of sale argument list required above
58    # Remember to use development = True!
59    pos_data = {'user': <username>, ...}
60
61    # The data arguments are documented in the .charge() method help
62    charge_test = {'account': <account>, ...}
63    mypayment = PaymentTech(**pos_data)
64    result = mypayment.charge(**charge_test)
65
66    print "##################################"
67    print "#       Charge test result       #"
68    print "##################################"
69    print result
70
71    #################################################################
72    #               Notes for web2py implementations                #
73    #################################################################
74
75    # A recommended model for handling payments
76
77    # Store this constants in a private model file (i.e. 0_private.py)
78
79    PAYMENTECH_USER = <str>
80    PAYMENTECH_PASSWORD = <str>
81    PAYMENTECH_INDUSTRY = <str>
82    PAYMENTECH_MESSAGE = <str>
83    PAYMENTECH_BIN_CODE= <str>
84    PAYMENTECH_MERCHANT = <str>
85    PAYMENTECH_terminal = <str>
86    DEVELOPMENT = True
87    PAYMENTECH_TARGET = <str>
88    PAYMENTECH_HOST = <str>
89    PAYMENTECH_API_URL = <str>
90
91    # The following table would allow passing data with web2py and to
92    # update records with the webservice authorization output by using
93    # the DAL
94    #
95    #   For example:
96    #
97    #   # Create a PaymenTech instance
98    #   mypaymentech = paymentech.PaymenTech(user=PAYMENTECH_USER, ...)
99    #
100    #   # Fetch a payment inserted within the app
101    #   myrow = db.paymentech[<id>]
102    #
103    #   # Send the authorization request to the webservice
104    #   result = mypaymentech.charge(myrow.as_dict())
105    #
106    #   # Update the db record with the webservice response
107    #   myrow.update_record(**result)
108
109    db.define_table("paymentech",
110        Field("account"),
111        Field("exp", comment="Must be of the mmyyyy form"),
112        Field("currency_code"),
113        Field("currency_exponent"),
114        Field("card_sec_val_ind"),
115        Field("card_sec_val"),
116        Field("avs_zip"),
117        Field("avs_address_1"),
118        Field("avs_address_2"),
119        Field("avs_city"),
120        Field("avs_state"),
121        Field("avs_phone"),
122        Field("avs_country"),
123        Field("profile_from_order_ind"),
124        Field("profile_order_override_ind"),
125        Field("order_id"),
126        Field("amount",
127              comment="all integers with two decimal digits, \
128                                 without dot separation"),
129        Field("header"),
130        Field("status_code"),
131        Field("status_message"),
132        Field("resp_code"),
133        Field("tx_ref_num"),
134        format="%(order_id)s")
135
136    TODO: add model form validators (for exp date and amount)
137    """
138
139    charge_xml = """
140        <?xml version="1.0" encoding="UTF-8"?>
141        <Request>
142            <NewOrder>
143                <OrbitalConnectionUsername>%(user)s</OrbitalConnectionUsername>
144                <OrbitalConnectionPassword>%(password)s</OrbitalConnectionPassword>
145                <IndustryType>%(industry)s</IndustryType>
146                <MessageType>%(message)s</MessageType>
147                <BIN>%(bin)s</BIN>
148                <MerchantID>%(merchant)s</MerchantID>
149                <TerminalID>%(terminal)s</TerminalID>
150                <AccountNum>%(account)s</AccountNum>
151                <Exp>%(exp)s</Exp>
152                <CurrencyCode>%(currency_code)s</CurrencyCode>
153                <CurrencyExponent>%(currency_exponent)s</CurrencyExponent>
154                <CardSecValInd>%(card_sec_val_ind)s</CardSecValInd>
155                <CardSecVal>%(card_sec_val)s</CardSecVal>
156                <AVSzip>%(avs_zip)s</AVSzip>
157                <AVSaddress1>%(avs_address_1)s</AVSaddress1>
158                <AVSaddress2>%(avs_address_2)s</AVSaddress2>
159                <AVScity>%(avs_city)s</AVScity>
160                <AVSstate>%(avs_state)s</AVSstate>
161                <AVSphoneNum>%(avs_phone)s</AVSphoneNum>
162                <AVScountryCode>%(avs_country)s</AVScountryCode>
163                <CustomerProfileFromOrderInd>%(profile_from_order_ind)s</CustomerProfileFromOrderInd>
164                <CustomerProfileOrderOverrideInd>%(profile_order_override_ind)s</CustomerProfileOrderOverrideInd>
165                <OrderID>%(order_id)s</OrderID>
166                <Amount>%(amount)s</Amount>
167            </NewOrder>
168        </Request>
169    """
170
171    def __init__(self, development=False, user=None, password=None,
172               industry=None, message=None, api_url=None,
173               bin_code=None, merchant=None, host=None,
174               terminal=None, target=None):
175
176        # PaymenTech point of sales data
177        self.user = user
178        self.password = password
179        self.industry = industry
180        self.message = message
181        self.bin_code = bin_code
182        self.merchant = merchant
183        self.terminal = terminal
184
185        # Service options
186        self.development = development
187        self.target = target
188        self.host = host
189        self.api_url = api_url
190
191        # dev: https://orbitalvar1.paymentech.net/authorize:443
192        # prod: https://orbital1.paymentech.net/authorize
193
194        if self.development is False:
195            if not self.target:
196                # production
197                self.target = "https://orbital1.paymentech.net/authorize"
198
199            self.host, self.api_url = \
200                urllib2.splithost(urllib2.splittype(self.target)[1])
201
202        else:
203            if not self.target:
204                # development
205                self.target = "https://orbitalvar1.paymentech.net/authorize"
206            if not self.host:
207                self.host = "orbitalvar1.paymentech.net/authorize:443"
208            if not self.api_url:
209                self.api_url = "/"
210
211    def charge(self, raw=None, **kwargs):
212        """
213        Post an XML request to Paymentech
214        This is an example of a call with raw xml data:
215
216             from paymentech import PaymenTech
217
218             # Note: user/password/etc data is not mandatory as it
219             # is retrieved from instance attributes (set on init)
220
221             pt = PaymenTech(user="<myuser>",
222                             password="<mypassword>",
223                             ...) # see basic user in the class help
224             result = pt.charge(raw=xml_string)
225
226        A better way to make a charge request is to unpack a dict object
227        with the operation data:
228
229             ...
230             # The complete input values are listed below in
231             # "Transacion data..."
232
233             charge_data = dict(account=<str>, exp=<str mmyyyy>, ...)
234             result = pt.charge(**charge_data)
235
236
237        Variable xml_string contains all details about the order,
238        plus we are sending username/password in there too...
239
240
241        Transaction data (to be passed to the charge() method)
242        ======================================================
243
244        (Note that it is possible to override the class user,
245        pass, etc. passing those arguments to the .charge() method,
246        which are documented in the class help)
247
248        account
249        exp <str mmyyyy>
250        currency_code
251        currency_exponent
252        card_sec_val_ind
253        card_sec_val
254        avs_zip
255        avs_address_1
256        avs_address_2
257        avs_city
258        avs_state
259        avs_phone
260        avs_country
261        profile_from_order_ind
262        profile_order_override_ind
263        order_id
264        amount <str> (all integers with two decimal digits, without dot
265                     separation)
266
267        Request header example
268        ======================
269
270        Request: sent as POST to https://orbitalvar1.paymentech.net/authorize:443
271        from 127.0.0.1
272        request headers:
273        Content-Type: application/PTI45
274        Content-Type: application/PTI46
275        Content-transfer-encoding: text
276        Request-number: 1
277        Document-type: Request
278        Trace-number: 1234556446
279        <?xml version="1.0" encoding="UTF-8"?>
280        """
281
282        # default charge data
283        data = dict(user=self.user, password=self.password,
284                    industry=self.industry, message=self.message,
285                    bin_code=self.bin_code, merchant=self.merchant,
286                    terminal=self.terminal, account="", exp="",
287                    currency_code="", currency_exponent="",
288                    card_sec_val_ind="", card_sec_val="", avs_zip="",
289                    avs_address_1="", avs_address_2="", avs_city="",
290                    avs_state="", avs_phone="", avs_country="",
291                    profile_from_order_ind="",
292                    profile_order_override_ind="", order_id="",
293                    amount="")
294
295        result = dict()
296
297        # Complete the charge request with the method kwargs
298        for k, v in kwargs.iteritems():
299            data[k] = v
300
301        status_code = status_message = header = resp_code = \
302        tx_ref_num = order_id = None
303        conn = httplib.HTTPS(self.host)
304        conn.putrequest('POST', self.api_url)
305
306        if self.development:
307            content_type = "PTI56"
308        else:
309            content_type = "PTI46"
310
311        if raw is None:
312            xml_string = self.charge_xml % data
313        else:
314            xml_string = raw
315
316        conn.putheader("Content-Type",
317                       "application/%s") % content_type
318        conn.putheader("Content-transfer-encoding", "text")
319        conn.putheader("Request-number", "1")
320        conn.putheader("Content-length", str(len(xml_string)))
321        conn.putheader("Document-type", "Request")
322        conn.putheader("Trace-number", str(data["order_id"]))
323        conn.putheader("MIME-Version", "1.0")
324        conn.endheaders()
325        conn.send(xml_string)
326
327        result["status_code"], result["status_message"], \
328        result["header"] = conn.getreply()
329
330        fp = conn.getfile()
331        output = fp.read()
332        fp.close()
333
334        dom = parseString(output)
335        result["resp_code"] = \
336            dom.getElementsByTagName('RespCode')[0].firstChild.data
337        result["tx_ref_num"] = \
338            dom.getElementsByTagName('TxRefNum')[0].firstChild.data
339        result["order_id"] = \
340            dom.getElementsByTagName('CustomerRefNum')[0].firstChild.data
341
342        return result
Note: See TracBrowser for help on using the repository browser.