')
53 | def remove_from_cart(item_id):
54 | cart = Cart(session['cart'])
55 | if cart.change_item(item_id, 'remove'):
56 | session['cart'] = cart.to_dict()
57 | return list_products()
58 |
59 |
60 | @main.route('/notification')
61 | def notification_view(request):
62 | notification_code = request.POST['notificationCode']
63 | pg = PagSeguro(email=app.config['EMAIL'], token=app.config['TOKEN'])
64 | pg.check_notification(notification_code)
65 | # use the return of the function above to update the order
66 |
67 |
68 | @main.route('/checkout', methods=['GET'])
69 | def checkout_get():
70 | return render_template('checkout.jinja2')
71 |
72 |
73 | @main.route('/checkout', methods=['POST'])
74 | def checkout_post():
75 | for field in ['name', 'email', 'street', 'number', 'complement',
76 | 'district', 'postal_code', 'city', 'state']:
77 | if not request.form.get(field, False):
78 | return jsonify({'error_msg': 'Todos os campos são obrigatórios.'})
79 | cart = Cart(session['cart'])
80 | if len(cart.items) == 0:
81 | return jsonify({'error_msg': 'Seu carrinho está vazio.'})
82 | sender = {
83 | "name": request.form.get("name"),
84 | "email": request.form.get("email"),
85 | }
86 | shipping = {
87 | "street": request.form.get("street"),
88 | "number": request.form.get("number"),
89 | "complement": request.form.get("complement"),
90 | "district": request.form.get("district"),
91 | "postal_code": request.form.get("postal_code"),
92 | "city": request.form.get("city"),
93 | "state": request.form.get("state"),
94 | "country": 'BRA'
95 | }
96 | pagseguro = checkout_pg(sender, shipping, cart)
97 | response = pagseguro.checkout()
98 | return redirect(response.payment_url)
99 |
100 |
101 | def checkout_pg(sender, shipping, cart):
102 | pagseguro = PagSeguro(email=app.config['EMAIL'], token=app.config['TOKEN'])
103 | pagseguro.sender = sender
104 | shipping['type'] = pagseguro.SEDEX
105 | pagseguro.shipping = shipping
106 | pagseguro.extra_amount = "%.2f" % float(app.config['EXTRA_AMOUNT'])
107 | pagseguro.redirect_url = app.config['REDIRECT_URL']
108 | pagseguro.notification_url = app.config['NOTIFICATION_URL']
109 | pagseguro.items = cart.items
110 | for item in cart.items:
111 | item['amount'] = "%.2f" % float(app.config['EXTRA_AMOUNT'])
112 | return pagseguro
113 |
--------------------------------------------------------------------------------
/examples/flask/flask_seguro/controllers/main/views.py:
--------------------------------------------------------------------------------
1 | from flask import Blueprint
2 |
3 | main = Blueprint('main', __name__)
4 |
5 |
6 | from . import views # noqa
7 |
--------------------------------------------------------------------------------
/examples/flask/flask_seguro/products.py:
--------------------------------------------------------------------------------
1 | class Products:
2 | def __init__(self):
3 | self.products = [
4 | {
5 | "id": "0001",
6 | "description": "Produto 1",
7 | "amount": 1.00,
8 | "quantity": 1,
9 | "weight": 200,
10 | "price": 10.10
11 | },
12 | {
13 | "id": "0002",
14 | "description": "Produto 2",
15 | "amount": 50,
16 | "quantity": 1,
17 | "weight": 1000,
18 | "price": 10.50
19 | },
20 | ]
21 |
22 | def get_all(self):
23 | return self.products
24 |
25 | def get_one(self, item_id):
26 | p = [p for p in self.products if p['id'] == item_id]
27 | if len(p) > 0:
28 | return p[0]
29 | else:
30 | return False
31 |
--------------------------------------------------------------------------------
/examples/flask/flask_seguro/static/css/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | }
4 | .products{
5 | margin-top: 100px !important;
6 | }
--------------------------------------------------------------------------------
/examples/flask/flask_seguro/templates/base.jinja2:
--------------------------------------------------------------------------------
1 | {% extends 'bootstrap/base.html' %}
2 |
3 | {% block styles %}
4 | {{super()}}
5 |
6 | {% endblock %}
7 |
8 | {% block title %}{% endblock %}
9 |
10 | {% block navbar %}
11 | {% include 'menu.jinja2' %}
12 | {% endblock %}
13 |
14 | {% block content %}
15 |
16 |
17 |
{{ self.title() }}
18 | {% block subcontent %}{% endblock %}
19 |
20 |
21 | {% endblock %}
--------------------------------------------------------------------------------
/examples/flask/flask_seguro/templates/cart.jinja2:
--------------------------------------------------------------------------------
1 | {% extends 'base.jinja2' %}
2 |
3 | {% block title %}Carrinho{% endblock %}
4 |
5 | {% block subcontent %}
6 |
7 |
8 |
9 | Descrição |
10 | Valor |
11 |
12 |
13 |
14 | {% for item in cart['items'] %}
15 |
16 | {{item['description']}} |
17 | R${{'%0.2f'| format(item['price']|float)}} |
18 |
19 | {% endfor %}
20 |
21 |
22 | Subtotal: R${{'%0.2f'| format(cart.subtotal|float)}}
23 | Taxa Extra: R${{'%0.2f'| format(cart.extra_amount|float)}}
24 | Total: R${{'%0.2f'| format(cart.total|float)}}
25 | {% endblock %}
26 |
--------------------------------------------------------------------------------
/examples/flask/flask_seguro/templates/checkout.jinja2:
--------------------------------------------------------------------------------
1 | {% extends 'base.jinja2' %}
2 |
3 | {% block title %}Pagamento{% endblock %}
4 |
5 | {% block subcontent %}
6 |
45 | {% endblock %}
--------------------------------------------------------------------------------
/examples/flask/flask_seguro/templates/menu.jinja2:
--------------------------------------------------------------------------------
1 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/flask/flask_seguro/templates/products.jinja2:
--------------------------------------------------------------------------------
1 | {% extends 'base.jinja2' %}
2 | {% block title %}Produtos{% endblock %}
3 |
4 | {% block subcontent %}
5 | {% for product in products %}
6 |
21 | {% endfor %}
22 | {% endblock%}
23 |
--------------------------------------------------------------------------------
/examples/flask/requirements.txt:
--------------------------------------------------------------------------------
1 | arrow==0.12.1
2 | astroid==2.0.1
3 | atomicwrites==1.1.5
4 | attrs==18.1.0
5 | certifi==2018.4.16
6 | chardet==3.0.4
7 | click==6.7
8 | coverage==4.5.1
9 | dominate==2.3.1
10 | Flask==1.0.2
11 | Flask-Bootstrap==3.3.7.1
12 | idna==2.7
13 | isort==4.3.4
14 | itsdangerous==0.24
15 | Jinja2==2.10
16 | lazy-object-proxy==1.3.1
17 | MarkupSafe==1.0
18 | mccabe==0.6.1
19 | more-itertools==4.2.0
20 | pagseguro==0.3.2
21 | pluggy==0.6.0
22 | py==1.5.4
23 | pylint==2.0.0
24 | pytest==3.6.3
25 | pytest-cov==2.5.1
26 | pytest-sugar==0.9.1
27 | python-dateutil==2.7.3
28 | requests==2.19.1
29 | six==1.11.0
30 | termcolor==1.1.0
31 | urllib3==1.23
32 | visitor==0.1.3
33 | Werkzeug==0.14.1
34 | wrapt==1.10.11
35 | xmltodict==0.11.0
36 |
--------------------------------------------------------------------------------
/examples/flask/run.py:
--------------------------------------------------------------------------------
1 | from flask_seguro import create_app
2 |
3 | app = create_app('development')
4 |
5 |
6 | @app.shell_context_processor
7 | def make_shell_context():
8 | return dict(app=app)
9 |
--------------------------------------------------------------------------------
/examples/flask/screenshots/screen1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Japle/python-pagseguro/08a8aa7f934b16d00948ead17a0e470a88f2479f/examples/flask/screenshots/screen1.png
--------------------------------------------------------------------------------
/examples/flask/screenshots/screen2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Japle/python-pagseguro/08a8aa7f934b16d00948ead17a0e470a88f2479f/examples/flask/screenshots/screen2.png
--------------------------------------------------------------------------------
/examples/flask/screenshots/screen3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Japle/python-pagseguro/08a8aa7f934b16d00948ead17a0e470a88f2479f/examples/flask/screenshots/screen3.png
--------------------------------------------------------------------------------
/examples/flask/tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import os
4 | import unittest
5 | import tempfile
6 | import flask
7 | from flask import json
8 |
9 | from flask_seguro.products import Products
10 | from flask_seguro import create_app
11 |
12 |
13 | class FlasKSeguroTestCase(unittest.TestCase):
14 | def setUp(self):
15 | self._current_app = create_app('development')
16 |
17 | self.db_fd, self._current_app.config['DATABASE'] = tempfile.mkstemp()
18 | self._current_app.config['TESTING'] = True
19 | self.app = self._current_app.test_client()
20 | # flaskr.init_db()
21 |
22 | def tearDown(self):
23 | os.close(self.db_fd)
24 | os.unlink(self._current_app.config['DATABASE'])
25 |
26 | def list_products(self):
27 | return Products().get_all()
28 |
29 | def check_cart_fields(self, response):
30 | cart = flask.session['cart']
31 | self.assertNotIn('error_msg', cart)
32 | self.assertIn('total', cart)
33 | self.assertIn('subtotal', cart)
34 | return cart
35 |
36 | def test_retrieve_cart_and_add_remove_item(self):
37 | with self._current_app.test_client() as c:
38 | response = c.get('/')
39 | session = flask.session
40 | self.assertIn('cart', session)
41 | self.assertEquals(0, len(session['cart']['items']))
42 |
43 | products = self.list_products()
44 |
45 | response = c.get('/cart/add/%s' % (products[0]['id']))
46 | self.assertEquals(1, len(session['cart']['items']))
47 | cart = self.check_cart_fields(response)
48 | self.assertEquals(
49 | float(cart['subtotal']), float(products[0]['price']))
50 |
51 | response = c.get('/cart/remove/%s' % (products[0]['id']))
52 | self.assertEquals(0, len(session['cart']['items']))
53 | cart = self.check_cart_fields(response)
54 | self.assertEquals(0, float(cart['total']))
55 | self.assertEquals(float(cart['total']), float(cart['subtotal']))
56 |
57 | def checkout(self, data, c, decode_json=True):
58 | response = c.post('/checkout', data=data)
59 | if decode_json:
60 | response = json.loads(response.data)
61 | return response
62 |
63 | def test_checkout(self):
64 | with self._current_app.test_client() as c:
65 | response = c.get('/')
66 | session = flask.session
67 |
68 | self.assertEquals(0, len(session['cart']['items']))
69 |
70 | data = {
71 | "name": "Victor Shyba",
72 | "email": "teste@example.com",
73 | "street": "Av Brig Faria Lima",
74 | "number": 1234,
75 | "complement": "5 andar",
76 | "district": "Jardim Paulistano",
77 | "postal_code": "06650030",
78 | "city": "Sao Paulo",
79 | "state": "SP"
80 | }
81 |
82 | response = self.checkout(data, c)
83 | self.assertIn('error_msg', response)
84 | self.assertEquals(
85 | u'Seu carrinho está vazio.', response['error_msg'])
86 |
87 | response = self.checkout({}, c)
88 | self.assertIn('error_msg', response)
89 | self.assertEquals(
90 | u'Todos os campos são obrigatórios.', response['error_msg'])
91 |
92 | products = self.list_products()
93 | response = c.get('/cart/add/%s' % (products[0]['id']))
94 | self.assertEquals(1, len(session['cart']['items']))
95 |
96 | response = self.checkout(data, c, decode_json=False)
97 | self.assertEquals(302, response.status_code)
98 | self.assertIn('pagseguro', response.location)
99 |
100 | def test_cart_view(self):
101 | response = self.app.get('/cart')
102 | self.assertEquals(200, response.status_code)
103 |
104 |
105 | if __name__ == '__main__':
106 | unittest.main()
107 |
--------------------------------------------------------------------------------
/pagseguro/__init__.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | import logging
3 | import requests
4 |
5 | from .config import Config
6 | from .utils import is_valid_email, is_valid_cpf, is_valid_cnpj
7 | from .parsers import (PagSeguroNotificationResponse,
8 | PagSeguroPreApprovalNotificationResponse,
9 | PagSeguroPreApprovalCancel,
10 | PagSeguroCheckoutSession,
11 | PagSeguroPreApprovalPayment,
12 | PagSeguroCheckoutResponse,
13 | PagSeguroTransactionSearchResult,
14 | PagSeguroPreApproval,
15 | PagSeguroPreApprovalSearch)
16 |
17 | logger = logging.getLogger()
18 |
19 |
20 | class PagSeguro(object):
21 | """ Pag Seguro V2 wrapper """
22 |
23 | PAC = 1
24 | SEDEX = 2
25 | NONE = 3
26 |
27 | def __init__(self, email, token, data=None, config=None):
28 |
29 | config = config or {}
30 | if not type(config) == dict:
31 | raise Exception('Malformed config dict param')
32 |
33 | self.config = Config(**config)
34 |
35 | self.data = {}
36 | self.data['email'] = email
37 | self.data['token'] = token
38 |
39 | if data and isinstance(data, dict):
40 | self.data.update(data)
41 |
42 | self.items = []
43 | self.sender = {}
44 | self.shipping = {}
45 | self._reference = ""
46 | self.extra_amount = None
47 | self.redirect_url = None
48 | self.notification_url = None
49 | self.abandon_url = None
50 | self.credit_card = {}
51 | self.pre_approval = {}
52 | self.checkout_session = None
53 | self.payment = {}
54 |
55 | def build_checkout_params(self, **kwargs):
56 | """ build a dict with params """
57 | params = kwargs or {}
58 | if self.sender:
59 | params['senderName'] = self.sender.get('name')
60 | params['senderAreaCode'] = self.sender.get('area_code')
61 | params['senderPhone'] = self.sender.get('phone')
62 | params['senderEmail'] = is_valid_email(self.sender.get('email'))
63 | params['senderCPF'] = is_valid_cpf(self.sender.get('cpf'))
64 | params['senderCNPJ'] = is_valid_cnpj(self.sender.get('cnpj'))
65 | params['senderBornDate'] = self.sender.get('born_date')
66 | params['senderHash'] = self.sender.get('hash')
67 |
68 | if self.config.USE_SHIPPING:
69 | if self.shipping:
70 | params['shippingType'] = self.shipping.get('type')
71 | params['shippingAddressStreet'] = self.shipping.get('street')
72 | params['shippingAddressNumber'] = self.shipping.get('number')
73 | params['shippingAddressComplement'] = self.shipping.get(
74 | 'complement')
75 | params['shippingAddressDistrict'] = self.shipping.get(
76 | 'district')
77 | params['shippingAddressPostalCode'] = self.shipping.get(
78 | 'postal_code')
79 | params['shippingAddressCity'] = self.shipping.get('city')
80 | params['shippingAddressState'] = self.shipping.get('state')
81 | params['shippingAddressCountry'] = self.shipping.get('country',
82 | 'BRA')
83 | if self.shipping.get('cost'):
84 | params['shippingCost'] = self.shipping.get('cost')
85 | else:
86 | params['shippingAddressRequired'] = 'false'
87 |
88 | if self.extra_amount:
89 | params['extraAmount'] = self.extra_amount
90 |
91 | params['reference'] = self.reference
92 | params['receiverEmail'] = self.data['email']
93 |
94 | if self.redirect_url:
95 | params['redirectURL'] = self.redirect_url
96 |
97 | if self.notification_url:
98 | params['notificationURL'] = self.notification_url
99 |
100 | if self.abandon_url:
101 | params['abandonURL'] = self.abandon_url
102 |
103 | for i, item in enumerate(self.items, 1):
104 | params['itemId%s' % i] = item.get('id')
105 | params['itemDescription%s' % i] = item.get('description')
106 | params['itemAmount%s' % i] = item.get('amount')
107 | params['itemQuantity%s' % i] = item.get('quantity')
108 | params['itemWeight%s' % i] = item.get('weight')
109 | params['itemShippingCost%s' % i] = item.get('shipping_cost')
110 |
111 | if self.payment:
112 |
113 | params['paymentMethod'] = self.payment.get('method')
114 | params['paymentMode'] = self.payment.get('mode')
115 |
116 | if self.credit_card:
117 | params['billingAddressCountry'] = 'BRA'
118 |
119 | credit_card_keys_map = [
120 | ('creditCardToken', 'credit_card_token'),
121 | ('installmentQuantity', 'installment_quantity'),
122 | ('installmentValue', 'installment_value'),
123 | ('noInterestInstallmentQuantity',
124 | 'no_interest_installment_quantity'),
125 | ('creditCardHolderName', 'card_holder_name'),
126 | ('creditCardHolderCPF', 'card_holder_cpf'),
127 | ('creditCardHolderBirthDate', 'card_holder_birth_date'),
128 | ('creditCardHolderAreaCode', 'card_holder_area_code'),
129 | ('creditCardHolderPhone', 'card_holder_phone'),
130 | ('billingAddressStreet', 'billing_address_street'),
131 | ('billingAddressNumber', 'billing_address_number'),
132 | ('billingAddressComplement', 'billing_address_complement'),
133 | ('billingAddressDistrict', 'billing_address_district'),
134 | ('billingAddressPostalCode', 'billing_address_postal_code'),
135 | ('billingAddressCity', 'billing_address_city'),
136 | ('billingAddressState', 'billing_address_state'),
137 | ]
138 |
139 | for key_to_set, key_to_get in credit_card_keys_map:
140 | params[key_to_set] = self.credit_card.get(key_to_get)
141 |
142 | if self.pre_approval:
143 |
144 | params['preApprovalCharge'] = self.pre_approval.get('charge')
145 | params['preApprovalName'] = self.pre_approval.get('name')
146 | params['preApprovalDetails'] = self.pre_approval.get('details')
147 | params['preApprovalAmountPerPayment'] = self.pre_approval.get(
148 | 'amount_per_payment')
149 | params['preApprovalMaxAmountPerPayment'] = self.pre_approval.get(
150 | 'max_amount_per_payment')
151 | params['preApprovalPeriod'] = self.pre_approval.get('period')
152 | params['preApprovalMaxPaymentsPerPeriod'] = self.pre_approval.get(
153 | 'max_payments_per_period')
154 | params['preApprovalMaxAmountPerPeriod'] = self.pre_approval.get(
155 | 'max_amount_per_period')
156 | params['preApprovalInitialDate'] = self.pre_approval.get(
157 | 'initial_date')
158 | params['preApprovalFinalDate'] = self.pre_approval.get(
159 | 'final_date')
160 | params['preApprovalMaxTotalAmount'] = self.pre_approval.get(
161 | 'max_total_amount')
162 |
163 | self.data.update(params)
164 | self.clean_none_params()
165 |
166 | def build_pre_approval_payment_params(self, **kwargs):
167 | """ build a dict with params """
168 |
169 | params = kwargs or {}
170 |
171 | params['reference'] = self.reference
172 | params['preApprovalCode'] = self.code
173 |
174 | for i, item in enumerate(self.items, 1):
175 | params['itemId%s' % i] = item.get('id')
176 | params['itemDescription%s' % i] = item.get('description')
177 | params['itemAmount%s' % i] = item.get('amount')
178 | params['itemQuantity%s' % i] = item.get('quantity')
179 | params['itemWeight%s' % i] = item.get('weight')
180 | params['itemShippingCost%s' % i] = item.get('shipping_cost')
181 |
182 | self.data.update(params)
183 | self.clean_none_params()
184 |
185 | def clean_none_params(self):
186 | self.data = \
187 | {k: v for k, v in self.data.items() if v or isinstance(v, bool)}
188 |
189 | @property
190 | def reference_prefix(self):
191 | return self.config.REFERENCE_PREFIX or "%s"
192 |
193 | @reference_prefix.setter
194 | def reference_prefix(self, value):
195 | self.config.REFERENCE_PREFIX = (value or "") + "%s"
196 |
197 | @property
198 | def reference(self):
199 | return self.reference_prefix % self._reference
200 |
201 | @reference.setter
202 | def reference(self, value):
203 | if not isinstance(value, str):
204 | value = str(value)
205 | if value.startswith(self.reference_prefix):
206 | value = value[len(self.reference_prefix):]
207 | self._reference = value
208 |
209 | def get(self, url):
210 | """ do a get transaction """
211 | return requests.get(url, params=self.data, headers=self.config.HEADERS)
212 |
213 | def post(self, url):
214 | """ do a post request """
215 | return requests.post(url, data=self.data, headers=self.config.HEADERS)
216 |
217 | def checkout(self, transparent=False, **kwargs):
218 | """ create a pagseguro checkout """
219 | self.data['currency'] = self.config.CURRENCY
220 | self.build_checkout_params(**kwargs)
221 | if transparent:
222 | response = self.post(url=self.config.TRANSPARENT_CHECKOUT_URL)
223 | else:
224 | response = self.post(url=self.config.CHECKOUT_URL)
225 | return PagSeguroCheckoutResponse(response.content, config=self.config)
226 |
227 | def transparent_checkout_session(self):
228 | response = self.post(url=self.config.SESSION_CHECKOUT_URL)
229 | return PagSeguroCheckoutSession(response.content,
230 | config=self.config).session_id
231 |
232 | def check_notification(self, code):
233 | """ check a notification by its code """
234 | response = self.get(url=self.config.NOTIFICATION_URL % code)
235 | return PagSeguroNotificationResponse(response.content, self.config)
236 |
237 | def check_pre_approval_notification(self, code):
238 | """ check a notification by its code """
239 | response = self.get(
240 | url=self.config.PRE_APPROVAL_NOTIFICATION_URL % code)
241 | return PagSeguroPreApprovalNotificationResponse(
242 | response.content, self.config)
243 |
244 | def pre_approval_ask_payment(self, **kwargs):
245 | """ ask form a subscribe payment """
246 | self.build_pre_approval_payment_params(**kwargs)
247 | response = self.post(url=self.config.PRE_APPROVAL_PAYMENT_URL)
248 | return PagSeguroPreApprovalPayment(response.content, self.config)
249 |
250 | def pre_approval_cancel(self, code):
251 | """ cancel a subscribe """
252 | response = self.get(url=self.config.PRE_APPROVAL_CANCEL_URL % code)
253 | return PagSeguroPreApprovalCancel(response.content, self.config)
254 |
255 | def check_transaction(self, code):
256 | """ check a transaction by its code """
257 | response = self.get(url=self.config.TRANSACTION_URL % code)
258 | return PagSeguroNotificationResponse(response.content, self.config)
259 |
260 | def query_transactions(self, initial_date, final_date,
261 | page=None,
262 | max_results=None):
263 | """ query transaction by date range """
264 | last_page = False
265 | results = []
266 | while last_page is False:
267 | search_result = self._consume_query_transactions(
268 | initial_date, final_date, page, max_results)
269 | results.extend(search_result.transactions)
270 | if search_result.current_page is None or \
271 | search_result.total_pages is None or \
272 | search_result.current_page == search_result.total_pages:
273 | last_page = True
274 | else:
275 | page = search_result.current_page + 1
276 |
277 | return results
278 |
279 | def _consume_query_transactions(self, initial_date, final_date,
280 | page=None,
281 | max_results=None):
282 | querystring = {
283 | 'initialDate': initial_date.strftime('%Y-%m-%dT%H:%M'),
284 | 'finalDate': final_date.strftime('%Y-%m-%dT%H:%M'),
285 | 'page': page,
286 | 'maxPageResults': max_results,
287 | }
288 | self.data.update(querystring)
289 | self.clean_none_params()
290 | response = self.get(url=self.config.QUERY_TRANSACTION_URL)
291 | return PagSeguroTransactionSearchResult(response.content, self.config)
292 |
293 | def query_pre_approvals(self, initial_date, final_date, page=None,
294 | max_results=None):
295 | """ query pre-approvals by date range """
296 | last_page = False
297 | results = []
298 | while last_page is False:
299 | search_result = self._consume_query_pre_approvals(
300 | initial_date, final_date, page, max_results)
301 | results.extend(search_result.pre_approvals)
302 | if search_result.current_page is None or \
303 | search_result.total_pages is None or \
304 | search_result.current_page == search_result.total_pages:
305 | last_page = True
306 | else:
307 | page = search_result.current_page + 1
308 |
309 | return results
310 |
311 | def _consume_query_pre_approvals(self, initial_date, final_date, page=None,
312 | max_results=None):
313 | querystring = {
314 | 'initialDate': initial_date.strftime('%Y-%m-%dT%H:%M'),
315 | 'finalDate': final_date.strftime('%Y-%m-%dT%H:%M'),
316 | 'page': page,
317 | 'maxPageResults': max_results,
318 | }
319 |
320 | self.data.update(querystring)
321 | self.clean_none_params()
322 |
323 | response = self.get(url=self.config.QUERY_PRE_APPROVAL_URL)
324 | return PagSeguroPreApprovalSearch(response.content, self.config)
325 |
326 | def query_pre_approvals_by_code(self, code):
327 | """ query pre-approvals by code """
328 | result = self._consume_query_pre_approvals_by_code(code)
329 | return result
330 |
331 | def _consume_query_pre_approvals_by_code(self, code):
332 |
333 | response = self.get(
334 | url='%s/%s' % (self.config.QUERY_PRE_APPROVAL_URL, code)
335 | )
336 | return PagSeguroPreApproval(response.content, self.config)
337 |
338 | def add_item(self, **kwargs):
339 | self.items.append(kwargs)
340 |
--------------------------------------------------------------------------------
/pagseguro/config.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | class Config(dict):
3 | def __init__(self, **kwargs):
4 | sandbox = kwargs.pop('sandbox', False)
5 |
6 | base_url = 'https://ws.pagseguro.uol.com.br'
7 | payment_host = 'https://pagseguro.uol.com.br'
8 | if sandbox:
9 | base_url = 'https://ws.sandbox.pagseguro.uol.com.br'
10 | payment_host = 'https://sandbox.pagseguro.uol.com.br'
11 |
12 | # prefixes/suffixes
13 | version = '/v2/'
14 | checkout_suffix = '{}checkout'.format(version)
15 | session_checkout_suffix = '{}sessions/'.format(version)
16 | notification_suffix = '{}transactions/notifications/%s'.format(version)
17 | pre_approval_notification_suffix = ('{}pre-approvals/'
18 | 'notifications/%s'.format(version))
19 | transaction_suffix = '{}transactions/%s'.format(version)
20 | query_transaction_suffix = '{}transactions'.format(version)
21 | ctype = 'application/x-www-form-urlencoded; charset=UTF-8'
22 |
23 | # default config settings
24 | defaults = dict(
25 | PRE_APPROVAL_PAYMENT_URL='{}{}pre-approvals/payment'.format(
26 | base_url, version),
27 | PRE_APPROVAL_CANCEL_URL='{}{}pre-approvals/cancel/%s'.format(
28 | base_url, version),
29 | SESSION_CHECKOUT_URL='{}{}'.format(
30 | base_url, session_checkout_suffix),
31 | TRANSPARENT_CHECKOUT_URL='{}{}'.format(
32 | base_url, query_transaction_suffix),
33 | CHECKOUT_URL='{}{}'.format(base_url, checkout_suffix),
34 | NOTIFICATION_URL='{}{}'.format(base_url, notification_suffix),
35 | PRE_APPROVAL_NOTIFICATION_URL='{}{}'.format(
36 | base_url, pre_approval_notification_suffix),
37 | TRANSACTION_URL='{}{}'.format(base_url, transaction_suffix),
38 | QUERY_TRANSACTION_URL='{}{}'.format(
39 | base_url, query_transaction_suffix),
40 | QUERY_PRE_APPROVAL_URL='{}{}pre-approvals'.format(
41 | base_url, version),
42 | CURRENCY='BRL',
43 | HEADERS={'Content-Type': ctype},
44 | PAYMENT_URL='{}{}/payment.html?code=%s'.format(
45 | payment_host, checkout_suffix),
46 | DATETIME_FORMAT='%Y-%m-%dT%H:%M:%S',
47 | REFERENCE_PREFIX='REF%s',
48 | USE_SHIPPING=True,
49 | )
50 |
51 | kwargs = {key.upper(): val for key, val in kwargs.items()}
52 | keys = defaults.keys()
53 | for key in keys:
54 | # only add override keys to properties
55 | value = kwargs.pop(key, defaults[key])
56 | setattr(self, key, value)
57 |
58 | def __getitem__(self, key):
59 | return getattr(self, key)
60 |
61 | def __setitem__(self, key, value):
62 | return setattr(self, key, value)
63 |
--------------------------------------------------------------------------------
/pagseguro/exceptions.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 |
3 |
4 | class PagSeguroValidationError(Exception):
5 | pass
6 |
--------------------------------------------------------------------------------
/pagseguro/parsers.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import logging
3 |
4 | from .utils import parse_date
5 | from .config import Config
6 |
7 | import xmltodict
8 |
9 | logger = logging.getLogger()
10 |
11 |
12 | class XMLParser(object):
13 | def __init__(self, xml, config=None):
14 | self.xml = xml
15 | self.errors = None
16 | if config is None:
17 | config = Config()
18 | self.config = config
19 | self.parse_xml(xml)
20 | logger.debug(self.__dict__)
21 |
22 | def parse_xml(self, xml):
23 | try:
24 | parsed = xmltodict.parse(xml, encoding="iso-8859-1")
25 | except Exception as e:
26 | logger.debug('Cannot parse the returned xml "%s" -> "%s"', xml, e)
27 | parsed = {}
28 |
29 | if 'errors' in parsed:
30 | self.errors = parsed['errors']['error']
31 |
32 | return parsed
33 |
34 |
35 | class PagSeguroNotificationResponse(XMLParser):
36 | def __getitem__(self, key):
37 | getattr(self, key, None)
38 |
39 | def parse_xml(self, xml):
40 | parsed = super(PagSeguroNotificationResponse, self).parse_xml(xml)
41 | if self.errors:
42 | return
43 | transaction = parsed.get('transaction', {})
44 | for k, v in transaction.items():
45 | setattr(self, k, v)
46 |
47 |
48 | class PagSeguroPreApprovalNotificationResponse(XMLParser):
49 | def __getitem__(self, key):
50 | getattr(self, key, None)
51 |
52 | def parse_xml(self, xml):
53 | parsed = super(PagSeguroPreApprovalNotificationResponse,
54 | self).parse_xml(xml)
55 | if self.errors:
56 | return
57 | transaction = parsed.get('transaction', {})
58 | for k, v in transaction.items():
59 | setattr(self, k, v)
60 |
61 |
62 | class PagSeguroPreApprovalCancel(XMLParser):
63 | def __getitem__(self, key):
64 | getattr(self, key, None)
65 |
66 | def parse_xml(self, xml):
67 | parsed = super(PagSeguroPreApprovalCancel, self).parse_xml(xml)
68 | if self.errors:
69 | return
70 | transaction = parsed.get('transaction', {})
71 | for k, v in transaction.items():
72 | setattr(self, k, v)
73 |
74 |
75 | class PagSeguroCheckoutSession(XMLParser):
76 | def __init__(self, xml, config=None):
77 | self.session_id = None
78 | super(PagSeguroCheckoutSession, self).__init__(xml, config)
79 |
80 | def parse_xml(self, xml):
81 | parsed = super(PagSeguroCheckoutSession, self).parse_xml(xml)
82 | if self.errors:
83 | return
84 | session = parsed.get('session', {})
85 | self.session_id = session.get('id')
86 |
87 |
88 | class PagSeguroPreApprovalPayment(XMLParser):
89 | def __init__(self, xml, config=None):
90 | self.code = None
91 | super(PagSeguroPreApprovalPayment, self).__init__(xml, config)
92 |
93 | def parse_xml(self, xml):
94 | parsed = super(PagSeguroPreApprovalPayment, self).parse_xml(xml)
95 | if self.errors:
96 | return
97 | result = parsed.get('result', {})
98 | self.code = result.get('transactionCode')
99 | self.date = parse_date(result.get('date'))
100 |
101 |
102 | class PagSeguroCheckoutResponse(XMLParser):
103 | def __init__(self, xml, config=None):
104 | self.code = None
105 | self.date = None
106 | self.payment_url = None
107 | self.payment_link = None
108 | self.transaction = None
109 | super(PagSeguroCheckoutResponse, self).__init__(xml, config)
110 |
111 | def parse_xml(self, xml):
112 | parsed = super(PagSeguroCheckoutResponse, self).parse_xml(xml)
113 | if self.errors:
114 | return
115 | checkout = parsed.get('checkout', {})
116 | self.code = checkout.get('code')
117 | self.date = parse_date(checkout.get('date'))
118 |
119 | self.payment_url = self.config.PAYMENT_URL % self.code
120 |
121 | # this is used only for transparent checkout process
122 | self.transaction = parsed.get('transaction', {})
123 | self.payment_link = self.transaction.get('paymentLink')
124 |
125 |
126 | class PagSeguroTransactionSearchResult(XMLParser):
127 | current_page = None
128 | total_pages = None
129 | results_in_page = None
130 | transactions = []
131 |
132 | def __getitem__(self, key):
133 | getattr(self, key, None)
134 |
135 | def parse_xml(self, xml):
136 | parsed = super(PagSeguroTransactionSearchResult, self).parse_xml(xml)
137 | if self.errors:
138 | return
139 | search_result = parsed.get('transactionSearchResult', {})
140 | self.transactions = search_result.get('transactions', {})
141 | self.transactions = self.transactions.get('transaction', [])
142 | if not isinstance(self.transactions, list):
143 | self.transactions = [self.transactions]
144 | self.current_page = search_result.get('currentPage', None)
145 | if self.current_page is not None:
146 | self.current_page = int(self.current_page)
147 | self.results_in_page = search_result.get('resultsInThisPage', None)
148 | if self.results_in_page is not None:
149 | self.results_in_page = int(self.results_in_page)
150 | self.total_pages = search_result.get('totalPages', None)
151 | if self.total_pages is not None:
152 | self.total_pages = int(self.total_pages)
153 |
154 |
155 | class PagSeguroPreApproval(XMLParser):
156 |
157 | def __getitem__(self, key):
158 | getattr(self, key, None)
159 |
160 | def parse_xml(self, xml):
161 | parsed = super(PagSeguroPreApproval, self).parse_xml(xml)
162 | if self.errors:
163 | return
164 | result = parsed.get('preApproval', {})
165 | self.name = result.get('name', None)
166 | self.code = result.get('code', None)
167 | self.date = parse_date(result.get('date'))
168 | self.tracker = result.get('tracker', None)
169 | self.status = result.get('status', None)
170 | self.reference = result.get('reference', None)
171 | self.last_event_date = result.get('lastEventDate', None)
172 | self.charge = result.get('charge', None)
173 | self.sender = result.get('sender', {})
174 |
175 |
176 | class PagSeguroPreApprovalSearch(XMLParser):
177 |
178 | current_page = None
179 | total_pages = None
180 | results_in_page = None
181 | pre_approvals = []
182 |
183 | def __getitem__(self, key):
184 | getattr(self, key, None)
185 |
186 | def parse_xml(self, xml):
187 | parsed = super(PagSeguroPreApprovalSearch, self).parse_xml(xml)
188 | if self.errors:
189 | return
190 | search_result = parsed.get('preApprovalSearchResult', {})
191 | self.pre_approvals = search_result.get('preApprovals', {})
192 | self.pre_approvals = self.pre_approvals.get('preApproval', [])
193 | if not isinstance(self.pre_approvals, list):
194 | self.pre_approvals = [self.pre_approvals]
195 | self.current_page = search_result.get('currentPage', None)
196 | if self.current_page is not None:
197 | self.current_page = int(self.current_page)
198 | self.results_in_page = search_result.get('resultsInThisPage', None)
199 | if self.results_in_page is not None:
200 | self.results_in_page = int(self.results_in_page)
201 | self.total_pages = search_result.get('totalPages', None)
202 | if self.total_pages is not None:
203 | self.total_pages = int(self.total_pages)
204 |
--------------------------------------------------------------------------------
/pagseguro/utils.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | import re
3 |
4 | import arrow
5 |
6 | from .exceptions import PagSeguroValidationError
7 |
8 |
9 | def parse_date(date_str):
10 | return arrow.get(date_str).datetime
11 |
12 | # Validators
13 | EMPTY_VALUES = (None, '', [], (), {})
14 |
15 |
16 | def is_valid_email(value):
17 | user_regex = re.compile(
18 | r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*$"
19 | r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013'
20 | r"""\014\016-\177])*"$)""", re.IGNORECASE)
21 | domain_regex = re.compile(
22 | r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|'
23 | r'[A-Z0-9-]{2,})$|^\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|'
24 | r'2[0-4]\d|[0-1]?\d?\d)){3}\]$', re.IGNORECASE)
25 | domain_whitelist = ['localhost']
26 |
27 | if not value or '@' not in value:
28 | raise PagSeguroValidationError(u'Email inválido')
29 |
30 | user_part, domain_part = value.rsplit('@', 1)
31 |
32 | if not user_regex.match(user_part):
33 | raise PagSeguroValidationError(u'Email inválido')
34 |
35 | if (domain_part not in domain_whitelist and
36 | not domain_regex.match(domain_part)):
37 | # Try for possible IDN domain-part
38 | try:
39 | domain_part = domain_part.encode('idna').decode('ascii')
40 | if not domain_regex.match(domain_part):
41 | raise PagSeguroValidationError(u'Email inválido')
42 | else:
43 | return value
44 | except UnicodeError:
45 | pass
46 | raise PagSeguroValidationError(u'Email inválido')
47 | return value
48 |
49 |
50 | def DV_maker(v):
51 | if v >= 2:
52 | return 11 - v
53 | return 0
54 |
55 |
56 | def is_valid_cpf(value):
57 | error_messages = {
58 | 'invalid': u"CPF Inválido",
59 | 'max_digits': (u"CPF possui 11 dígitos (somente números) ou 14"
60 | u" (com pontos e hífen)"),
61 | 'digits_only': (u"Digite um CPF com apenas números ou com ponto e "
62 | u"hífen"),
63 | }
64 |
65 | if value in EMPTY_VALUES:
66 | return u''
67 | orig_value = value[:]
68 | if not value.isdigit():
69 | value = re.sub("[-\.]", "", value)
70 | try:
71 | int(value)
72 | except ValueError:
73 | raise PagSeguroValidationError(error_messages['digits_only'])
74 | if len(value) != 11:
75 | raise PagSeguroValidationError(error_messages['max_digits'])
76 | orig_dv = value[-2:]
77 |
78 | new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -
79 | 1))])
80 | new_1dv = DV_maker(new_1dv % 11)
81 | value = value[:-2] + str(new_1dv) + value[-1]
82 | new_2dv = sum([i * int(value[idx]) for idx, i in enumerate(range(11, 1, -
83 | 1))])
84 | new_2dv = DV_maker(new_2dv % 11)
85 | value = value[:-1] + str(new_2dv)
86 | if value[-2:] != orig_dv:
87 | raise PagSeguroValidationError(error_messages['invalid'])
88 |
89 | return orig_value
90 |
91 |
92 | def is_valid_cnpj(value):
93 |
94 | error_messages = {
95 | 'invalid': u"CNPJ Inválido",
96 | 'max_digits': (u"CNPJ possui 14 dígitos (somente números) ou 14"
97 | u" (com pontos e hífen)"),
98 | 'digits_only': (
99 | u"Digite um CNPJ com apenas números ou com ponto, barra "
100 | u"hífen"),
101 | }
102 |
103 | if value in EMPTY_VALUES:
104 | return u''
105 | if not value.isdigit():
106 | value = re.sub("[-/\.]", "", value)
107 | orig_value = value[:]
108 | try:
109 | int(value)
110 | except ValueError:
111 | raise PagSeguroValidationError(error_messages['digits_only'])
112 | if len(value) != 14:
113 | raise PagSeguroValidationError(error_messages['max_digits'])
114 |
115 | orig_dv = value[-2:]
116 |
117 | new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(list(range(
118 | 5, 1, -1)) + list(range(9, 1, -1)))])
119 | new_1dv = DV_maker(new_1dv % 11)
120 | value = value[:-2] + str(new_1dv) + value[-1]
121 | new_2dv = sum([i * int(value[idx]) for idx, i in enumerate(list(range(
122 | 6, 1, -1)) + list(range(9, 1, -1)))])
123 | new_2dv = DV_maker(new_2dv % 11)
124 | value = value[:-1] + str(new_2dv)
125 | if value[-2:] != orig_dv:
126 | raise PagSeguroValidationError(error_messages['invalid'])
127 |
128 | return orig_value
129 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests>=2.11.1
2 | xmltodict>=0.10.2
3 | arrow>=0.8.0
4 |
--------------------------------------------------------------------------------
/requirements_dev.txt:
--------------------------------------------------------------------------------
1 | flake8==2.6.2
2 | pytest==2.9.2
3 | pytest-cov==2.3.0
4 | pytest-sugar==0.7.1
5 | responses==0.5.1
6 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | try:
4 | from setuptools import setup
5 | except ImportError:
6 | from distutils.core import setup
7 |
8 | readme = open('README.md').read()
9 |
10 | with open('requirements.txt') as reqs:
11 | requirements = reqs.read().split()
12 |
13 |
14 | setup(
15 | name='pagseguro',
16 | version='0.3.4',
17 | description='Pagseguro API v2 wrapper',
18 | author='Bruno Rocha',
19 | author_email='rochacbruno@gmail.com',
20 | url='https://github.com/rochacbruno/python-pagseguro',
21 | packages=['pagseguro', ],
22 | package_dir={'pagseguro': 'pagseguro'},
23 | include_package_data=True,
24 | install_requires=requirements,
25 | long_description=readme,
26 | long_description_content_type='text/markdown',
27 | license='MIT',
28 | test_suite='tests',
29 | zip_safe=False,
30 | classifiers=[
31 | 'Development Status :: 3 - Alpha',
32 | 'Environment :: Web Environment',
33 | 'Intended Audience :: Developers',
34 | 'Natural Language :: English',
35 | 'License :: OSI Approved :: BSD License',
36 | 'Programming Language :: Python',
37 | 'Programming Language :: Python :: 2',
38 | 'Programming Language :: Python :: 2.6',
39 | 'Programming Language :: Python :: 2.7',
40 | 'Programming Language :: Python :: 3',
41 | 'Programming Language :: Python :: 3.3',
42 | 'Programming Language :: Python :: 3.6',
43 | 'Programming Language :: Python :: 3.7',
44 | ],
45 | keywords='pagseguro, payment, payments, credit-card'
46 | )
47 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Japle/python-pagseguro/08a8aa7f934b16d00948ead17a0e470a88f2479f/tests/__init__.py
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import pytest
3 |
4 | from pagseguro import PagSeguro
5 |
6 |
7 | @pytest.fixture(scope='session')
8 | def sender():
9 | return {
10 | 'name': u'Guybrush Treepwood',
11 | 'area_code': 11,
12 | "phone": 5555555,
13 | "email": 'guybrush@monkeyisland.com',
14 | "cpf": "00000000000",
15 | "born_date": "06/08/1650",
16 | }
17 |
18 |
19 | @pytest.fixture(scope='session')
20 | def shipping():
21 | return {
22 | "type": PagSeguro.SEDEX,
23 | "street": "Av Brig Faria Lima",
24 | "number": 1234,
25 | "complement": "5 andar",
26 | "district": "Jardim Paulistano",
27 | "postal_code": "06650030",
28 | "city": "Sao Paulo",
29 | "state": "SP",
30 | "country": "BRA",
31 | "cost": "1234.56"
32 | }
33 |
34 |
35 | @pytest.fixture(scope='session')
36 | def items():
37 | return [
38 | {
39 | "id": "0001",
40 | "description": "Produto 1",
41 | "amount": 354.20,
42 | "quantity": 2,
43 | "weight": 200
44 | },
45 | {
46 | "id": "0002",
47 | "description": "Produto 2",
48 | "amount": 355.20,
49 | "quantity": 1,
50 | "weight": 200
51 | },
52 | ]
53 |
--------------------------------------------------------------------------------
/tests/test_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from pagseguro.config import Config
4 |
5 | import pytest
6 |
7 |
8 | @pytest.fixture
9 | def custom_config():
10 | return {
11 | 'payment_url': 'http://google.com'
12 | }
13 |
14 |
15 | @pytest.fixture
16 | def common_config():
17 | return { # flake8: noqa
18 | 'CHECKOUT_URL': 'https://ws.pagseguro.uol.com.br/v2/checkout',
19 | 'CURRENCY': 'BRL',
20 | 'DATETIME_FORMAT': '%Y-%m-%dT%H:%M:%S',
21 | 'HEADERS': {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
22 | 'NOTIFICATION_URL': 'https://ws.pagseguro.uol.com.br/v2/transactions/notifications/%s',
23 | 'PAYMENT_URL': 'https://pagseguro.uol.com.br/v2/checkout/payment.html?code=%s',
24 | 'PRE_APPROVAL_CANCEL_URL': 'https://ws.pagseguro.uol.com.br/v2/pre-approvals/cancel/%s',
25 | 'PRE_APPROVAL_NOTIFICATION_URL': 'https://ws.pagseguro.uol.com.br/v2/pre-approvals/notifications/%s',
26 | 'PRE_APPROVAL_PAYMENT_URL': 'https://ws.pagseguro.uol.com.br/v2/pre-approvals/payment',
27 | 'QUERY_PRE_APPROVAL_URL': 'https://ws.pagseguro.uol.com.br/v2/pre-approvals',
28 | 'QUERY_TRANSACTION_URL': 'https://ws.pagseguro.uol.com.br/v2/transactions',
29 | 'SESSION_CHECKOUT_URL': 'https://ws.pagseguro.uol.com.br/v2/sessions/',
30 | 'TRANSACTION_URL': 'https://ws.pagseguro.uol.com.br/v2/transactions/%s',
31 | 'TRANSPARENT_CHECKOUT_URL': 'https://ws.pagseguro.uol.com.br/v2/transactions'
32 | }
33 |
34 |
35 | def test_common_config(common_config):
36 | c = Config()
37 | for key, val in common_config.items():
38 | print(key, val)
39 | assert getattr(c, key) == common_config[key]
40 |
41 |
42 | def test_custom_config(custom_config):
43 | c = Config(**custom_config)
44 | assert c.PAYMENT_URL == custom_config['payment_url']
45 |
46 | def test_config_special_methods():
47 | c = Config()
48 | assert c.__getitem__('PAYMENT_URL') == c.PAYMENT_URL
49 |
50 | c.__setitem__('PAYMENT_URL', 'http://google.com')
51 | assert c.PAYMENT_URL == 'http://google.com'
52 | assert c['PAYMENT_URL'] == 'http://google.com'
53 |
--------------------------------------------------------------------------------
/tests/test_pagseguro.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import pytest
3 |
4 | from pagseguro import PagSeguro, PagSeguroTransactionSearchResult
5 | from pagseguro.config import Config
6 | from pagseguro.exceptions import PagSeguroValidationError
7 | from pagseguro.utils import is_valid_email, is_valid_cpf
8 |
9 | TOKEN = '123456'
10 | EMAIL = 'seu@email.com'
11 |
12 |
13 | @pytest.fixture
14 | def pagseguro():
15 | return PagSeguro(token=TOKEN, email=EMAIL)
16 |
17 |
18 | @pytest.fixture
19 | def xml():
20 | return """
21 |
22 | 2011-02-16T20:14:35.000-02:00
23 | 1
24 | 2
25 | 1
26 |
27 |
28 | 2011-02-05T15:46:12.000-02:00
29 | 2011-02-15T17:39:14.000-03:00
30 | 9E884542-81B3-4419-9A75-BCC6FB495EF1
31 | REF1234
32 | 1
33 | 3
34 |
35 | 1
36 |
37 | 49900.00
38 | 0.00
39 | 0.00
40 | 49900.00
41 | 0.00
42 |
43 |
44 | 2011-02-07T18:57:52.000-02:00
45 | 2011-02-14T21:37:24.000-03:00
46 | 2FB07A22-68FF-4F83-A356-24153A0C05E1
47 | REF5678
48 | 3
49 | 4
50 |
51 | 3
52 |
53 | 26900.00
54 | 0.00
55 | 0.00
56 | 26900.00
57 | 0.00
58 |
59 |
60 | """
61 |
62 |
63 | def test_pagseguro_class(pagseguro):
64 | assert isinstance(pagseguro, PagSeguro)
65 |
66 |
67 | def test_pagseguro_initial_attrs(pagseguro):
68 | assert isinstance(pagseguro.config, Config)
69 | assert isinstance(pagseguro.data, dict)
70 | assert 'email' in pagseguro.data
71 | assert 'token' in pagseguro.data
72 | assert pagseguro.data['email'] == EMAIL
73 | assert pagseguro.data['token'] == TOKEN
74 | assert isinstance(pagseguro.items, list)
75 | assert isinstance(pagseguro.sender, dict)
76 | assert isinstance(pagseguro.shipping, dict)
77 | assert pagseguro._reference == ''
78 | assert pagseguro.extra_amount is None
79 | assert pagseguro.redirect_url is None
80 | assert pagseguro.notification_url is None
81 | assert pagseguro.abandon_url is None
82 |
83 |
84 | def test_build_checkout_params_with_all_params(pagseguro, sender, shipping,
85 | items):
86 | pagseguro.sender = sender
87 | pagseguro.shipping = shipping
88 | pagseguro.extra_amount = 12.50
89 | pagseguro.redirect_url = '/redirecionando/'
90 | pagseguro.abandon_url = '/abandonando/'
91 | pagseguro.items = items
92 | pagseguro.build_checkout_params()
93 |
94 | # check all data fields
95 | assert isinstance(pagseguro.data, dict)
96 | keys = ['email', 'token', 'senderName', 'senderAreaCode',
97 | 'senderPhone', 'senderEmail', 'senderCPF', 'senderBornDate',
98 | 'shippingType', 'shippingAddressStreet',
99 | 'shippingAddressNumber', 'shippingAddressComplement',
100 | 'shippingAddressDistrict', 'shippingAddressPostalCode',
101 | 'shippingAddressCity', 'shippingAddressState',
102 | 'shippingAddressCountry', 'shippingCost', 'extraAmount',
103 | 'redirectURL', 'abandonURL']
104 | # items
105 | item_keys = ['itemId{}', 'itemDescription{}', 'itemAmount{}',
106 | 'itemQuantity{}', 'itemWeight{}']
107 |
108 | for key in keys:
109 | assert key in pagseguro.data
110 |
111 | for i, key in enumerate(pagseguro.items, 1):
112 | keys_to_compare = map(lambda x: x.format(i), item_keys)
113 | for item_key in keys_to_compare:
114 | assert item_key in pagseguro.data
115 |
116 |
117 | def test_add_items_util(items):
118 | pagseguro = PagSeguro(token=TOKEN, email=EMAIL)
119 | pagseguro.add_item(**items[0])
120 | pagseguro.add_item(**items[1])
121 | assert len(pagseguro.items) == 2
122 |
123 |
124 | def test_reference(pagseguro):
125 | pagseguro.reference = '12345'
126 | assert str(pagseguro.reference) == u'REF12345'
127 |
128 |
129 | def test_clean_none_params(sender):
130 | pagseguro = PagSeguro(email=EMAIL, token=TOKEN)
131 | sender_copy = sender.copy()
132 | sender_copy['cpf'] = None
133 | sender_copy['born_date'] = None
134 | pagseguro.sender = sender_copy
135 | pagseguro.build_checkout_params()
136 | assert 'senderCPF' not in pagseguro.data
137 | assert 'senderBornData' not in pagseguro.data
138 |
139 |
140 | def test_is_valid_email(sender):
141 | bad_email = 'john.com'
142 | pagseguro = PagSeguro(email=bad_email, token=TOKEN)
143 | pagseguro.sender = {'email': bad_email}
144 | with pytest.raises(PagSeguroValidationError):
145 | pagseguro.build_checkout_params()
146 |
147 | # Now testing with a valid email
148 | pagseguro.sender['email'] = sender['email']
149 | assert is_valid_email(pagseguro.sender['email']) == sender['email']
150 |
151 |
152 | def test_is_valid_cpf():
153 | bad_cpf = '123.456.267-45'
154 | pagseguro = PagSeguro(email=EMAIL, token=TOKEN)
155 | pagseguro.sender = {'cpf': bad_cpf}
156 |
157 | with pytest.raises(PagSeguroValidationError):
158 | pagseguro.build_checkout_params()
159 |
160 | # Now testing with a valid email
161 | pagseguro.sender['cpf'] = '482.268.465-28'
162 | assert is_valid_cpf(pagseguro.sender['cpf']) == pagseguro.sender['cpf']
163 |
164 | pagseguro.sender['cpf'] = '48226846528'
165 | assert is_valid_cpf(pagseguro.sender['cpf']) == pagseguro.sender['cpf']
166 |
167 |
168 | def test_parse_xml(xml):
169 | pg = PagSeguro(email=EMAIL, token=TOKEN)
170 | result = PagSeguroTransactionSearchResult(xml, pg.config)
171 | assert result.current_page == 1
172 | assert result.results_in_page == 2
173 | assert result.total_pages == 1
174 | assert len(result.transactions) == 2
175 |
176 |
177 | def test_pagseguro_with_bad_config():
178 | with pytest.raises(Exception):
179 | PagSeguro(email=EMAIL, token=TOKEN, config=2)
180 |
--------------------------------------------------------------------------------
/tests/test_pagseguro_sandbox.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import pytest
3 |
4 | from pagseguro import PagSeguro, PagSeguroTransactionSearchResult
5 | from pagseguro.exceptions import PagSeguroValidationError
6 | from pagseguro.utils import is_valid_email, is_valid_cpf
7 |
8 | TOKEN = 'sandbox_token'
9 | EMAIL = 'pagseguro_email'
10 |
11 |
12 | @pytest.fixture
13 | def pagseguro_sandbox():
14 | return PagSeguro(token=TOKEN, email=EMAIL, config=dict(sandbox=True))
15 |
16 |
17 | @pytest.fixture
18 | def xml_sandbox():
19 | return """
20 |
21 | 2011-02-16T20:14:35.000-02:00
22 | 1
23 | 2
24 | 1
25 |
26 |
27 | 2011-02-05T15:46:12.000-02:00
28 | 2011-02-15T17:39:14.000-03:00
29 | 9E884542-81B3-4419-9A75-BCC6FB495EF1
30 | REF1234
31 | 1
32 | 3
33 |
34 | 1
35 |
36 | 49900.00
37 | 0.00
38 | 0.00
39 | 49900.00
40 | 0.00
41 |
42 |
43 | 2011-02-07T18:57:52.000-02:00
44 | 2011-02-14T21:37:24.000-03:00
45 | 2FB07A22-68FF-4F83-A356-24153A0C05E1
46 | REF5678
47 | 3
48 | 4
49 |
50 | 3
51 |
52 | 26900.00
53 | 0.00
54 | 0.00
55 | 26900.00
56 | 0.00
57 |
58 |
59 | """
60 |
61 |
62 | def test_pagseguro_class(pagseguro_sandbox):
63 | assert isinstance(pagseguro_sandbox, PagSeguro)
64 |
65 |
66 | def test_pagseguro_initial_attrs(pagseguro_sandbox):
67 | assert isinstance(pagseguro_sandbox.data, dict)
68 | assert 'email' in pagseguro_sandbox.data
69 | assert 'token' in pagseguro_sandbox.data
70 | assert pagseguro_sandbox.data['email'] == EMAIL
71 | assert pagseguro_sandbox.data['token'] == TOKEN
72 | assert isinstance(pagseguro_sandbox.items, list)
73 | assert isinstance(pagseguro_sandbox.sender, dict)
74 | assert isinstance(pagseguro_sandbox.shipping, dict)
75 | assert pagseguro_sandbox._reference == ''
76 | assert pagseguro_sandbox.extra_amount is None
77 | assert pagseguro_sandbox.redirect_url is None
78 | assert pagseguro_sandbox.notification_url is None
79 | assert pagseguro_sandbox.abandon_url is None
80 |
81 |
82 | def test_build_checkout_params_with_all_params(pagseguro_sandbox, sender,
83 | shipping, items):
84 | pagseguro_sandbox.sender = sender
85 | pagseguro_sandbox.shipping = shipping
86 | pagseguro_sandbox.extra_amount = 12.50
87 | pagseguro_sandbox.redirect_url = '/redirecionando/'
88 | pagseguro_sandbox.abandon_url = '/abandonando/'
89 | pagseguro_sandbox.items = items
90 | pagseguro_sandbox.build_checkout_params()
91 | # check all data fields
92 | assert isinstance(pagseguro_sandbox.data, dict)
93 | keys = ['email', 'token', 'senderName', 'senderAreaCode',
94 | 'senderPhone', 'senderEmail', 'senderCPF', 'senderBornDate',
95 | 'shippingType', 'shippingAddressStreet',
96 | 'shippingAddressNumber', 'shippingAddressComplement',
97 | 'shippingAddressDistrict', 'shippingAddressPostalCode',
98 | 'shippingAddressCity', 'shippingAddressState',
99 | 'shippingAddressCountry', 'shippingCost', 'extraAmount',
100 | 'redirectURL', 'abandonURL']
101 |
102 | # items
103 | item_keys = ['itemId{}', 'itemDescription{}', 'itemAmount{}',
104 | 'itemQuantity{}', 'itemWeight{}']
105 |
106 | import pprint
107 | pprint.pprint(pagseguro_sandbox.data)
108 |
109 | for key in keys:
110 | assert key in pagseguro_sandbox.data
111 |
112 | for i, key in enumerate(pagseguro_sandbox.items, 1):
113 | keys_to_compare = map(lambda x: x.format(i), item_keys)
114 | for item_key in keys_to_compare:
115 | assert item_key in pagseguro_sandbox.data
116 |
117 |
118 | def test_add_items_util(items):
119 | pagseguro = PagSeguro(email=EMAIL, token=TOKEN)
120 | pagseguro.add_item(**items[0])
121 | pagseguro.add_item(**items[1])
122 | assert len(pagseguro.items) == 2
123 |
124 |
125 | def test_reference(pagseguro_sandbox):
126 | pagseguro_sandbox.reference = '12345'
127 | assert str(pagseguro_sandbox.reference) == u'REF12345'
128 |
129 |
130 | def test_clean_none_params(sender):
131 | pagseguro = PagSeguro(email=EMAIL, token=TOKEN)
132 | sender_copy = sender.copy()
133 | sender_copy['cpf'] = None
134 | sender_copy['born_date'] = None
135 | pagseguro.sender = sender_copy
136 | pagseguro.build_checkout_params()
137 |
138 | assert 'senderCPF' not in pagseguro.data
139 | assert 'senderBornData' not in pagseguro.data
140 |
141 |
142 | def test_is_valid_email(sender):
143 | bad_email = 'john.com'
144 | pagseguro = PagSeguro(email=bad_email, token=TOKEN)
145 | pagseguro.sender = {'email': bad_email}
146 | with pytest.raises(PagSeguroValidationError):
147 | pagseguro.build_checkout_params()
148 |
149 | # Now testing with a valid email
150 | pagseguro.sender['email'] = sender['email']
151 | assert is_valid_email(pagseguro.sender['email']) == sender['email']
152 |
153 |
154 | def test_is_valid_cpf():
155 | bad_cpf = '123.456.267-45'
156 | pagseguro = PagSeguro(email=EMAIL, token=TOKEN)
157 | pagseguro.sender = {'cpf': bad_cpf}
158 | with pytest.raises(PagSeguroValidationError):
159 | pagseguro.build_checkout_params()
160 |
161 | # Now testing with a valid email
162 | pagseguro.sender['cpf'] = '482.268.465-28'
163 | assert is_valid_cpf(pagseguro.sender['cpf']) == pagseguro.sender['cpf']
164 |
165 | pagseguro.sender['cpf'] = '48226846528'
166 | assert is_valid_cpf(pagseguro.sender['cpf']) == pagseguro.sender['cpf']
167 |
168 |
169 | def test_parse_xml(xml_sandbox):
170 | pg = PagSeguro(email='seu@email.com', token='123456')
171 | result = PagSeguroTransactionSearchResult(xml_sandbox, pg.config)
172 | assert result.current_page == 1
173 | assert result.results_in_page == 2
174 | assert result.total_pages == 1
175 | assert len(result.transactions) == 2
176 |
--------------------------------------------------------------------------------
/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import datetime
3 |
4 | from pagseguro.utils import (is_valid_cpf, is_valid_cnpj, is_valid_email,
5 | parse_date)
6 | from pagseguro.exceptions import PagSeguroValidationError
7 |
8 | import pytest
9 | from dateutil.tz import tzutc
10 |
11 |
12 | def test_is_valid_email():
13 | valid = 'test@email.com'
14 | valid2 = u'user@росси́я.ро'
15 | not_valid = '@asd.com'
16 | not_valid2 = 'bad'
17 | not_valid3 = u'user@росси́я'
18 |
19 | with pytest.raises(PagSeguroValidationError):
20 | is_valid_email(not_valid)
21 |
22 | with pytest.raises(PagSeguroValidationError):
23 | is_valid_email(not_valid2)
24 |
25 | with pytest.raises(PagSeguroValidationError):
26 | is_valid_email(not_valid3)
27 |
28 | assert is_valid_email(valid) == 'test@email.com'
29 | assert is_valid_email(valid2) == u'user@росси́я.ро'
30 |
31 |
32 | def test_parse_date():
33 | # DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
34 | date_str = '2016-10-10T10:10:10'
35 | assert parse_date(date_str) == datetime.datetime(2016, 10, 10, 10, 10, 10,
36 | tzinfo=tzutc())
37 |
38 |
39 | def test_is_valid_cpf():
40 | valid = '041.684.826-50'
41 | valid2 = '04168482650'
42 | bad = 'bla///'
43 | max_digits = '1111111111111111111111111'
44 | invalid_cpf = '040.684.826-50'
45 |
46 | with pytest.raises(PagSeguroValidationError):
47 | is_valid_cpf(bad)
48 |
49 | with pytest.raises(PagSeguroValidationError):
50 | is_valid_cpf(max_digits)
51 |
52 | with pytest.raises(PagSeguroValidationError):
53 | is_valid_cpf(invalid_cpf)
54 |
55 | assert is_valid_cpf(valid) == valid
56 | assert is_valid_cpf(valid2) == '04168482650'
57 |
58 |
59 | def test_is_valid_cnpj():
60 | valid = '31331052000174'
61 | valid2 = '72.168.117/0001-90'
62 |
63 | invalid = '///'
64 | digits = '1111111'
65 | wrong_number = '31331052000175'
66 |
67 | with pytest.raises(PagSeguroValidationError):
68 | is_valid_cnpj(invalid)
69 |
70 | with pytest.raises(PagSeguroValidationError):
71 | is_valid_cnpj(digits)
72 |
73 | with pytest.raises(PagSeguroValidationError):
74 | is_valid_cnpj(wrong_number)
75 |
76 | assert is_valid_cnpj(valid) == '31331052000174'
77 | assert is_valid_cnpj(valid2) == '72168117000190'
78 |
--------------------------------------------------------------------------------