Ejemplo n.º 1
0
    def charge_recurring(self, grace_period=None):
        """
        Charge a cart's recurring item, if necessary.
        NOTE: Currently only one recurring item is supported per cart,
              so charge the first one found.
        We use braintree's subscriptions for recurring billing, so we don't manually
        charge recurring payments. Instead, we poll braintree to get new
        payments/transactions.
        """
        if not grace_period:
            grace_period = self.settings.get("CHARGE_RECURRING_GRACE_PERIOD",
                                             None)
        recurring = [
            li for li in self.cart.recurring_lineitems if li.is_active
        ]
        if not recurring or not recurring[0].is_expired(
                grace_period=grace_period):
            return
        item = recurring[0]

        handler = BraintreeIPN(self.cart)
        result = braintree.Subscription.find(item.payment_token)
        transactions = result.transactions
        for t in transactions:
            handler.accept_payment(t)
Ejemplo n.º 2
0
def update_payment_status(hiicart_id, transaction_id, tries=0, cart_class=HiiCart):
    """Check the payment status of a Braintree transaction."""
    if transaction_id is None:
        return
    hiicart = cart_class.objects.get(pk=hiicart_id)
    handler = BraintreeIPN(hiicart)
    done = handler.update_order_status(transaction_id)
    # Reschedule the failed payment to run in 4 hours
    if not done:
        # After 18 tries (72 hours) we will void and fail the payment
        if tries >= 18:
            handler.void_order(transaction_id)
        else:
            tries = tries + 1
            update_payment_status.apply_async(args=[hiicart_id, transaction_id, tries, cart_class], countdown=14400)
Ejemplo n.º 3
0
def update_payment_status(hiicart_id,
                          transaction_id,
                          tries=0,
                          cart_class=HiiCart):
    """Check the payment status of a Braintree transaction."""
    hiicart = cart_class.objects.get(pk=hiicart_id)
    handler = BraintreeIPN(hiicart)
    done = handler.update_order_status(transaction_id)
    # Reschedule the failed payment to run in 4 hours
    if not done:
        # After 18 tries (72 hours) we will void and fail the payment
        if tries >= 18:
            handler.void_order(transaction_id)
        else:
            tries = tries + 1
            update_payment_status.apply_async(
                args=[hiicart_id, transaction_id, tries, cart_class],
                countdown=14400)
Ejemplo n.º 4
0
    def charge_recurring(self, grace_period=None):
        """
        Charge a cart's recurring item, if necessary.
        NOTE: Currently only one recurring item is supported per cart,
              so charge the first one found.
        We use braintree's subscriptions for recurring billing, so we don't manually
        charge recurring payments. Instead, we poll braintree to get new
        payments/transactions.
        """
        if not grace_period:
            grace_period = self.settings.get("CHARGE_RECURRING_GRACE_PERIOD", None)
        recurring = [li for li in self.cart.recurring_lineitems if li.is_active]
        if not recurring or not recurring[0].is_expired(grace_period=grace_period):
            return
        item = recurring[0]

        handler = BraintreeIPN(self.cart)
        result = braintree.Subscription.find(item.payment_token)
        transactions = result.transactions
        for t in transactions:
            handler.accept_payment(t)
Ejemplo n.º 5
0
class BraintreeGateway(PaymentGatewayBase):
    """Payment Gateway for Braintree."""
    def __init__(self, cart):
        super(BraintreeGateway, self).__init__("braintree", cart,
                                               default_settings)
        self._require_settings(
            ["MERCHANT_ID", "MERCHANT_KEY", "MERCHANT_PRIVATE_KEY"])
        braintree.Configuration.configure(
            self.environment, self.settings["MERCHANT_ID"],
            self.settings["MERCHANT_KEY"],
            self.settings["MERCHANT_PRIVATE_KEY"])

    def _is_valid(self):
        """Return True if gateway is valid."""
        # TODO: Query Braintree to validate credentials
        return True

    @property
    def is_recurring(self):
        return len(self.cart.recurring_lineitems) > 0

    @property
    def environment(self):
        """Determine which Braintree environment to use."""
        if self.settings["LIVE"]:
            return braintree.Environment.Production
        else:
            return braintree.Environment.Sandbox

    def submit(self,
               collect_address=False,
               cart_settings_kwargs=None,
               submit=False):
        """
        Simply returns the gateway type to let the frontend know how to proceed.
        """
        return SubmitResult("direct")

    @property
    def form(self):
        """Returns an instance of PaymentForm."""
        return make_form(self.is_recurring)()

    def start_transaction(self, request):
        """
        Submits transaction details to Braintree and returns form data.
        If we're processing a one-time sale, submit the transaction for settlement
        right away. Otherwise, if we're starting a subscription, create the credit
        card on Braintree and create a subscription with the payment method token
        after confirmation.
        """
        redirect_url = request.build_absolute_uri(request.path)
        if self.is_recurring:
            tr_data = braintree.Customer.tr_data_for_create(
                {
                    'customer': {
                        'credit_card': {
                            'options': {
                                'verify_card': True
                            }
                        }
                    }
                }, redirect_url)
        else:
            tr_data = braintree.Transaction.tr_data_for_sale(
                {
                    "transaction": {
                        "type": "sale",
                        "order_id": self.cart.cart_uuid,
                        "amount": self.cart.total,
                        "options": {
                            "submit_for_settlement": True
                        }
                    }
                }, redirect_url)
        return tr_data

    def confirm_payment(self, request, gateway_dict=None):
        """
        Confirms payment result with Braintree.

        This method should be called after the Braintree transaction redirect
        to determine the payment result. It expects the request to contain the
        query string coming back from Braintree.
        """
        result_class = SubscriptionResult if self.is_recurring else TransactionResult

        try:
            result = braintree.TransparentRedirect.confirm(
                request.META['QUERY_STRING'])
        except Exception, e:
            errors = {'non_field_errors': 'Request to payment gateway failed.'}
            return result_class(transaction_id=None,
                                success=False,
                                status=None,
                                errors=errors,
                                gateway_result=None)

        if result.is_success:
            handler = BraintreeIPN(self.cart)
            if self.is_recurring:
                credit_card = result.customer.credit_cards[0]
                create_result = handler.create_subscription(
                    credit_card, gateway_dict=gateway_dict)
                transaction_id = None
                status = 'success'
                created = create_result.success
                gateway_result = create_result.gateway_result
            else:
                created = handler.new_order(result.transaction)
                transaction_id = result.transaction.id
                status = result.transaction.status
                gateway_result = result
            if created:
                return result_class(transaction_id=transaction_id,
                                    success=True,
                                    status=status,
                                    gateway_result=gateway_result)

        errors = {}
        transaction_id = None
        status = None
        obj = getattr(result, 'transaction', None) or getattr(
            result, 'credit_card_verification', None)
        if obj:
            transaction_id = getattr(obj, 'id', None)
            status = getattr(obj, 'status', None)
            if status == 'processor_declined':
                errors = {
                    'non_field_errors':
                    getattr(
                        obj, 'processor_response_text',
                        'There was an error communicating with the gateway')
                }
            elif status == 'gateway_rejected':
                errors = {
                    'non_field_errors':
                    getattr(obj, 'gateway_rejection_reason',
                            'The card was declined')
                }
        else:
            message = getattr(result, 'message', None)
            errors = {'non_field_errors': message}
            if result.errors:
                for error in result.errors.deep_errors:
                    errors[error.attribute] = error.message

        return result_class(transaction_id=transaction_id,
                            success=False,
                            status=status,
                            errors=errors,
                            gateway_result=result)