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&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')
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)
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
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
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
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