예제 #1
0
    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')
예제 #2
0
 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
예제 #3
0
    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)
예제 #4
0
    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')
예제 #5
0
    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