Example #1
0
    def get_payment_form(self, payment, locale=None, remote_addr=None):
        # Check whether we've "used" this payment already. We don't really need
        # this for MSP here, but in our current implementation, the user of the
        # payment API calls mark_submitted() (transfer_initiated=yes) on the
        # redirecting/form page. When called a second time, that will raise an
        # error.
        if payment.transfer_initiated:
            raise PaymentAlreadyUsed()  # user clicked back?

        # (1) Start transaction by messaging MultiSafepay directly.
        result = self.start_transaction(payment,
                                        locale=locale,
                                        remote_addr=remote_addr)
        # result = '''<?xml version="1.0" encoding="UTF-8"?>
        # <redirecttransaction result="ok">
        #     <transaction>
        #         <id>4039</id>
        #         <payment_url>https://pay.multisafepay.com/pay/?transaction=12345&amp;lang=nl_NL</payment_url>
        #     </transaction>
        # </redirecttransaction>'''
        # OR
        # result = '''<?xml version="1.0" encoding="UTF-8"?>
        # <redirecttransaction result="error">
        #     <error>
        #         <code>1006</code>
        #         <description>Invalid transaction ID</description>
        #     </error>
        #     <transaction><id>4326</id></transaction>
        # </redirecttransaction>'''

        # (2) Fetch URL from results.
        try:
            dom = string2dom(result)

            # FIXME: ugly code
            error = dom.getElementsByTagName('error')
            if error:
                code = error[0].getElementsByTagName('code')[0]
                code = u''.join(i.wholeText for i in code.childNodes)
                desc = error[0].getElementsByTagName('description')[0]
                desc = u''.join(i.wholeText for i in desc.childNodes)
                if code == '1006':  # Invalid transaction ID
                    # User clicked back and a used payment was attempted
                    # again, or it could simply be that the credentials are
                    # bad.
                    log('Credentials bad or payment already used', 'msp',
                        'err')
                    raise PaymentAlreadyUsed()  # user clicked back somehow?

            payment_url_node = dom.getElementsByTagName('payment_url')[0]
            payment_url = u''.join(i.wholeText
                                   for i in payment_url_node.childNodes)
        except PaymentAlreadyUsed:
            raise
        except:
            mail_admins('Error when parsing result', result)  # XXX/TEMP
            raise

        # (3) Send user to URL.
        return self.create_html_form_from_url(payment_url, 'msp_form')
Example #2
0
    def post(self, request, payment_id):
        self.check_remote_addr(request)

        log(repr(request.POST), 'targetpay', 'report')

        content_type = 'text/plain; charset=UTF-8'
        try:
            payment = Payment.objects.get(id=payment_id)
            if payment.unique_key:
                trxid = payment.unique_key.split('-', 1)[1]
                if request.POST.get('trxid') != trxid:
                    raise Payment.DoesNotExist('bad trxid?')
            elif request.POST.get('status') == 'Expired':
                # Since aug2018, TGP started sending Expired notices for
                # 3-hour old transactions that weren't picked up.
                # Mark transaction as failed, and answer with OK.
                payment.mark_aborted()
                return HttpResponse('OK', content_type=content_type)

        except Payment.DoesNotExist as e:
            mail_admins('Check failed at TGP TransactionReport',
                        (u'Exception: %s (%r)\n\nGet: %r\n\nPost: %r\n\n'
                         u'Meta: %r' %
                         (e, e, request.GET, request.POST, request.META)))
            response = HttpResponse('NAK', content_type=content_type)
            response.status_code = 500
            return response

        provider_sub = payment.unique_key.split('-', 1)[0]
        targetpay = get_instance(provider_sub)
        try:
            targetpay.request_status(payment, request)
        except Exception as e:
            if isinstance(e, AtomicUpdateDupe):
                # "duplicate status"
                status, reply = 200, 'OK'
            else:
                status, reply = 500, 'NAK'

            payinfo = {
                'id': payment.id,
                'created': payment.created,
                'is_success': payment.is_success,
                'blob': payment.blob,
            }
            mail_admins(
                (u'Replying with %s to TGP report [%s, %s, %s] '
                 u'(might indicate a problem)' %
                 (reply, payment.id, payment.is_success, payment.created)),
                (u'Exception: %s (%s)\n\nGet: %r\n\nPost: %r\n\n'
                 u'Traceback: %s\n\nMeta: %r\n\nPayment: %r' %
                 (e, e, request.GET, request.POST, traceback.format_exc(),
                  request.META, payinfo)))
            response = HttpResponse(reply, content_type=content_type)
            response.status_code = status
            return response

        return HttpResponse('OK', content_type=content_type)
Example #3
0
    def _do_request(self, params):
        """
        Do request, check for failure and return original XML as binary
        string.
        """
        params['USER'] = self.username
        params['PWD'] = self.password
        params['SIGNATURE'] = self.signature
        params['VERSION'] = '78'  # not sure if needed

        urlencoded_params = urllib.urlencode(params)
        log(self.backend_url + '?' + urlencoded_params, 'paypal', 'qry')
        response = urllib2.urlopen(self.backend_url, urlencoded_params)
        data = response.read()
        log(data, 'paypal', 'ret')
        decoded = urlparse.parse_qs(data)

        ack = decoded.get('ACK', ['Failure'])[0]
        if ack != 'Success':
            # API failure codes:
            # https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_errorcodes
            errorcode = int(
                decoded.get('L_ERRORCODE0', ['-1'])[0])
            severity = decoded.get(
                'L_SEVERITYCODE0', ['Unspecified'])[0]
            shortmsg = decoded.get(
                'L_SHORTMESSAGE0', ['Unknown'])[0]
            longmsg = decoded.get(
                'L_LONGMESSAGE0', ['An unknown error occurred'])[0]
            if errorcode == 10001:
                # Internal Error
                raise ProviderDown('PayPal')
            if errorcode == 10002:
                # Error, Security Error, Security header is not valid
                raise ProviderBadConfig(
                    'Security error (paypal error 10002); '
                    'common cause is bad or test mode credentials')
            if errorcode == 10417:
                # Transaction cannot complete (Instruct the customer to
                # use an alternative payment method)
                raise TryDifferentPayment()
            raise ProviderError(errorcode, severity, shortmsg, longmsg)

        try:
            values = listdict2stringdict(decoded)
        except ValueError:
            raise ProviderError(repr(decoded))

        return values
Example #4
0
    def _do_request(self, params, timeout_seconds=None):
        """
        Do request, check for failure and return original XML as binary
        string.
        """
        if self.testing:
            params['testmode'] = 'true'
        url = '%s?%s' % (self.api_url, urllib.urlencode(params))
        log(url, 'mollie', 'qry')
        response = urllib2.urlopen(url, data=None, timeout=timeout_seconds)
        data = response.read()
        log(data, 'mollie', 'ret')

        # Mollie implements error raising in more than one way. Find all
        # the available errors.
        errors = any2errorlist(data)
        if errors:
            # Translate some errors into something we can trap.
            for i in errors:
                # http://www.mollie.nl/support/documentatie/betaaldiensten/ideal/en/
                # (-2, u"A fetch was issued without specification "
                #      u"of 'partnerid'.")
                # (-2, u'This account does not exist or is suspended.')
                # ...
                if i[0] in (-2, -11, -13, -15, -16):
                    raise ProviderBadConfig(
                        ('Account does not exist or does not have enough '
                         'permissions'), data)
                # Our custom error from the <order>.
                # (E_BANK_DOWN, 'Your iDEAL-payment has not been setup '
                #               'because of a temporary technical error '
                #               'at the bank')
                if i[0] == E_BANK_DOWN:
                    try:
                        bank = int(params.get('bank_id'))
                        if aboutconfig:
                            banks = ast.literal_eval(
                                aboutconfig('payment.provider.mollie.banks',
                                            BANKS_MARCH_2012))
                            banks = dict((i['id'], i['name']) for i in banks)
                            bank = banks[bank]
                    except:
                        bank = '(unknown)'
                    raise ProviderDown('Mollie', bank)
            raise ProviderError(errors)

        return data
Example #5
0
    def _do_request(self, template, extra_kwargs):
        kwargs = {
            'ua': self.ua,
            'account': self.account,
            'site_id': self.site_id,
            'site_secure_code': self.site_secure_code,
        }
        kwargs.update(extra_kwargs)
        body = template % kwargs

        headers = {
            'Content-Type': 'text/xml; charset=UTF-8',
            'Accept': '*/*',
            'User-Agent': self.ua,
        }

        # Apparently we get SSL handshake timeouts after 1m52. That's too
        # long. Try and reduce that by adding a defaulttimeout. Only if that
        # works can be start adding a retry.
        # NOTE: http://hg.python.org/cpython/rev/3d9a86b8cc98/
        # NOTE: http://hg.python.org/cpython/rev/ce4916ca06dd/
        socket.setdefaulttimeout(20)

        timeout_seconds = 5
        request = urllib2.Request(self.api_url,
                                  data=body.encode('utf-8'),
                                  headers=headers)

        log(body, 'msp', 'out')
        try:
            response = urllib2.urlopen(request, timeout=timeout_seconds)
        except urllib2.HTTPError as e:
            # TEMP: this could use some tweaking
            contents = e.read()
            mail_admins(
                'Incoming error to this XML', body + '\n\n--\n' + contents +
                '\n\nURL: ' + self.api_url + '\n')
            log('Got error %s with response: %s' % (e.code, contents), 'msp',
                'error')
            raise
        except Exception as e:
            # TEMP: this could use some tweaking
            mail_admins('Incoming error to this XML',
                        body + '\n\nURL: ' + self.api_url + '\n')
            log('Got error: %s' % (e, ), 'msp', 'error')
            raise
        else:
            data = response.read()
            log(data, 'msp', 'in')

        return data
Example #6
0
    def _do_request(self, url, parameters):
        url = '{url}?{parameters}'.format(
            url=url, parameters=urlencode(parameters))

        for attempt in range(3):
            log(url, 'targetpay', 'qry.{}'.format(self.provider_sub))
            try:
                ret = http_get(url, opt=http_opt)
            except (HTTPError, URLError, error, timeout) as e:
                log('connection error #{}: {}'.format(attempt + 1, e),
                    'targetpay', 'ret.{}'.format(self.provider_sub))
            else:
                break
        else:
            raise
        log(ret, 'targetpay', 'ret.{}'.format(self.provider_sub))

        return ret