예제 #1
0
def process_paypal_payment(cd_donation_form):
    # https://developer.paypal.com/webapps/developer/docs/integration/web/accept-paypal-payment/
    access_token = get_paypal_access_token()
    if not access_token:
        raise PaymentFailureException('NO_ACCESS_TOKEN')

    data = {
        'intent':
        'sale',
        'redirect_urls': {
            'return_url': settings.PAYPAL_CALLBACK,
            'cancel_url': settings.PAYPAL_CANCELLATION,
        },
        'payer': {
            'payment_method': 'paypal'
        },
        'transactions': [{
            'amount': {
                'total': cd_donation_form['amount'],
                'currency': 'USD',
            },
            'description': 'Donation to Free Law Project',
        }]
    }
    r = requests.post('%s/v1/payments/payment' % settings.PAYPAL_ENDPOINT,
                      headers={
                          'Content-Type': 'application/json',
                          'Authorization': 'Bearer %s' % access_token
                      },
                      data=json.dumps(data))

    if r.status_code == 201:  # "Created"
        r_content_as_dict = json.loads(r.content)
        # Get the redirect value from the 'links' attribute. Links look like:
        #   [{u'href': u'https://api.sandbox.paypal.com/v1/payments/payment/PAY-8BC403022U6413151KIQPC2I',
        #     u'method': u'GET',
        #     u'rel': u'self'},
        #    {u'href': u'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-6VV58324J9479725S',
        #     u'method': u'REDIRECT',
        #     u'rel': u'approval_url'},
        #    {u'href': u'https://api.sandbox.paypal.com/v1/payments/payment/PAY-8BC403022U6413151KIQPC2I/execute',
        #     u'method': u'POST',
        #     u'rel': u'execute'}
        #   ]
        redirect = [
            link for link in r_content_as_dict['links']
            if link['rel'].lower() == 'approval_url'
        ][0]['href']
        parsed_redirect = urlparse(redirect)
        token = parse_qs(parsed_redirect.query)['token'][0]
        response = {
            'status': Donation.AWAITING_PAYMENT,
            'payment_id': r_content_as_dict.get('id'),
            'transaction_id': token,
            'redirect': redirect,
        }
        logger.info("Created payment in paypal with response: %s", response)
        return response
    else:
        raise PaymentFailureException("UNABLE_TO_MAKE_PAYMENT")
예제 #2
0
def create_stripe_customer(source: str, email: str) -> StripeObject:
    """Create a stripe customer so that we can charge this person more than
    once

    :param source: The stripe token to use for the creation
    :param email: The customer's email address
    :return: A stripe customer object
    """
    stripe.api_key = settings.STRIPE_SECRET_KEY
    try:
        return stripe.Customer.create(source=source, email=email)
    except (stripe.error.CardError, stripe.error.InvalidRequestError) as e:
        logger.warning("Stripe was unable to create the customer: %s" % e)
        message = (
            "Oops, we had an error with your donation: "
            "<strong>%s</strong>" % e.json_body["error"]["message"]
        )
        raise PaymentFailureException(message)
    except APIConnectionError:
        logger.warning("Unable to connect to stripe to create customer.")
        raise PaymentFailureException(
            "Oops. We were unable to connect to our payment provider. "
            "Please try again. If this error continues, please try again "
            "later."
        )
예제 #3
0
def route_and_process_payment(
    request,
    cd_donation_form,
    cd_user_form,
    payment_provider,
    frequency,
    stripe_redirect_url,
    payment_type,
):
    """Routes the donation to the correct payment provider, then normalizes
    its response.


    :param request: The WSGI request from Django
    :param cd_donation_form: The donation form with cleaned data
    :param cd_user_form: The user form with cleaned data
    :param payment_provider: The payment provider for the payment
    :param frequency: Whether monthly or one-time payment/donation
    :param stripe_redirect_url: Where to redirect a stripe payment after
    success
    :param payment_type: Whether it's a donation or payment

    Returns a dict with:
     - message: Any error messages that apply
     - status: The status of the payment for the database
     - payment_id: The ID of the payment
    """
    customer = None
    if payment_provider == PROVIDERS.PAYPAL:
        response = process_paypal_payment(cd_donation_form)
    elif payment_provider == PROVIDERS.CREDIT_CARD:
        stripe_token = request.POST.get("stripeToken")
        stripe_args = {"metadata": {"type": payment_type}}
        if frequency == FREQUENCIES.ONCE:
            stripe_args["card"] = stripe_token
        elif frequency == FREQUENCIES.MONTHLY:
            customer = create_stripe_customer(stripe_token,
                                              cd_user_form["email"])
            stripe_args["customer"] = customer.id
            stripe_args["metadata"].update({"recurring": True})
        else:
            raise NotImplementedError("Unknown frequency value: %s" %
                                      frequency)

        if cd_donation_form["reference"]:
            stripe_args["metadata"].update(
                {"reference": cd_donation_form["reference"]})

        # Calculate the amount in cents
        amount = int(float(cd_donation_form["amount"]) * 100)
        response = process_stripe_payment(amount, cd_user_form["email"],
                                          stripe_args, stripe_redirect_url)
    else:
        raise PaymentFailureException("Unknown/unhandled payment provider.")

    return response, customer
예제 #4
0
def create_stripe_customer(source, email):
    """Create a stripe customer so that we can charge this person more than
    once
    """
    stripe.api_key = settings.STRIPE_SECRET_KEY
    try:
        return stripe.Customer.create(source=source, email=email)
    except (stripe.error.CardError, stripe.error.InvalidRequestError) as e:
        logger.warn("Stripe was unable to create the customer: %s" % e)
        message = ('Oops, we had an error with your donation: '
                   '<strong>%s</strong>' % e.json_body['error']['message'])
        raise PaymentFailureException(message)
예제 #5
0
def process_stripe_payment(
    amount: int,
    email: str,
    kwargs: Dict[str, Union[str, bool, Dict[str, str]]],
    stripe_redirect_url: str,
) -> Dict[str, Union[str, int]]:
    """Process a stripe payment.

    :param amount: The amount, in pennies, that you wish to charge
    :param email: The email address of the person being charged
    :param kwargs: Keyword arguments to pass to Stripe's `create` method. Some
    functioning options for this dict are:

        {'card': stripe_token}

    And:

        {'customer': customer.id}

    Where stripe_token is a token returned by Stripe's client-side JS library,
    and customer is an object returned by stripe's customer creation server-
    side library.

    :param stripe_redirect_url: Where to send the user after a successful
    transaction
    :return: response object with information about whether the transaction
    succeeded.
    """
    stripe.api_key = settings.STRIPE_SECRET_KEY

    # Create the charge on Stripe's servers
    try:
        charge = stripe.Charge.create(
            amount=amount, currency="usd", description=email, **kwargs
        )
        response = {
            "status": Donation.AWAITING_PAYMENT,
            "payment_id": charge.id,
            "redirect": stripe_redirect_url,
        }
    except (stripe.error.CardError, stripe.error.InvalidRequestError) as e:
        logger.info("Stripe was unable to process the payment: %s" % e)
        message = (
            "Oops, we had an error with your donation: "
            "<strong>%s</strong>" % e.json_body["error"]["message"]
        )
        raise PaymentFailureException(message)

    return response
예제 #6
0
def process_stripe_payment(amount, email, kwargs):
    """
    Process a stripe payment.

    :param amount: The amount, in pennies, that you wish to charge
    :param email: The email address of the person being charged
    :param kwargs: Keyword arguments to pass to Stripe's `create` method. Some
    functioning options for this dict are:

        {'card': stripe_token}

    And:

        {'customer': customer.id}

    Where stripe_token is a token returned by Stripe's client-side JS library,
    and customer is an object returned by stripe's customer creation server-
    side library.

    :return: response object with information about whether the transaction
    succeeded.
    """
    stripe.api_key = settings.STRIPE_SECRET_KEY

    # Create the charge on Stripe's servers
    try:
        charge = stripe.Charge.create(amount=amount,
                                      currency="usd",
                                      description=email,
                                      **kwargs)
        response = {
            'status': Donation.AWAITING_PAYMENT,
            'payment_id': charge.id,
            'redirect': reverse('stripe_complete'),
        }
    except (stripe.error.CardError, stripe.error.InvalidRequestError) as e:
        logger.warn("Stripe was unable to process the payment: %s" % e)
        message = ('Oops, we had an error with your donation: '
                   '<strong>%s</strong>' % e.json_body['error']['message'])
        raise PaymentFailureException(message)

    return response
예제 #7
0
def get_paypal_access_token() -> str:
    """Get a token for the PayPal API.

    Query the PayPal API to get an access token. This could be improved by
    caching the token and detecting when it is expired.
    """
    r = requests.post(
        "%s/v1/oauth2/token" % settings.PAYPAL_ENDPOINT,
        headers={
            "Accept": "application/json",
            "Accept-Language": "en_US"
        },
        auth=(settings.PAYPAL_CLIENT_ID, settings.PAYPAL_SECRET_KEY),
        data={"grant_type": "client_credentials"},
    )
    if r.status_code == HTTP_200_OK:
        logger.info("Got paypal token successfully.")
    else:
        logger.warning("Problem getting paypal token status_code was: %s, "
                       "with content: %s" % (r.status_code, r.text))
        raise PaymentFailureException(
            "Oops, sorry. PayPal had an error. Please try again.")
    return json.loads(r.content).get("access_token")
예제 #8
0
def process_paypal_payment(
    cd_donation_form: CleanedDonationFormType,
) -> Dict[str, str]:
    # https://developer.paypal.com/webapps/developer/docs/integration/web/accept-paypal-payment/
    access_token = get_paypal_access_token()
    if not access_token:
        raise PaymentFailureException("NO_ACCESS_TOKEN")

    return_url = "https://www.courtlistener.com%s" % reverse("paypal_callback")
    cancel_url = "https://www.courtlistener.com%s" % reverse("paypal_cancel")
    data = {
        "intent": "sale",
        "redirect_urls": {"return_url": return_url, "cancel_url": cancel_url},
        "payer": {"payment_method": "paypal"},
        "transactions": [
            {
                "amount": {
                    "total": cd_donation_form["amount"],
                    "currency": "USD",
                },
                "description": "Donation to Free Law Project",
            }
        ],
    }
    r = requests.post(
        "%s/v1/payments/payment" % settings.PAYPAL_ENDPOINT,
        headers={
            "Content-Type": "application/json",
            "Authorization": "Bearer %s" % access_token,
        },
        data=json.dumps(data),
        timeout=30,
    )

    if r.status_code == HTTP_201_CREATED:
        r_content_as_dict = json.loads(r.content)
        # Get the redirect value from the 'links' attribute. Links look like:
        #   [{u'href': u'https://api.sandbox.paypal.com/v1/payments/payment/PAY-8BC403022U6413151KIQPC2I',
        #     u'method': u'GET',
        #     u'rel': u'self'},
        #    {u'href': u'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-6VV58324J9479725S',
        #     u'method': u'REDIRECT',
        #     u'rel': u'approval_url'},
        #    {u'href': u'https://api.sandbox.paypal.com/v1/payments/payment/PAY-8BC403022U6413151KIQPC2I/execute',
        #     u'method': u'POST',
        #     u'rel': u'execute'}
        #   ]
        redirect = [
            link
            for link in r_content_as_dict["links"]
            if link["rel"].lower() == "approval_url"
        ][0]["href"]
        parsed_redirect = urlparse(redirect)
        token = parse_qs(parsed_redirect.query)["token"][0]
        response = {
            "status": Donation.AWAITING_PAYMENT,
            "payment_id": r_content_as_dict.get("id"),
            "transaction_id": token,
            "redirect": redirect,
        }
        logger.info("Created payment in paypal with response: %s", response)
        return response
    else:
        raise PaymentFailureException("UNABLE_TO_MAKE_PAYMENT")