diff --git a/paypal/standard/conf.py b/paypal/standard/conf.py index 5c5fd45..979504f 100644 --- a/paypal/standard/conf.py +++ b/paypal/standard/conf.py @@ -15,7 +15,7 @@ class PayPalSettingsError(Exception): SANDBOX_POSTBACK_ENDPOINT = "https://www.sandbox.paypal.com/cgi-bin/webscr" # Images -IMAGE = getattr(settings, "PAYPAL_IMAGE", "http://images.paypal.com/images/x-click-but01.gif") +IMAGE = getattr(settings, "PAYPAL_IMAGE", "https://images.paypal.com/images/x-click-but01.gif") SUBSCRIPTION_IMAGE = getattr(settings, "PAYPAL_SUBSCRIPTION_IMAGE", "https://www.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif") DONATION_IMAGE = getattr(settings, "PAYPAL_DONATION_IMAGE", "https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif") SANDBOX_IMAGE = getattr(settings, "PAYPAL_SANDBOX_IMAGE", "https://www.sandbox.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif") diff --git a/paypal/standard/forms.py b/paypal/standard/forms.py index e9992a5..5912c01 100644 --- a/paypal/standard/forms.py +++ b/paypal/standard/forms.py @@ -99,6 +99,10 @@ def __init__(self, button_type="buy", *args, **kwargs): super(PayPalPaymentsForm, self).__init__(*args, **kwargs) self.button_type = button_type + def smart_render(self): + """Render for PayPal if not PAYPAL_DEBUG, for PayPay Sandbox if PAYPAL_DEBUG.""" + return self.render() if not settings.PAYPAL_DEBUG else self.sandbox() + def render(self): return mark_safe(u"""
%s @@ -217,3 +221,8 @@ class PayPalStandardBaseForm(forms.ModelForm): next_payment_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT) subscr_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT) subscr_effective = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT) + auction_closing_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT) + case_creation_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT) + # format not documented by PayPal, but empirically consistent + retry_at = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT) + diff --git a/paypal/standard/ipn/admin.py b/paypal/standard/ipn/admin.py index 173c97c..ae50081 100644 --- a/paypal/standard/ipn/admin.py +++ b/paypal/standard/ipn/admin.py @@ -63,7 +63,7 @@ class PayPalIPNAdmin(admin.ModelAdmin): "__unicode__", "flag", "flag_info", "invoice", "custom", "payment_status", "created_at" ] - search_fields = ["txn_id", "recurring_payment_id"] + search_fields = ["txn_id", "recurring_payment_id", "subscr_id"] -admin.site.register(PayPalIPN, PayPalIPNAdmin) \ No newline at end of file +admin.site.register(PayPalIPN, PayPalIPNAdmin) diff --git a/paypal/standard/ipn/models.py b/paypal/standard/ipn/models.py index 9495463..0236c47 100644 --- a/paypal/standard/ipn/models.py +++ b/paypal/standard/ipn/models.py @@ -42,7 +42,7 @@ def send_signals(self): recurring_skipped.send(sender=self) elif self.is_recurring_failed(): recurring_failed.send(sender=self) - # Subscription signals: + # Subscription signals: else: if self.is_subscription_cancellation(): subscription_cancel.send(sender=self) @@ -51,4 +51,6 @@ def send_signals(self): elif self.is_subscription_end_of_term(): subscription_eot.send(sender=self) elif self.is_subscription_modified(): - subscription_modify.send(sender=self) \ No newline at end of file + subscription_modify.send(sender=self) + elif self.is_subscription_failed(): + subscription_failed.send(sender=self) diff --git a/paypal/standard/ipn/signals.py b/paypal/standard/ipn/signals.py index c5aa14b..603e511 100644 --- a/paypal/standard/ipn/signals.py +++ b/paypal/standard/ipn/signals.py @@ -24,6 +24,9 @@ # Sent when a subscription is created. subscription_signup = Signal() +# Sent when a subscription's payment fails +subscription_failed = Signal() + # recurring_payment_profile_created recurring_create = Signal() @@ -34,4 +37,4 @@ recurring_skipped = Signal() -recurring_failed = Signal() \ No newline at end of file +recurring_failed = Signal() diff --git a/paypal/standard/models.py b/paypal/standard/models.py index c07e2fd..e3ab5e8 100644 --- a/paypal/standard/models.py +++ b/paypal/standard/models.py @@ -211,6 +211,9 @@ def is_subscription_modified(self): def is_subscription_signup(self): return self.txn_type == "subscr_signup" + def is_subscription_failed(self): + return self.txn_type == "subscr_failed" + def is_recurring_create(self): return self.txn_type == "recurring_payment_profile_created" diff --git a/paypal/standard/nvp/__init__.py b/paypal/standard/nvp/__init__.py new file mode 100644 index 0000000..c2c3074 --- /dev/null +++ b/paypal/standard/nvp/__init__.py @@ -0,0 +1,64 @@ +# Partial implementation of the NameValuePair interface of PayPal + +# currently allows: +# * change status of subscription/recurring payment + +import urllib +import urllib2 +import urlparse + +from django.conf import settings + +base_params = { + 'USER': settings.PAYPAL_API_USERNAME, + 'PWD': settings.PAYPAL_API_PASSWORD, + 'SIGNATURE': settings.PAYPAL_API_SIGNATURE, + 'VERSION': '60.0' + } + + + +class PayPalResponse: + def __init__(self, raw_data): + self.raw = raw_data + self.parse() + + def parse(self): + self.params = urlparse.parse_qs(self.raw) + + +class RecurringSubscription: + def __init__(self, profileid): + self.profileid = profileid + + def __unicode__(self): + return str(self.profileid) + + def __str__(self): + return self.__unicode__() + + def updateStatus(self, new_status): + """Update the status of the recurring payment profile. + + new_status is in { Cancel, Suspend, Reactivate }.""" + + if new_status not in ('Cancel', 'Suspend', 'Reactivate'): + raise ValueError("Invalid value '%s' for new_status. Must be in Cancel, Suspend, Reactivate." % new_status) + + pars = { + 'METHOD': 'ManageRecurringPaymentsProfileStatus', + 'PROFILEID': self.profileid, + 'ACTION': new_status + } + + ok, params = self.issue_cmd(pars) + return ok + + def issue_cmd(self, parameters): + parameters = dict(base_params.items() + parameters.items()) + data = urllib.urlencode(parameters) + req = urllib2.Request(settings.PAYPAL_API_NVP_ENDPOINT, data, {}) + resp = urllib2.urlopen(req).read() + resp_params = urlparse.parse_qs(resp) + return all([x.lower() == 'success' for x in resp_params['ACK']]), resp_params +