Source code for braspag.core

# -*- encoding: utf-8 -*-

from __future__ import absolute_import

import uuid
import httplib
import logging
import unicodedata

import jinja2

from .utils import spaceless, is_valid_guid
from .exceptions import BraspagHttpResponseException
from .response import CreditCardAuthorizationResponse, BilletResponse, \
                      BilletDataResponse, CreditCardCancelResponse
from xml.dom import minidom
from xml.etree import ElementTree
from decimal import Decimal, InvalidOperation


class BraspagRequest(object):
    """Implements Braspag Pagador API (manual version 1.9).

Billet generation is not yet implemented.

    """

    def __init__(self, merchant_id=None, homologation=False):
        if homologation:
            self.url = 'homologacao.pagador.com.br'
        else:
            self.url = 'www.pagador.com.br'

        self.merchant_id = merchant_id

        self.jinja_env = jinja2.Environment(
            autoescape=True,
            loader=jinja2.PackageLoader('braspag'),
        )

        self.log = logging.getLogger('braspag')

    def _request(self, xml, query=False):
        if query:
            uri = '/services/pagadorQuery.asmx'
        else:
            uri = '/webservice/pagadorTransaction.asmx'

        if isinstance(xml, unicode):
            xml = xml.encode('utf-8')

        http = httplib.HTTPSConnection(self.url)
        http.request("POST", uri, body=xml, headers = {
            "Host": "localhost",
            "Content-Type": "text/xml; charset=UTF-8",
        })
        response = http.getresponse()
        xmlresponse = response.read()
        self.log.debug(minidom.parseString(xmlresponse).toprettyxml(indent='  '))
        return xmlresponse

[docs] def authorize(self, **kwargs): """All arguments supplied to this method must be keyword arguments. :arg order_id: Order id. It will be used to indentify the order later in Braspag. :arg customer_id: Must be user's CPF/CNPJ. :arg customer_name: User's full name. :arg customer_email: User's email address. :arg amount: Amount to charge. :arg card_holder: Name printed on card. :arg card_number: Card number. :arg card_security_code: Card security code. :arg card_exp_date: Card expiration date. :arg save_card: Flag that tell to Braspag to store card number. If set to True Response will return a card token. *Default: False*. :arg card_token: Card token returned by Braspag. When used it should replace *card_holder*, *card_exp_date*, *card_number* and *card_security_code*. :arg number_of_payments: Number of payments that the amount will be devided (number of months). *Default: 1*. :arg currency: Currency of the given amount. *Default: BRL*. :arg country: User's country. *Default: BRA*. :arg transaction_type: An integer representing one of the :ref:`transaction_types`. *Default: 2*. :arg payment_plan: An integer representing how multiple payments should be handled. *Default: 0*. See :ref:`payment_plans`. :arg payment_method: Integer representing one of the available :ref:`payment_methods`. :arg soft_descriptor: Order description to be shown on the customer card statement. Maximum of 13 characters. :returns: :class:`~braspag.BraspagResponse` """ assert any((kwargs.get('card_number'), kwargs.get('card_token'))),\ 'card_number ou card_token devem ser fornecidos' if kwargs.get('card_number'): card_keys = ( 'card_holder', 'card_security_code', 'card_exp_date', 'card_number', ) assert all(kwargs.has_key(key) for key in card_keys), \ (u'Transações com Cartão de Crédito exigem os ' u'parametros: {0}'.format(' ,'.join(card_keys))) if not kwargs.get('number_of_payments'): kwargs['number_of_payments'] = 1 try: number_of_payments = int(kwargs.get('number_of_payments')) except ValueError: raise BraspagException('Number of payments must be int.') if not kwargs.get('payment_plan'): if number_of_payments > 1: # 2 = parcelado pelo emissor do cartão kwargs['payment_plan'] = 2 else: # 0 = a vista kwargs['payment_plan'] = 0 if not kwargs.get('currency'): kwargs['currency'] = 'BRL' if not kwargs.get('country'): kwargs['country'] = 'BRA' if not kwargs.get('transaction_type'): # 2 = captura automatica kwargs['transaction_type'] = 2 if kwargs.get('save_card', False): kwargs['save_card'] = 'true' soft_desc = kwargs.get('soft_descriptor') if soft_desc: # only keep first 13 chars soft_desc = soft_desc[:13] # Replace special chars by ascii soft_desc = unicodedata.normalize('NFKD', soft_desc) soft_desc = soft_desc.encode('ascii', 'ignore') kwargs['soft_descriptor'] = soft_desc xml_request = self._render_template('authorize_creditcard.xml', kwargs) response = self._request(spaceless(xml_request)) return CreditCardAuthorizationResponse(response)
def _base_transaction(self, transaction_id, amount, type=None): assert type in ('Refund', 'Void', 'Capture') assert is_valid_guid(transaction_id), 'Transaction ID invalido' data_dict = { 'amount': amount, 'type': type, 'transaction_id': transaction_id, } xml_request = self._render_template('base.xml', data_dict) xml_response = self._request(xml_request) if type in ('Void', 'Refund'): return CreditCardCancelResponse(xml_response) else: return CreditCardAuthorizationResponse(xml_response) def void(self, transaction_id, amount=0): """Void the given amount for the given transaction_id. This method should be used to return funds to customers for transactions that happened within less than 23h and 59 minutes ago. For other transactions use :meth:`~braspag.BraspagRequest.refund`. If the amount is 0 (zero) the full transaction will be voided. :returns: :class:`~braspag.BraspagResponse` """ return self._base_transaction(transaction_id, amount, 'Void') def refund(self, transaction_id, amount=0): """Refund the given amount for the given transaction_id. This method should be used to return funds to customers for transactions that happened at least 24 hours ago. For transactions that happended within 24 hours use :meth:`~braspag.BraspagRequest.void`. If the amount is 0 (zero) the full transaction will be refunded. :returns: :class:`~braspag.BraspagResponse` """ return self._base_transaction(transaction_id, amount, 'Refund') def capture(self, transaction_id, amount=0): """Capture the given `amount` from the given transaction_id. This method should only be called after pre-authorizing the transaction by calling :meth:`~braspag.BraspagRequest.authorize` with `transaction_types` 1 or 3. :returns: :class:`~braspag.BraspagResponse` """ return self._base_transaction(transaction_id, amount, 'Capture') def _render_template(self, template_name, data_dict): if self.merchant_id: data_dict['merchant_id'] = self.merchant_id if not data_dict.has_key('request_id'): data_dict['request_id'] = unicode(uuid.uuid4()) template = self.jinja_env.get_template(template_name) xml_request = template.render(data_dict) self.log.debug(xml_request) return xml_request def issue_billet(self, **kwargs): """All arguments supplied to this method must be keyword arguments. :arg order_id: Order id. It will be used to indentify the order later in Braspag. :arg customer_id: Must be user's CPF/CNPJ. :arg customer_name: User's full name. :arg customer_email: User's email address. :arg amount: Amount to charge. :arg currency: Currency of the given amount. *Default: BRL*. :arg country: User's country. *Default: BRA*. :arg payment_method: Payment method code :returns: :class:`~braspag.BraspagResponse` """ if not kwargs.get('currency'): kwargs['currency'] = 'BRL' if not kwargs.get('country'): kwargs['country'] = 'BRA' kwargs['is_billet'] = True xml_request = self._render_template('authorize_billet.xml', kwargs) return BilletResponse(self._request(spaceless(xml_request))) def get_billet_data(self, transaction_id): """All arguments supplied to this method must be keyword arguments. :arg transaction_id: The id of the transaction generated previously by *issue_billet* :returns: :class:`~braspag.BilletResponse` """ assert is_valid_guid(transaction_id), 'Invalid Transaction ID' context = {'transaction_id': transaction_id} xml_request = self._render_template('get_billet_data.xml', context) xml_response = self._request(spaceless(xml_request), query=True) return BilletDataResponse(xml_response)

Project Versions

This Page