Source code for debits.paypal.models

import json

import requests
from dateutil.relativedelta import relativedelta

from debits.debits_base.base import Period, period_to_delta

try:
    from html import escape  # python 3.x
except ImportError:
    from cgi import escape  # python 2.x
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from debits.debits_base.models import logger, CannotCancelSubscription, CannotRefund


[docs]class PayPalProcessorInfo(models.Model): class Meta: """Don't save it in the DB (use only for get_model())""" managed = False
[docs] def api(self): return PayPalAPI()
[docs] @staticmethod def offset_date(date, offset): """Used to calculate the next recurring payment date.""" delta = period_to_delta(offset) new_date = date + delta if offset.unit in (Period.UNIT_MONTHS, Period.UNIT_YEARS) and new_date.day != date.day: new_date += relativedelta(days=1) return new_date
[docs]class PayPalAPI(object): """PayPal API. This code only provides a subset of the possible functionality, for something more comprehensive see https://github.com/paypal/PayPal-Python-SDK To login into PayPal we use a Bearer from https://api.paypal.com/v1/oauth2/token with secret from https://developer.paypal.com/developer/applications""" def __init__(self): """Creates a HTTP session to access PayPal API.""" debug = settings.PAYPAL_DEBUG self.server = 'https://api.sandbox.paypal.com' if debug else 'https://api.paypal.com' s = requests.Session() s.headers.update({'Accept': 'application/json', 'Accept-Language': 'en_US'}) r = s.post(self.server + '/v1/oauth2/token', data='grant_type=client_credentials', headers={'content-type': 'application/x-www-form-urlencoded'}, auth=(settings.PAYPAL_CLIENT_ID, settings.PAYPAL_SECRET)) token = r.json()["access_token"] s.headers.update({'Authorization': 'Bearer '+token}) self.session = s
[docs] def cancel_agreement(self, agreement_id, is_upgrade=False): """Cancels a PayPal recurring payment.""" note = _("Upgrading billing plan") if is_upgrade else _("Canceling a service") # https://developer.paypal.com/docs/api/#agreement_cancel # https://developer.paypal.com/docs/api/payments.billing-agreements#agreement_cancel logger.debug("PayPal: now canceling agreement %s" % escape(agreement_id)) r = self.session.post(self.server + ('/v1/payments/billing-agreements/%s/cancel' % escape(agreement_id)), data='{"note": "%s"}' % note, headers={'content-type': 'application/json'}) if r.status_code < 200 or r.status_code >= 300: # PayPal returns 204, to be sure # Don't include secret information into the message raise CannotCancelSubscription(r.json()["message"])
# raise RuntimeError(_("Cannot cancel a billing agreement at PayPal. Please contact support:\n" + r.json()["message"]))
[docs] def refund(self, transaction_id, sum=None, currency='USD'): """Refunds a PayPal payment.""" logger.debug("PayPal: now refunding transaction %s" % escape(transaction_id)) data = {} if sum is not None: data['amount'] = {'total': sum, 'currency': currency} r = self.session.post(self.server + ('/v1/payments/sale/%s/refund' % escape(transaction_id)), data=json.dumps(data), headers = {'content-type': 'application/json'}) if r.status_code < 200 or r.status_code >= 300: # PayPal returns 204, to be sure # Don't include secret information into the message raise CannotRefund(r.json()["message"])
# raise RuntimeError(_("Cannot cancel a billing agreement at PayPal. Please contact support:\n" + r.json()["message"])) # It does not work with PayPal subscriptions: https://www.paypal-knowledge.com/infocenter/index?page=content&id=FAQ1987&actp=LIST # def agreement_is_active(self, agreement_id): # r = self.session.get(self.server + ('/v1/payments/billing-agreements/%s' % escape(agreement_id)), # headers={'content-type': 'application/json'}) # # ...