def post(self, request, **kwargs): # kwargs contains bank_code, but we're not doing anything with # that right now. # kwargs['bank_code'] # Create validation URL from user_variable_{0,1} and our secret: user_variable_0 = request.POST.get('user_variable_0', '') user_variable_1 = request.POST.get('user_variable_1', '') if not user_variable_0.isdigit() or user_variable_1 == '': raise TypeError('Someone is messing with us.') project_password = (getattr(settings, 'OSSO_PAYMENT_SOFORT', {}).get('project_password', '').encode('utf-8')) user_variable_1_hash_pass = sha256( '%s%s' % (user_variable_1.encode('utf-8'), project_password)).hexdigest() # Display a page where we can choose what "has happened" and # redirect to the appropriate place. base_url = '' path = reverse('osso_payment_sofort_passed', kwargs={ 'payment_id': user_variable_0, 'transaction_hash': user_variable_1_hash_pass }) passed_url = '%s%s' % (base_url, path) path = reverse('osso_payment_sofort_aborted', kwargs={ 'payment_id': user_variable_0, 'transaction_key': user_variable_1 }) aborted_url = '%s%s' % (base_url, path) message = '''<h1>Sofort FAKE iDEAL</h1> <dl> <dt>Regarding</dt> <dd>%(description)s</dd> <dt>Amount:</dt> <dd>%(amount)s</dd> </dl> <p> <a href="%(passed_url)s">Passed</a> or <a href="%(aborted_url)s">Aborted</a> </p> ''' % { 'description': xmlescape(request.POST.get('reason_1', '')), 'amount': xmlescape(request.POST.get('amount', '')), 'passed_url': xmlescape(passed_url, '"'), 'aborted_url': xmlescape(aborted_url, '"'), } return HttpResponse(message, content_type='text/html; charset=UTF-8')
def get_start_parameters(self): parameters = { 'rtlo': self.rtlo, 'description': self.payment.description, 'amount': int(self.payment.amount * 100), 'returnurl': self.build_absolute_uri(reverse( 'osso_payment_targetpay_return', kwargs={'payment_id': self.payment.id})), 'cancelurl': self.build_absolute_uri(reverse( 'osso_payment_targetpay_abort', kwargs={'payment_id': self.payment.id})), 'reporturl': self.build_absolute_uri(reverse( 'osso_payment_targetpay_report', kwargs={'payment_id': self.payment.id})), } return parameters
def start_transaction(self, payment, locale=None, remote_addr=None): """ Called by the code before redirecting the user to MSP. """ locale = locale or 'nl_NL' assert len(locale.split('_')) == 2, locale # looks like nl_NL ? remote_addr = remote_addr or '' host_prefix = payment.realm if '://' not in host_prefix: host_prefix = 'http://%s' % (host_prefix, ) notification_url = '%s%s' % (host_prefix, reverse('osso_payment_msp_report')) redirect_url = '%s%s' % (host_prefix, reverse('osso_payment_msp_return', kwargs={'payment_id': payment.id})) cancel_url = '%s%s' % (host_prefix, reverse('osso_payment_msp_abort', kwargs={'payment_id': payment.id})) # Prepare the data! template = self.XML_PAYMENT_REQUEST extra_kwargs = { 'transaction_id': payment.id, 'notification_url': notification_url, 'redirect_url': redirect_url, 'cancel_url': cancel_url, 'locale_code': locale, # nl_NL 'country_code': locale.split('_', 1)[1], # NL 'remote_addr': remote_addr, 'email': xmlescape(payment.paying_user.email), 'amount_cents': int(payment.amount * 100), 'description': xmlescape(payment.description), 'signature': self.get_signature(payment), } # Do the query! return self._do_request(template, extra_kwargs)
def get_payment_form(self, payment, bank_id): # Note that description should be clean. The caller should have # fed it through the provider specific clean_description(). description = str(payment.description) if any(i not in ('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz +,-.') for i in description): raise BuyerError( 'Illegal character found in description: %s' % (description,)) if len(description) > 29: raise BuyerError( 'Size of description too large (max 29): %s' % (description,)) scheme_and_host = payment.realm if '://' in scheme_and_host: scheme, host = scheme_and_host.split('://', 1) else: scheme, host = 'http', scheme_and_host if host == 'localhost' or host.startswith('localhost:'): host = 'example.com' # Mollie refuses 'localhost' even in testmode host_prefix = '%s://%s' % (scheme, host) report_url = '%s%s' % (host_prefix, reverse( 'osso_payment_mollie_report', kwargs={'payment_id': payment.id})) return_url = '%s%s' % (host_prefix, reverse( 'osso_payment_mollie_return', kwargs={'payment_id': payment.id})) # Check whether we've "used" this payment already. If we don't # check this here, we might first find out when setting # set_unique_key() below, and then we will have created a new # payment which we'll get status reports for. (And we won't # recognise those, since we won't know that it's the right key.) if payment.unique_key: raise PaymentAlreadyUsed() # user clicked back? # Mollie takes the amount in cents. amount = int(payment.get_amount() * 100) params = { 'a': 'fetch', 'partnerid': self.partner_id, 'amount': amount, 'bank_id': '%04d' % (bank_id,), 'description': description, 'reporturl': report_url, 'returnurl': return_url, } if self.profile_key: params['profile_key'] = self.profile_key response = self._do_request(params) try: order = order2dict(response) except ValueError: raise ProviderError('Provider response invalid', response) if order['currency'] != 'EUR': # what else? raise ProviderError('Unexpected currency', response) if int(order['amount']) != amount: raise ProviderError( 'Payment amount mismatch with request: %s vs. %s (cents)' % (order['amount'], amount), response) if len(order['transaction_id']) < 16: raise ProviderError( 'Received transaction_id seems unsafe', order['transaction_id']) # Store transaction_id as the unique key. This happens to be a # 32 byte string. We add just a little bit of extra uniqueness # by appending our PK. if '-' in str(payment.id): raise NotImplementedError( # rsplit below would fail 'Cannot cope with minus in PK', payment.id) unique_key = '%s-%s' % (order['transaction_id'], payment.id) payment.set_unique_key(unique_key) return self.create_html_form_from_url(order['URL'], 'mollie_form')
def get_payment_form(self, payment): # Note that description should be clean. # FIXME: the description is limited to 128 bytes alnum or # something similar? Not relevant for now, but we should check # that we're not feeding paypal something invalid... host_prefix = payment.realm if '://' not in host_prefix: host_prefix = 'http://%s' % (host_prefix,) success_url = '%s%s' % (host_prefix, reverse( 'osso_payment_paypal_passed', kwargs={'payment_id': payment.id})) abort_url = '%s%s' % (host_prefix, reverse( 'osso_payment_paypal_aborted', kwargs={'payment_id': payment.id})) # Check whether we've "used" this payment already. If we don't # check this here, we might first find out when setting # set_unique_key() below, and then we will have created a new # payment which we'll get status reports for. (And we won't # recognise those, since we won't know that it's the right key.) # FIXME/TODO: is it possible to not raise an error but continue # instead?? if payment.unique_key: raise PaymentAlreadyUsed() # user clicked back? # For some obscure reason, the payment total does not show up on # the initial SetExpressCheckout page shown by PayPal. We don't # need to show any other pages to the user, but we alter the # description to include the price. description_and_price = '%s - EUR %.2f' % ( payment.description, payment.get_amount()) params = { # API options 'METHOD': 'SetExpressCheckout', 'returnUrl': success_url, 'cancelUrl': abort_url, # Mandatory payment info 'PAYMENTREQUEST_0_AMT': '%.2f' % (payment.get_amount(),), 'PAYMENTREQUEST_0_CURRENCYCODE': 'EUR', # we're in the EU.. 'PAYMENTREQUEST_0_DESC': description_and_price, # UI options 'BRANDNAME': payment.realm, # TODO: should be optional 'LOCALECODE': 'NL', # Favor non-Paypal (creditcard) payments in UI. 'LANDINGPAGE': 'Billing', # We don't need a PayPal account ("PayPal account optional"). 'SOLUTIONTYPE': 'Sole', # Disable shipping and useless notes. With shipping # disabled, we can skip the GetExpressCheckoutDetails and # jump right to the DoExpressCheckoutPayment 'REQCONFIRMSHIPPING': '0', 'NOSHIPPING': '1', 'ALLOWNOTE': '0', } # Do request response = self._do_request(params) # SetExpressCheckout returns something like: # TOKEN=EC%2d7V703271YD3213158&TIMESTAMP=2012%2d03%2d24T21%3a55%3a03Z # &CORRELATIONID=35a823921598f&ACK=Success&VERSION=78&BUILD=2649250 token = response['TOKEN'] if len(token) < 16: raise BuyerError('Received TOKEN seems unsafe', token) # Store the TOKEN which, along with our PK, makes for a unique # key which is random enough not to attract any evil-doers. Not # that they would get very far anyway. The payment is performed # and validated by a call from us to paypal in process_passed(). if '-' in str(payment.id): raise NotImplementedError( # rsplit below would fail 'Cannot cope with minus in PK', payment.id) unique_key = '%s-%s' % (token, str(payment.id).upper()) payment.set_unique_key(unique_key) # Create a GET URL where the user gets redirected to: form = ( '<form id="paypal_form" method="GET" action="%(action)s">' '<input type="hidden" name="cmd" value="_express-checkout"/>' '<input type="hidden" name="token" value="%(token)s"/>' '</form>' ) % { 'action': htmlesc(self.frontend_url), 'token': htmlesc(token), } return form