Esempio n. 1
0
 def getPayment(self):
     api_instance = TransactionsApi()
     try:
         response = api_instance.retrieve_transaction(
             location_id=self.locationId, transaction_id=self.transactionId)
         if response.errors:
             logger.error(
                 'Unable to retrieve Square transaction from record.')
             return None
     except ApiException as e:
         logger.error('Unable to retrieve Square transaction from record.')
         return None
     return response.transaction
Esempio n. 2
0
    def getPayment(self):
        api_instance = TransactionsApi()
        api_instance.api_client.configuration.access_token = getattr(
            settings, 'SQUARE_ACCESS_TOKEN', '')

        try:
            response = api_instance.retrieve_transaction(
                location_id=self.locationId, transaction_id=self.transactionId)
            if response.errors:
                logger.error(
                    'Unable to retrieve Square transaction from record.')
                return None
        except ApiException as e:
            logger.error('Unable to retrieve Square transaction from record.')
            return None
        return response.transaction
Esempio n. 3
0
def chargePayment(orderId, ccData, ipAddress):
  try:
    order = Order.objects.get(id=orderId)
    idempotency_key = str(uuid.uuid1())
    convertedTotal = int(order.total*100)

    amount = {'amount': convertedTotal, 'currency': settings.SQUARE_CURRENCY}

    billing_address = {'address_line_1': ccData["address1"], 'address_line_2': ccData["address2"],
                       'locality': ccData["city"], 'administrative_district_level_1': ccData["state"],
                       'postal_code': ccData["postal"], 'country': ccData["country"],
                       'buyer_email_address': ccData["email"],
                       'first_name': ccData["cc_firstname"], 'last_name': ccData["cc_lastname"]}

    body = {'idempotency_key': idempotency_key, 'card_nonce': ccData["nonce"], 'amount_money': amount,
            'reference_id': order.reference, 'billing_address': billing_address}

    print("---- Begin Transaction ----")
    print(body)

    api_instance = TransactionsApi()
    api_instance.api_client.configuration.access_token = settings.SQUARE_ACCESS_TOKEN;
    api_response = api_instance.charge(settings.SQUARE_LOCATION_ID, body)

    print("---- Charge Submitted ----")
    print(api_response)

    if api_response.errors and len(api_response.errors) > 0:
        message = api_response.errors[0].details
        print("---- Transaction Failed ----")
        return False, message

    print("---- End Transaction ----")

    return True, ""
  except ApiException as e:
    print("---- Transaction Failed ----")
    print e
    print("---- End Transaction ----")
    logger.exception("!!Failed Square Transaction!!")
    return False, "An unexpected error has occurred."
Esempio n. 4
0
def process_square(price):
    if not request.form or 'nonce' not in request.form:
        return "Bad Request", 422
    square = Square.query.first()
    nonce = request.form['nonce']
    api_client = ApiClient()
    api_client.configuration.access_token = square.access_token
    customers_api = CustomersApi(api_client)
    customer_request = CreateCustomerRequest(email_address=current_user.email)
    try:
        customer_res = customers_api.create_customer(customer_request)
    except Exception as e:
        flash('Card could not be processed.')
        current_app.logger.error(e, exc_info=True)
        return redirect(url_for('main.support'))
    customer = customer_res.customer
    if customer is None:
        flash('Card could not be processed.')
        current_app.logger.info(f'''
            {current_user.username} card declined:
            {customer_res.errors}
            ''')
        return redirect(url_for('main.support'))
    else:
        customer_card_request = CreateCustomerCardRequest(card_nonce=nonce, )
        try:
            card_res = customers_api.create_customer_card(
                customer.id,
                customer_card_request,
            )
        except Exception as e:
            flash('Card could not be processed.')
            current_app.logger.error(e, exc_info=True)
            return redirect(url_for('main.support'))
        card = card_res.card
        if card is None:
            flash('Card could not be processed.')
            current_app.logger.info(f'''
                {current_user.username} card declined:
                {card_res.errors}
                ''')
            return redirect(url_for('main.support'))
        else:
            current_user.square_id = customer.id
            current_user.square_card = card.id
    transactions_api = TransactionsApi(api_client)
    idempotency_key = str(uuid.uuid1())
    cents = price * 100
    amount = {'amount': cents, 'currency': 'USD'}
    body = {
        'idempotency_key': idempotency_key,
        'customer_id': current_user.square_id,
        'customer_card_id': current_user.square_card,
        'amount_money': amount,
    }
    try:
        charge_response = transactions_api.charge(square.location_id, body)
    except Exception as e:
        flash('Card could not be processed.')
        current_app.logger.error(e, exc_info=True)
        return redirect(url_for('main.support'))
    transaction = charge_response.transaction
    if transaction is None:
        flash('Card could not be processed.')
        current_app.logger.info(f'''
            {current_user.username} card declined:
            {charge_response.errors}
            ''')
        return redirect(url_for('main.support'))
    elif transaction.id is not None:
        flash('Subscription Updated')
        if current_user.expiration <= datetime.today():
            base = datetime.today()
        else:
            base = current_user.expiration
        current_user.expiration = base + timedelta(days=30)
        new_role = PriceLevel.query.filter_by(price=price).first()
        if hasattr(new_role, 'name'):
            current_user.role = new_role.name
        else:
            current_user.role = PriceLevel.query.first().name
            current_app.logger.error(f'{current_user.username} \
                        signed up for nonexistent price level.')
        db.session.commit()
        return redirect(url_for('main.index'))
Esempio n. 5
0
from squareconnect.rest import ApiException
from squareconnect.apis.locations_api import LocationsApi
api_instance = LocationsApi()
api_instance.api_client.configuration.access_token = 'EAAAEOBMM7-B8GAqhBJEIqAgodrXyq7EmVZc_uGdx_GxdqF-IQG6lwITGsmPdV9J'
api_response = api_instance.list_locations()
print (api_response.locations)


from squareconnect.apis.transactions_api import TransactionsApi
api_instance = TransactionsApi()
api_instance.api_client.configuration.access_token = 'EAAAEOBMM7-B8GAqhBJEIqAgodrXyq7EmVZc_uGdx_GxdqF-IQG6lwITGsmPdV9J'
api_response = api_instance.list_transactions(location_id='3KK1N9SVK8QP1')

from squareconnect.apis.orders_api import OrdersApi
api_instance = OrdersApi()
api_instance.api_client.configuration.access_token = 'EAAAEOBMM7-B8GAqhBJEIqAgodrXyq7EmVZc_uGdx_GxdqF-IQG6lwITGsmPdV9J'
body = {
  "idempotency_key": "8193148c-9586-11e6-99f9-28cfe92138cf",
  "line_items": [
    {
      "name": "coke",
      "quantity": "2",
      "variation_name" : "Regular",
      "base_price_money": {
        "amount": 3300,
        "currency": "INR"
      }
    }
  ]
}
Esempio n. 6
0
    def refund(self, amount=None):
        api_instance = TransactionsApi()
        api_instance.api_client.configuration.access_token = getattr(
            settings, 'SQUARE_ACCESS_TOKEN', '')
        transaction = self.getPayment()

        # For both partial and full refunds, we loop through the tenders and refund
        # them as much as possible until we've refunded all that we want to refund.
        if not amount:
            amount = sum([x.amount_money.amount / 100 for x in transaction.tenders or []]) - \
                sum([x.amount_money.amount / 100 for x in transaction.refunds or []])

        refundData = []
        print('Beginning refund process.')

        remains_to_refund = amount
        tender_index = 0
        while remains_to_refund > 0:
            idempotency_key = str(uuid.uuid1())

            this_tender = transaction.tenders[tender_index]
            this_tender_refundamount = sum([
                x.amount_money.amount / 100 for x in transaction.refunds or []
                if x.tender_id == this_tender.id
            ])

            to_refund = min(
                this_tender.amount_money.amount - this_tender_refundamount,
                remains_to_refund)

            body = {
                'idempotency_key': idempotency_key,
                'tender_id': this_tender.id,
                'amount_money': {
                    'amount': int(to_refund * 100),
                    'currency': this_tender.amount_money.currency
                }
            }

            try:
                response = api_instance.create_refund(
                    location_id=self.locationId,
                    transaction_id=self.transactionId,
                    body=body)
                if response.errors:
                    logger.error('Error in providing Square refund: %s' %
                                 response.errors)
                    refundData.append({
                        'status': 'error',
                        'status': response.errors
                    })
                    break
            except ApiException as e:
                logger.error('Error in providing Square refund.')
                refundData.append({'status': 'error', 'errors': e})
                break

            print('Refund was successful?  Data is: %s' % response)

            # Note that fees are often 0 or missing here, but we enqueue the task
            # retrieve and update them afterward.
            refundData.append({
                'status':
                'success',
                'refund_id':
                response.refund.id,
                'refundAmount':
                float(response.refund.amount_money.amount) / 100,
                'fees':
                float(
                    getattr(
                        getattr(response.refund, 'processing_fee_money', None),
                        'amount', 0)) / 100,
            })

            remains_to_refund -= to_refund
            tender_index += 1

            # Once the refund process is complete, fees will be calculated,
            # so schedule a task to get them and update records one minute
            # in the future.
            updateSquareFees.schedule(args=(self, ), delay=60)

        print('Ready to return: %s' % refundData)
        return refundData
Esempio n. 7
0
def renewals_square(begin):
    scheduler.app.logger.info('Starting Square renewals')
    tomorrow = datetime.today() + timedelta(hours=24)
    failed_list = []
    declined_list = []
    with scheduler.app.app_context():
        charge_list = User.query.filter(
            User.expiration < tomorrow,
            User.expiration > begin,
            User.square_id != None,
            User.role != None,
        ).all()
        if charge_list:
            square = Square.query.first()
            api_client = ApiClient()
            api_client.configuration.access_token = square.access_token
            transactions_api = TransactionsApi(api_client)
            for user in charge_list:
                idempotency_key = str(uuid.uuid1())
                price_level = PriceLevel.query.filter_by(
                    name=user.role).first()
                if price_level is None:
                    failed_list.append(user)
                    continue
                cents = price_level.price * 100
                amount = {'amount': cents, 'currency': 'USD'}
                body = {
                    'idempotency_key': idempotency_key,
                    'customer_id': user.square_id,
                    'customer_card_id': user.square_card,
                    'amount_money': amount,
                }
                try:
                    charge_response = transactions_api.charge(
                        square.location_id, body)
                except Exception as e:
                    scheduler.app.logger.info(
                        f'{user.username} card declined {e}')
                    declined_list.append(user)
                    continue
                transaction = charge_response.transaction
                if transaction is None:
                    scheduler.app.logger.info(f'{user.username} card declined')
                    declined_list.append(user)
                    continue
                elif transaction.id is None:
                    scheduler.app.logger.info(f'{user.username} card declined')
                    declined_list.append(user)
                    continue
                else:
                    if user.expiration <= datetime.today():
                        base = datetime.today()
                    else:
                        base = user.expiration
                    user.expiration = base + timedelta(days=30)
                    db.session.commit()
    send_failed_emails(
        scheduler.app,
        failed_list=failed_list,
        declined_list=declined_list,
    )
    scheduler.app.logger.info('Square renewals complete')
Esempio n. 8
0
def chargePayment(order, ccData, ipAddress):
    """
    Returns two variabies:
        success - general success flag
        message - type of failure.
    """

    try:
        idempotency_key = str(uuid.uuid1())
        convertedTotal = int(order.total*100)

        amount = {'amount': convertedTotal, 'currency': settings.SQUARE_CURRENCY}

        billing_address = {
            'postal_code' : ccData['card_data']['billing_postal_code'],
        }

        try:
            billing_address.update(
                {'address_line_1': ccData["address1"], 'address_line_2': ccData["address2"],
                 'locality': ccData["city"], 'administrative_district_level_1': ccData["state"],
                 'postal_code': ccData["postal"], 'country': ccData["country"],
                 'buyer_email_address': ccData["email"],
                 'first_name': ccData["cc_firstname"], 'last_name': ccData["cc_lastname"]}
            )
        except KeyError as e:
            logger.debug("One or more billing address field omited - skipping")

        body = {
            'idempotency_key': idempotency_key,
            'card_nonce': ccData["nonce"], 
            'amount_money': amount,
            'reference_id': order.reference,
            'billing_address': billing_address
        }

        logger.debug("---- Begin Transaction ----")
        logger.debug(body)

        api_instance = TransactionsApi()
        api_instance.api_client.configuration.access_token = settings.SQUARE_ACCESS_TOKEN
        api_response = api_instance.charge(settings.SQUARE_LOCATION_ID, body)

        logger.debug("---- Charge Submitted ----")
        logger.debug(api_response)

        #try:
        #import pdb; pdb.set_trace()
        order.lastFour = api_response.transaction.tenders[0].card_details.card.last_4
        order.apiData = json.dumps(api_response.to_dict())
        order.notes = "Square: #" + api_response.transaction.id[:4]
        #except Exception as e:
        #    logger.debug(dir(api_response))
        #    logger.exception(e)
        order.save()

        if api_response.errors and len(api_response.errors) > 0:
            message = api_response.errors[0].details
            logger.debug("---- Transaction Failed ----")
            return False, message

        logger.debug("---- End Transaction ----")

        return True, ""
    except ApiException as e:
        logger.debug("---- Transaction Failed ----")
        logger.error("!!Failed Square Transaction!!")
        logger.exception(e)
        logger.debug("---- End Transaction ----")
        try:
            return False, json.loads(e.body)
        except:
            return False, str(e)
    def handle(self, *args, **options):

        from cms.api import add_plugin
        from cms.models import Page, StaticPlaceholder

        foundErrors = False

        try:
            initial_language = settings.LANGUAGES[0][0]
        except IndexError:
            initial_language = getattr(settings, 'LANGUAGE_CODE', 'en')

        # Do some sanity checks to ensure that necessary apps are listed in INSTALLED_APPS
        # before proceeding
        required_apps = [
            ('cms', 'Django CMS'),
            ('danceschool.core', 'Core danceschool app'),
            ('danceschool.payments.square', 'Square integration app'),
        ]
        for this_app in required_apps:
            if not apps.is_installed(this_app[0]):
                self.stdout.write(
                    self.style.ERROR(
                        'ERROR: %s is not installed or listed in INSTALLED_APPS. Please install before proceeding.'
                        % this_app[1]))
                return None

        self.stdout.write("""
CHECKING SQUARE INTEGRATION
---------------------------
            """)

        location_id = getattr(settings, 'SQUARE_LOCATION_ID', '')
        client_id = getattr(settings, 'SQUARE_APPLICATION_ID', '')
        client_secret = getattr(settings, 'SQUARE_ACCESS_TOKEN', '')

        if location_id:
            self.stdout.write('Square location ID set.')

        else:
            self.stdout.write(self.style.WARNING('Square location ID not set'))
            foundErrors = True

        if client_id:
            self.stdout.write('Square application ID set.')
        else:
            self.stdout.write(
                self.style.WARNING('Square application ID not set'))
            foundErrors = True

        if client_secret:
            self.stdout.write('Square access token set.')
        else:
            self.stdout.write(
                self.style.WARNING('Square access token not set.'))
            foundErrors = True

        if location_id and client_id and client_secret:
            try:
                from squareconnect.rest import ApiException
                from squareconnect.apis.locations_api import LocationsApi
                from squareconnect.apis.transactions_api import TransactionsApi

                locations_api_instance = LocationsApi()
                locations_api_instance.api_client.configuration.access_token = getattr(
                    settings, 'SQUARE_ACCESS_TOKEN', '')
                transactions_api_instance = TransactionsApi()
                transactions_api_instance.api_client.configuration.access_token = getattr(
                    settings, 'SQUARE_ACCESS_TOKEN', '')

                # Check that the location ID from settings actually identifies a location.
                api_response = locations_api_instance.list_locations()
                if api_response.errors:
                    self.stdout.write(
                        self.style.ERROR('Error in listing Locations: %s' %
                                         api_response.errors))
                    foundErrors = True
                if location_id not in [x.id for x in api_response.locations]:
                    self.stdout.write(
                        self.style.ERROR(
                            'Location ID from settings does not identify a valid Square Location.'
                        ))
                    foundErrors = True

                # Check that we can access transaction information
                api_response = transactions_api_instance.list_transactions(
                    location_id=location_id)
                if api_response.errors:
                    self.stdout.write(
                        self.style.ERROR('Error in listing Transactions: %s' %
                                         api_response.errors))
                    foundErrors = True
                else:
                    self.stdout.write(
                        self.style.SUCCESS(
                            'Successfully connected to Square API with provided credentials.'
                        ))
            except ImportError:
                self.stdout.write(
                    self.style.ERROR(
                        'Required squareconnect app not installed.'))
                foundErrors = True
            except ApiException as e:
                self.stdout.write(
                    self.style.ERROR('Exception in using Square API: %s\n' %
                                     e))
                foundErrors = True

        add_square_checkout = self.boolean_input(
            'Add Square Checkout form to the registration summary view to allow students to pay [Y/n]',
            True)
        if add_square_checkout:
            home_page = Page.objects.filter(is_home=True,
                                            publisher_is_draft=False).first()
            if not home_page:
                self.stdout.write(
                    self.style.ERROR(
                        'Cannot add Square Checkout form because a home page has not yet been set.'
                    ))
                foundErrors = True
            else:
                checkout_sp = StaticPlaceholder.objects.get_or_create(
                    code='registration_payment_placeholder')
                checkout_p_draft = checkout_sp[0].draft
                checkout_p_public = checkout_sp[0].public

                if checkout_p_public.get_plugins().filter(
                        plugin_type='SquareCheckoutFormPlugin').exists():
                    self.stdout.write('Square checkout form already present.')
                else:
                    add_plugin(
                        checkout_p_draft,
                        'SquareCheckoutFormPlugin',
                        initial_language,
                        successPage=home_page,
                    )
                    add_plugin(
                        checkout_p_public,
                        'SquareCheckoutFormPlugin',
                        initial_language,
                        successPage=home_page,
                    )
                    self.stdout.write('Square Checkout form added.')
                    self.stdout.write("""

Notes for Checkout integration
------------------------------

- In order for the Square checkout form to function on your
  website, you *must* be able to connect to the site using
  HTTPS, and the page on which your checkout form is included
  must be served over a secure connection via HTTPS.  Be sure
  that your server is set up to permit HTTPS connections, and that
  it automatically directs customers who are registering to
  an HTTPS connection.

- If you are running a development installation of the project
  on your local machine, then the above HTTPS requirement does
  not apply.  You will be able to see and test the checkout form
  on your local machine.

                        """)

        add_square_pos = self.boolean_input(
            'Add Square point-of-sale button to the registration summary view to allow students to pay [Y/n]',
            True)
        if add_square_pos:
            home_page = Page.objects.filter(is_home=True,
                                            publisher_is_draft=False).first()
            if not home_page:
                self.stdout.write(
                    self.style.ERROR(
                        'Cannot add Square point-of-sale button because a home page has not yet been set.'
                    ))
                foundErrors = True
            else:
                checkout_sp = StaticPlaceholder.objects.get_or_create(
                    code='registration_payment_placeholder')
                checkout_p_draft = checkout_sp[0].draft
                checkout_p_public = checkout_sp[0].public

                if checkout_p_public.get_plugins().filter(
                        plugin_type='SquarePointOfSalePlugin').exists():
                    self.stdout.write(
                        'Square point of sale button already present.')
                else:
                    add_plugin(
                        checkout_p_draft,
                        'SquarePointOfSalePlugin',
                        initial_language,
                        successPage=home_page,
                    )
                    add_plugin(
                        checkout_p_public,
                        'SquarePointOfSalePlugin',
                        initial_language,
                        successPage=home_page,
                    )
                    self.stdout.write('Square Checkout form added.')
                    self.stdout.write("""

Notes for point-of-sale integration
-----------------------------------

- Before using the Square point of sale button, you must log in
  and specify the URL on this project to which Square sends
  notifications of each transaction.  This callback URL that you specify
  *must* be a secure HTTPS URL, which means that your server must permit
  HTTPS connections.  To register your callback URL, complete the following
  steps:

    1. Log into the Square website at https://squareup.com/, go to your
    dashboard, and under "Apps > My Apps" select "Manage App" for the
    app whose credentials you have specified for this project.
    2. Under the "Point of Sale API" tab, look for the input box labeled
    "Web Callback URLs."  In that input box, enter the following URL:

    https://%s%s

    3. Click "Save" at the bottom of the page to save your change.

- If you need to test Square point-of-sale integration on a local
  installation, you will need your local installation to be served
  over HTTPS.  Consider a solution such as django-sslserver
  (https://github.com/teddziuba/django-sslserver) for testing purposes.
  Also, be advised that you may need to configure settings on your
  router and/or your computer's firewall in order to ensure that your
  local machine can receive HTTPS callbacks from Square.

- Prior to using the Square point-of-sale button, you must also
  install the Square point-of-sale app on your Android or iOS
  device, and log in using the account whose credentials you have
  specified in the project settings.  If you attempt to begin a point
  of sale transaction without logging into this account, your transaction
  will fail with an error.

                        """ % (Site.objects.get_current().domain,
                               reverse('processSquarePointOfSale')))
        if not foundErrors:
            self.stdout.write(self.style.SUCCESS('Square setup complete.'))
        else:
            self.stdout.write(
                self.style.ERROR(
                    'Square setup encountered errors.  Please see above for details.'
                ))
location_id = config.get(config_type, "location_id")

squareconnect.configuration.access_token = access_token

# The ID of the business location to associate processed payments with.
# See [Retrieve your business's locations]
# (https://docs.connect.squareup.com/articles/getting-started/#retrievemerchantprofile)
# for an easy way to get your business's location IDs.
# If you're testing things out, use a sandbox location ID.
if config.get("DEFAULT", "is_prod") == "true":
    location_id = config.get("PRODUCTION", "location_id")
else:
    location_id = config.get("SANDBOX", "location_id")
location_id = location_id

api_instance = TransactionsApi()

# Every payment you process with the SDK must have a unique idempotency key.
# If you're unsure whether a particular payment succeeded, you can reattempt
# it with the same idempotency key without worrying about double charging
# the buyer.
idempotency_key = str(uuid.uuid1())

# Monetary amounts are specified in the smallest unit of the applicable currency.
# This amount is in cents. It's also hard-coded for $1.00, which isn't very useful.
amount = {'amount': 100, 'currency': 'USD'}

# To learn more about splitting transactions with additional recipients,
# see the Transactions API documentation on our [developer site]
# (https://docs.connect.squareup.com/payments/transactions/overview#mpt-overview).
body = {
Esempio n. 11
0
def processSquarePayment(request):
    '''
    This view handles the charging of approved Square Checkout payments.

    All Checkout payments must either be associated with a pre-existing Invoice
    or a registration, or they must have an amount and type passed in the post data
    (such as gift certificate payment requests).
    '''
    logger.info('Received request for Square Checkout payment.')

    nonce_id = request.POST.get('nonce')
    invoice_id = request.POST.get('invoice_id')
    tr_id = request.POST.get('reg_id')
    amount = request.POST.get('amount')
    submissionUserId = request.POST.get('user_id')
    transactionType = request.POST.get('transaction_type')
    taxable = request.POST.get('taxable', False)
    sourceUrl = request.POST.get('sourceUrl', reverse('showRegSummary'))
    addSessionInfo = request.POST.get('addSessionInfo', False)
    successUrl = request.POST.get('successUrl', reverse('registration'))
    customerEmail = request.POST.get('customerEmail')

    # If a specific amount to pay has been passed, then allow payment
    # of that amount.
    if amount:
        try:
            amount = float(amount)
        except ValueError:
            logger.error('Invalid amount passed')
            messages.error(
                request,
                format_html(
                    '<p>{}</p><ul><li>{}</li></ul>',
                    str(
                        _('ERROR: Error with Square checkout transaction attempt.'
                          )), str(_('Invalid amount passed.'))))
            return HttpResponseRedirect(sourceUrl)

    # Parse if a specific submission user is indicated
    submissionUser = None
    if submissionUserId:
        try:
            submissionUser = User.objects.get(id=int(submissionUserId))
        except (ValueError, ObjectDoesNotExist):
            logger.warning(
                'Invalid user passed, submissionUser will not be recorded.')

    try:
        # Invoice transactions are usually payment on an existing invoice.
        if invoice_id:
            this_invoice = Invoice.objects.get(id=invoice_id)
            this_description = _('Invoice Payment: %s' % this_invoice.id)
            if not amount:
                amount = this_invoice.outstandingBalance
        # This is typical of payment at the time of registration
        elif tr_id:
            tr = TemporaryRegistration.objects.get(id=int(tr_id))
            tr.expirationDate = timezone.now() + timedelta(
                minutes=getConstant('registration__sessionExpiryMinutes'))
            tr.save()
            this_invoice = Invoice.get_or_create_from_registration(
                tr, submissionUser=submissionUser)
            this_description = _('Registration Payment: #%s' % tr_id)
            if not amount:
                amount = this_invoice.outstandingBalance
        # All other transactions require both a transaction type and an amount to be specified
        elif not transactionType or not amount:
            logger.error(
                'Insufficient information passed to createSquarePayment view.')
            messages.error(
                request,
                format_html(
                    '<p>{}</p><ul><li>{}</li></ul>',
                    str(
                        _('ERROR: Error with Square checkout transaction attempt.'
                          )),
                    str(
                        _('Insufficient information passed to createSquarePayment view.'
                          ))))
            return HttpResponseRedirect(sourceUrl)
        else:
            # Gift certificates automatically get a nicer invoice description
            if transactionType == 'Gift Certificate':
                this_description = _('Gift Certificate Purchase')
            else:
                this_description = transactionType
            this_invoice = Invoice.create_from_item(
                float(amount),
                this_description,
                submissionUser=submissionUser,
                calculate_taxes=(taxable is not False),
                transactionType=transactionType,
            )
    except (ValueError, ObjectDoesNotExist) as e:
        logger.error(
            'Invalid registration information passed to createSquarePayment view: (%s, %s, %s)'
            % (invoice_id, tr_id, amount))
        messages.error(
            request,
            format_html(
                '<p>{}</p><ul><li>{}</li></ul>',
                str(_(
                    'ERROR: Error with Square checkout transaction attempt.')),
                str(
                    _('Invalid registration information passed to createSquarePayment view: (%s, %s, %s)'
                      % (invoice_id, tr_id, amount)))))
        return HttpResponseRedirect(sourceUrl)

    this_currency = getConstant('general__currencyCode')
    this_total = min(this_invoice.outstandingBalance, amount)

    api_instance = TransactionsApi()
    api_instance.api_client.configuration.access_token = getattr(
        settings, 'SQUARE_ACCESS_TOKEN', '')
    idempotency_key = str(uuid.uuid1())
    location_id = getattr(settings, 'SQUARE_LOCATION_ID', '')
    amount = {'amount': int(100 * this_total), 'currency': this_currency}
    body = {
        'idempotency_key': idempotency_key,
        'card_nonce': nonce_id,
        'amount_money': amount
    }

    errors_list = []

    try:
        # Charge
        api_response = api_instance.charge(location_id, body)
        if api_response.errors:
            logger.error('Error in charging Square transaction: %s' %
                         api_response.errors)
            errors_list = api_response.errors
    except ApiException as e:
        logger.error('Exception when calling TransactionApi->charge: %s\n' % e)
        errors_list = json.loads(e.body).get('errors', [])

    if errors_list:
        this_invoice.status = Invoice.PaymentStatus.error
        this_invoice.save()
        errors_string = ''
        for err in errors_list:
            errors_string += '<li><strong>CODE:</strong> %s, %s</li>' % (
                err.get('code', str(
                    _('Unknown'))), err.get('detail', str(_('Unknown'))))
        messages.error(
            request,
            format_html(
                '<p>{}</p><ul>{}</ul>',
                str(_(
                    'ERROR: Error with Square checkout transaction attempt.')),
                mark_safe(errors_list),
            ))
        return HttpResponseRedirect(sourceUrl)
    else:
        logger.info('Square charge successfully created.')

    transaction = api_response.transaction

    paymentRecord = SquarePaymentRecord.objects.create(
        invoice=this_invoice,
        transactionId=transaction.id,
        locationId=transaction.location_id,
    )

    # We process the payment now, and enqueue the job to retrieve the
    # transaction again once fees have been calculated by Square
    this_invoice.processPayment(
        amount=this_total,
        fees=0,
        paidOnline=True,
        methodName='Square Checkout',
        methodTxn=transaction.id,
        notify=customerEmail,
    )
    updateSquareFees.schedule(args=(paymentRecord, ), delay=60)

    if addSessionInfo:
        paymentSession = request.session.get(INVOICE_VALIDATION_STR, {})

        paymentSession.update({
            'invoiceID': str(this_invoice.id),
            'amount': this_total,
            'successUrl': successUrl,
        })
        request.session[INVOICE_VALIDATION_STR] = paymentSession

    return HttpResponseRedirect(successUrl)
Esempio n. 12
0
def processPointOfSalePayment(request):
    '''
    This view handles the callbacks from point-of-sale transactions.
    Please note that this will only work if you have set up your callback
    URL in Square to point to this view.
    '''
    print('Request data is: %s' % request.GET)

    # iOS transactions put all response information in the data key:
    data = json.loads(request.GET.get('data', '{}'))
    if data:
        status = data.get('status')
        errorCode = data.get('error_code')
        errorDescription = errorCode

        try:
            stateData = data.get('state', '')
            if stateData:
                metadata = json.loads(
                    b64decode(unquote(stateData).encode()).decode())
            else:
                metadata = {}
        except (TypeError, ValueError, binascii.Error):
            logger.error('Invalid metadata passed from Square app.')
            messages.error(
                request,
                format_html(
                    '<p>{}</p><ul><li><strong>CODE:</strong> {}</li><li><strong>DESCRIPTION:</strong> {}</li></ul>',
                    str(
                        _('ERROR: Error with Square point of sale transaction attempt.'
                          )),
                    str(_('Invalid metadata passed from Square app.')),
                ))
            return HttpResponseRedirect(reverse('showRegSummary'))

        # This is the normal transaction identifier, which will be stored in the
        # database as a SquarePaymentRecord
        serverTransId = data.get('transaction_id')

        # This is the only identifier passed for non-card transactions.
        clientTransId = data.get('client_transaction_id')
    else:
        # Android transactions use this GET response syntax
        errorCode = request.GET.get('com.squareup.pos.ERROR_CODE')
        errorDescription = request.GET.get(
            'com.squareup.pos.ERROR_DESCRIPTION')
        status = 'ok' if not errorCode else 'error'

        # This is the normal transaction identifier, which will be stored in the
        # database as a SquarePaymentRecord
        serverTransId = request.GET.get(
            'com.squareup.pos.SERVER_TRANSACTION_ID')

        # This is the only identifier passed for non-card transactions.
        clientTransId = request.GET.get(
            'com.squareup.pos.CLIENT_TRANSACTION_ID')

        # Load the metadata, which includes the registration or invoice ids
        try:
            stateData = request.GET.get('com.squareup.pos.REQUEST_METADATA',
                                        '')
            if stateData:
                metadata = json.loads(
                    b64decode(unquote(stateData).encode()).decode())
            else:
                metadata = {}

        except (TypeError, ValueError, binascii.Error):
            logger.error('Invalid metadata passed from Square app.')
            messages.error(
                request,
                format_html(
                    '<p>{}</p><ul><li><strong>CODE:</strong> {}</li><li><strong>DESCRIPTION:</strong> {}</li></ul>',
                    str(
                        _('ERROR: Error with Square point of sale transaction attempt.'
                          )),
                    str(_('Invalid metadata passed from Square app.')),
                ))
            return HttpResponseRedirect(reverse('showRegSummary'))

    # Other things that can be passed in the metadata
    sourceUrl = metadata.get('sourceUrl', reverse('showRegSummary'))
    successUrl = metadata.get('successUrl', reverse('registration'))
    submissionUserId = metadata.get(
        'userId', getattr(getattr(request, 'user', None), 'id', None))
    transactionType = metadata.get('transaction_type')
    taxable = metadata.get('taxable', False)
    addSessionInfo = metadata.get('addSessionInfo', False)
    customerEmail = metadata.get('customerEmail')

    if errorCode or status != 'ok':
        # Return the user to their original page with the error message displayed.
        logger.error(
            'Error with Square point of sale transaction attempt.  CODE: %s; DESCRIPTION: %s'
            % (errorCode, errorDescription))
        messages.error(
            request,
            format_html(
                '<p>{}</p><ul><li><strong>CODE:</strong> {}</li><li><strong>DESCRIPTION:</strong> {}</li></ul>',
                str(
                    _('ERROR: Error with Square point of sale transaction attempt.'
                      )), errorCode, errorDescription))
        return HttpResponseRedirect(sourceUrl)

    api_instance = TransactionsApi()
    api_instance.api_client.configuration.access_token = getattr(
        settings, 'SQUARE_ACCESS_TOKEN', '')
    location_id = getattr(settings, 'SQUARE_LOCATION_ID', '')

    if serverTransId:
        try:
            api_response = api_instance.retrieve_transaction(
                transaction_id=serverTransId, location_id=location_id)
        except ApiException:
            logger.error('Unable to find Square transaction by server ID.')
            messages.error(
                request,
                _('ERROR: Unable to find Square transaction by server ID.'))
            return HttpResponseRedirect(sourceUrl)
        if api_response.errors:
            logger.error('Unable to find Square transaction by server ID: %s' %
                         api_response.errors)
            messages.error(
                request,
                str(_('ERROR: Unable to find Square transaction by server ID:')
                    ) + api_response.errors)
            return HttpResponseRedirect(sourceUrl)
        transaction = api_response.transaction
    elif clientTransId:
        # Try to find the transaction in the 50 most recent transactions
        try:
            api_response = api_instance.list_transactions(
                location_id=location_id)
        except ApiException:
            logger.error('Unable to find Square transaction by client ID.')
            messages.error(
                request,
                _('ERROR: Unable to find Square transaction by client ID.'))
            return HttpResponseRedirect(sourceUrl)
        if api_response.errors:
            logger.error('Unable to find Square transaction by client ID: %s' %
                         api_response.errors)
            messages.error(
                request,
                str(_('ERROR: Unable to find Square transaction by client ID:')
                    ) + api_response.errors)
            return HttpResponseRedirect(sourceUrl)
        transactions_list = [
            x for x in api_response.transactions
            if x.client_id == clientTransId
        ]
        if len(transactions_list) == 1:
            transaction = transactions_list[0]
        else:
            logger.error('Returned client transaction ID not found.')
            messages.error(
                request, _('ERROR: Returned client transaction ID not found.'))
            return HttpResponseRedirect(sourceUrl)
    else:
        logger.error(
            'An unknown error has occurred with Square point of sale transaction attempt.'
        )
        messages.error(
            request,
            _('ERROR: An unknown error has occurred with Square point of sale transaction attempt.'
              ))
        return HttpResponseRedirect(sourceUrl)

    # Get total information from the transaction for handling invoice.
    this_total = sum([x.amount_money.amount / 100 for x in transaction.tenders or []]) - \
        sum([x.amount_money.amount / 100 for x in transaction.refunds or []])

    # Parse if a specific submission user is indicated
    submissionUser = None
    if submissionUserId:
        try:
            submissionUser = User.objects.get(id=int(submissionUserId))
        except (ValueError, ObjectDoesNotExist):
            logger.warning(
                'Invalid user passed, submissionUser will not be recorded.')

    if 'registration' in metadata.keys():
        try:
            tr_id = int(metadata.get('registration'))
            tr = TemporaryRegistration.objects.get(id=tr_id)
        except (ValueError, TypeError, ObjectDoesNotExist):
            logger.error('Invalid registration ID passed: %s' %
                         metadata.get('registration'))
            messages.error(
                request,
                str(_('ERROR: Invalid registration ID passed')) +
                ': %s' % metadata.get('registration'))
            return HttpResponseRedirect(sourceUrl)

        tr.expirationDate = timezone.now() + timedelta(
            minutes=getConstant('registration__sessionExpiryMinutes'))
        tr.save()
        this_invoice = Invoice.get_or_create_from_registration(
            tr, submissionUser=submissionUser)
        this_description = _('Registration Payment: #%s' % tr_id)

    elif 'invoice' in metadata.keys():
        try:
            this_invoice = Invoice.objects.get(id=int(metadata.get('invoice')))
            this_description = _('Invoice Payment: %s' % this_invoice.id)

        except (ValueError, TypeError, ObjectDoesNotExist):
            logger.error('Invalid invoice ID passed: %s' %
                         metadata.get('invoice'))
            messages.error(
                request,
                str(_('ERROR: Invalid invoice ID passed')) +
                ': %s' % metadata.get('invoice'))
            return HttpResponseRedirect(sourceUrl)
    else:
        # Gift certificates automatically get a nicer invoice description
        if transactionType == 'Gift Certificate':
            this_description = _('Gift Certificate Purchase')
        else:
            this_description = transactionType
        this_invoice = Invoice.create_from_item(
            this_total,
            this_description,
            submissionUser=submissionUser,
            calculate_taxes=(taxable is not False),
            transactionType=transactionType,
        )

    paymentRecord, created = SquarePaymentRecord.objects.get_or_create(
        transactionId=transaction.id,
        locationId=transaction.location_id,
        defaults={
            'invoice': this_invoice,
        })
    if created:
        # We process the payment now, and enqueue the job to retrieve the
        # transaction again once fees have been calculated by Square
        this_invoice.processPayment(
            amount=this_total,
            fees=0,
            paidOnline=True,
            methodName='Square Point of Sale',
            methodTxn=transaction.id,
            notify=customerEmail,
        )
    updateSquareFees.schedule(args=(paymentRecord, ), delay=60)

    if addSessionInfo:
        paymentSession = request.session.get(INVOICE_VALIDATION_STR, {})

        paymentSession.update({
            'invoiceID': str(this_invoice.id),
            'amount': this_total,
            'successUrl': successUrl,
        })
        request.session[INVOICE_VALIDATION_STR] = paymentSession

    return HttpResponseRedirect(successUrl)
Esempio n. 13
0
    def handle(self, *args, **options):

        from cms.api import add_plugin
        from cms.models import Page, StaticPlaceholder

        foundErrors = False

        try:
            initial_language = settings.LANGUAGES[0][0]
        except IndexError:
            initial_language = getattr(settings, 'LANGUAGE_CODE', 'en')

        # Do some sanity checks to ensure that necessary apps are listed in INSTALLED_APPS
        # before proceeding
        required_apps = [
            ('cms', 'Django CMS'),
            ('danceschool.core', 'Core danceschool app'),
            ('danceschool.payments.square', 'Square integration app'),
        ]
        for this_app in required_apps:
            if not apps.is_installed(this_app[0]):
                self.stdout.write(self.style.ERROR('ERROR: %s is not installed or listed in INSTALLED_APPS. Please install before proceeding.' % this_app[1]))
                return None

        self.stdout.write(
            """
CHECKING SQUARE INTEGRATION
---------------------------
            """
        )

        location_id = getattr(settings,'SQUARE_LOCATION_ID','')
        client_id = getattr(settings,'SQUARE_APPLICATION_ID','')
        client_secret = getattr(settings,'SQUARE_ACCESS_TOKEN','')

        if location_id:
            self.stdout.write('Square location ID set.')

        else:
            self.stdout.write(self.style.WARNING('Square location ID not set'))
            foundErrors = True

        if client_id:
            self.stdout.write('Square application ID set.')
        else:
            self.stdout.write(self.style.WARNING('Square application ID not set'))
            foundErrors = True

        if client_secret:
            self.stdout.write('Square access token set.')
        else:
            self.stdout.write(self.style.WARNING('Square access token not set.'))
            foundErrors = True

        if location_id and client_id and client_secret:
            try:
                import squareconnect
                from squareconnect.rest import ApiException
                from squareconnect.apis.locations_api import LocationsApi
                from squareconnect.apis.transactions_api import TransactionsApi

                squareconnect.configuration.access_token = client_secret
                locations_api_instance = LocationsApi()
                transactions_api_instance = TransactionsApi()

                # Check that the location ID from settings actually identifies a location.
                api_response = locations_api_instance.list_locations()
                if api_response.errors:
                    self.stdout.write(self.style.ERROR('Error in listing Locations: %s' % api_response.errors))
                    foundErrors = True
                if location_id not in [x.id for x in api_response.locations]:
                    self.stdout.write(self.style.ERROR('Location ID from settings does not identify a valid Square Location.'))
                    foundErrors = True

                # Check that we can access transaction information
                api_response = transactions_api_instance.list_transactions(location_id=location_id)
                if api_response.errors:
                    self.stdout.write(self.style.ERROR('Error in listing Transactions: %s' % api_response.errors))
                    foundErrors = True
                else:
                    self.stdout.write(self.style.SUCCESS('Successfully connected to Square API with provided credentials.'))
            except ImportError:
                self.stdout.write(self.style.ERROR('Required squareconnect app not installed.'))
                foundErrors = True
            except ApiException as e:
                self.stdout.write(self.style.ERROR('Exception in using Square API: %s\n' % e))
                foundErrors = True

        add_square_checkout = self.boolean_input('Add Square Checkout form to the registration summary view to allow students to pay [Y/n]', True)
        if add_square_checkout:
            home_page = Page.objects.filter(is_home=True,publisher_public=True).first()
            if not home_page:
                self.stdout.write(self.style.ERROR('Cannot add Square Checkout form because a home page has not yet been set.'))
                foundErrors = True
            else:
                checkout_sp = StaticPlaceholder.objects.get_or_create(code='registration_payment_placeholder')
                checkout_p_draft = checkout_sp[0].draft
                checkout_p_public = checkout_sp[0].public

                if checkout_p_public.get_plugins().filter(plugin_type='SquareCheckoutFormPlugin').exists():
                    self.stdout.write('Square checkout form already present.')
                else:
                    add_plugin(
                        checkout_p_draft, 'SquareCheckoutFormPlugin', initial_language,
                        successPage=home_page,
                    )
                    add_plugin(
                        checkout_p_public, 'SquareCheckoutFormPlugin', initial_language,
                        successPage=home_page,
                    )
                    self.stdout.write('Square Checkout form added.')
                    self.stdout.write(
                        """

Notes for Checkout integration
------------------------------

- In order for the Square checkout form to function on your
  website, you *must* be able to connect to the site using
  HTTPS, and the page on which your checkout form is included
  must be served over a secure connection via HTTPS.  Be sure
  that your server is set up to permit HTTPS connections, and that
  it automatically directs customers who are registering to
  an HTTPS connection.

- If you are running a development installation of the project
  on your local machine, then the above HTTPS requirement does
  not apply.  You will be able to see and test the checkout form
  on your local machine.

                        """
                    )

        add_square_pos = self.boolean_input('Add Square point-of-sale button to the registration summary view to allow students to pay [Y/n]', True)
        if add_square_pos:
            home_page = Page.objects.filter(is_home=True,publisher_public=True).first()
            if not home_page:
                self.stdout.write(self.style.ERROR('Cannot add Square point-of-sale button because a home page has not yet been set.'))
                foundErrors = True
            else:
                checkout_sp = StaticPlaceholder.objects.get_or_create(code='registration_payment_placeholder')
                checkout_p_draft = checkout_sp[0].draft
                checkout_p_public = checkout_sp[0].public

                if checkout_p_public.get_plugins().filter(plugin_type='SquarePointOfSalePlugin').exists():
                    self.stdout.write('Square point of sale button already present.')
                else:
                    add_plugin(
                        checkout_p_draft, 'SquarePointOfSalePlugin', initial_language,
                        successPage=home_page,
                    )
                    add_plugin(
                        checkout_p_public, 'SquarePointOfSalePlugin', initial_language,
                        successPage=home_page,
                    )
                    self.stdout.write('Square Checkout form added.')
                    self.stdout.write(
                        """

Notes for point-of-sale integration
-----------------------------------

- Before using the Square point of sale button, you must log in
  and specify the URL on this project to which Square sends
  notifications of each transaction.  This callback URL that you specify
  *must* be a secure HTTPS URL, which means that your server must permit
  HTTPS connections.  To register your callback URL, complete the following
  steps:

    1. Log into the Square website at https://squareup.com/, go to your
    dashboard, and under "Apps > My Apps" select "Manage App" for the
    app whose credentials you have specified for this project.
    2. Under the "Point of Sale API" tab, look for the input box labeled
    "Web Callback URLs."  In that input box, enter the following URL:

    https://%s%s

    3. Click "Save" at the bottom of the page to save your change.

- If you need to test Square point-of-sale integration on a local
  installation, you will need your local installation to be served
  over HTTPS.  Consider a solution such as django-sslserver
  (https://github.com/teddziuba/django-sslserver) for testing purposes.
  Also, be advised that you may need to configure settings on your
  router and/or your computer's firewall in order to ensure that your
  local machine can receive HTTPS callbacks from Square.

- Prior to using the Square point-of-sale button, you must also
  install the Square point-of-sale app on your Android or iOS
  device, and log in using the account whose credentials you have
  specified in the project settings.  If you attempt to begin a point
  of sale transaction without logging into this account, your transaction
  will fail with an error.

                        """ % (Site.objects.get_current().domain,reverse('processSquarePointOfSale'))
                    )
        if not foundErrors:
            self.stdout.write(self.style.SUCCESS('Square setup complete.'))
        else:
            self.stdout.write(self.style.ERROR('Square setup encountered errors.  Please see above for details.'))
Esempio n. 14
0
def get_transactions_api():
    return TransactionsApi(_sqapi_inst)