Advertisement
Guest User

Untitled

a guest
Apr 26th, 2017
162
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 20.32 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from __future__ import unicode_literals
  4.  
  5. from warnings import warn
  6.  
  7. from django.conf import settings
  8. from django.db import models
  9. from django.utils.functional import cached_property
  10.  
  11. from paypal.standard.conf import POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT
  12. from paypal.standard.helpers import check_secret, duplicate_txn_id
  13. from paypal.utils import warn_untested
  14.  
  15. ST_PP_ACTIVE = 'Active'
  16. ST_PP_CANCELLED = 'Cancelled'
  17. ST_PP_CANCELED_REVERSAL = 'Canceled_Reversal'
  18. ST_PP_CLEARED = 'Cleared'
  19. ST_PP_COMPLETED = 'Completed'
  20. ST_PP_CREATED = 'Created'
  21. ST_PP_DECLINED = 'Declined'
  22. ST_PP_DENIED = 'Denied'
  23. ST_PP_EXPIRED = 'Expired'
  24. ST_PP_FAILED = 'Failed'
  25. ST_PP_PAID = 'Paid'
  26. ST_PP_PENDING = 'Pending'
  27. ST_PP_PROCESSED = 'Processed'
  28. ST_PP_REFUNDED = 'Refunded'
  29. ST_PP_REFUSED = 'Refused'
  30. ST_PP_REVERSED = 'Reversed'
  31. ST_PP_REWARDED = 'Rewarded'
  32. ST_PP_UNCLAIMED = 'Unclaimed'
  33. ST_PP_UNCLEARED = 'Uncleared'
  34. ST_PP_VOIDED = 'Voided'
  35.  
  36. try:
  37.     from idmapper.models import SharedMemoryModel as Model
  38. except ImportError:
  39.     Model = models.Model
  40.  
  41.  
  42. DEFAULT_ENCODING = 'windows-1252'  # PayPal seems to normally use this.
  43.  
  44.  
  45. class PayPalStandardBase(Model):
  46.     """Base class for common variables shared by IPN and PDT"""
  47.     # See https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables/
  48.  
  49.     # @@@ Might want to add all these one distant day.
  50.     # FLAG_CODE_CHOICES = (
  51.     # PAYMENT_STATUS_CHOICES = "Canceled_ Reversal Completed Denied Expired " \
  52.     #                          "Failed Pending Processed Refunded Reversed Voided".split()
  53.     PAYMENT_STATUS_CHOICES = [ST_PP_ACTIVE,
  54.                               ST_PP_CANCELLED,
  55.                               ST_PP_CANCELED_REVERSAL,
  56.                               ST_PP_CLEARED,
  57.                               ST_PP_COMPLETED,
  58.                               ST_PP_CREATED,
  59.                               ST_PP_DECLINED,
  60.                               ST_PP_DENIED,
  61.                               ST_PP_EXPIRED,
  62.                               ST_PP_FAILED,
  63.                               ST_PP_PAID,
  64.                               ST_PP_PENDING,
  65.                               ST_PP_PROCESSED,
  66.                               ST_PP_REFUNDED,
  67.                               ST_PP_REFUSED,
  68.                               ST_PP_REVERSED,
  69.                               ST_PP_REWARDED,
  70.                               ST_PP_UNCLAIMED,
  71.                               ST_PP_UNCLEARED,
  72.                               ST_PP_VOIDED,
  73.                               ]
  74.     # AUTH_STATUS_CHOICES = "Completed Pending Voided".split()
  75.     # ADDRESS_STATUS_CHOICES = "confirmed unconfirmed".split()
  76.     # PAYER_STATUS_CHOICES = "verified / unverified".split()
  77.     # PAYMENT_TYPE_CHOICES =  "echeck / instant.split()
  78.     # PENDING_REASON = "address authorization echeck intl multi-currency unilateral upgrade verify other".split()
  79.     # REASON_CODE = "chargeback guarantee buyer_complaint refund other".split()
  80.     # TRANSACTION_ENTITY_CHOICES = "auth reauth order payment".split()
  81.  
  82.     # Transaction and Notification-Related Variables
  83.     business = models.CharField(max_length=127, blank=True, help_text="Email where the money was sent.")
  84.     charset = models.CharField(max_length=255, blank=True)
  85.     custom = models.CharField(max_length=256, blank=True)
  86.     notify_version = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  87.     parent_txn_id = models.CharField("Parent Transaction ID", max_length=19, blank=True)
  88.     receiver_email = models.EmailField(max_length=254, blank=True)
  89.     receiver_id = models.CharField(max_length=255, blank=True)  # 258DLEHY2BDK6
  90.     residence_country = models.CharField(max_length=2, blank=True)
  91.     test_ipn = models.BooleanField(default=False, blank=True)
  92.     txn_id = models.CharField("Transaction ID", max_length=255, blank=True, help_text="PayPal transaction ID.",
  93.                               db_index=True)
  94.     txn_type = models.CharField("Transaction Type", max_length=255, blank=True, help_text="PayPal transaction type.")
  95.     verify_sign = models.CharField(max_length=255, blank=True)
  96.  
  97.     # Buyer Information Variables
  98.     address_country = models.CharField(max_length=64, blank=True)
  99.     address_city = models.CharField(max_length=40, blank=True)
  100.     address_country_code = models.CharField(max_length=64, blank=True, help_text="ISO 3166")
  101.     address_name = models.CharField(max_length=128, blank=True)
  102.     address_state = models.CharField(max_length=40, blank=True)
  103.     address_status = models.CharField(max_length=255, blank=True)
  104.     address_street = models.CharField(max_length=200, blank=True)
  105.     address_zip = models.CharField(max_length=20, blank=True)
  106.     contact_phone = models.CharField(max_length=20, blank=True)
  107.     first_name = models.CharField(max_length=64, blank=True)
  108.     last_name = models.CharField(max_length=64, blank=True)
  109.     payer_business_name = models.CharField(max_length=127, blank=True)
  110.     payer_email = models.CharField(max_length=127, blank=True)
  111.     payer_id = models.CharField(max_length=13, blank=True)
  112.  
  113.     # Payment Information Variables
  114.     auth_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  115.     auth_exp = models.CharField(max_length=28, blank=True)
  116.     auth_id = models.CharField(max_length=19, blank=True)
  117.     auth_status = models.CharField(max_length=255, blank=True)
  118.     exchange_rate = models.DecimalField(max_digits=64, decimal_places=16, default=0, blank=True, null=True)
  119.     invoice = models.CharField(max_length=127, blank=True)
  120.     item_name = models.CharField(max_length=127, blank=True)
  121.     item_number = models.CharField(max_length=127, blank=True)
  122.     mc_currency = models.CharField(max_length=32, default="USD", blank=True)
  123.     mc_fee = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  124.     mc_gross = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  125.     mc_handling = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  126.     mc_shipping = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  127.     memo = models.CharField(max_length=255, blank=True)
  128.     num_cart_items = models.IntegerField(blank=True, default=0, null=True)
  129.     option_name1 = models.CharField(max_length=64, blank=True)
  130.     option_name2 = models.CharField(max_length=64, blank=True)
  131.     option_selection1 = models.CharField(max_length=200, blank=True)
  132.     option_selection2 = models.CharField(max_length=200, blank=True)
  133.     payer_status = models.CharField(max_length=255, blank=True)
  134.     payment_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
  135.     payment_gross = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  136.     payment_status = models.CharField(max_length=255, blank=True)
  137.     payment_type = models.CharField(max_length=255, blank=True)
  138.     pending_reason = models.CharField(max_length=255, blank=True)
  139.     protection_eligibility = models.CharField(max_length=255, blank=True)
  140.     quantity = models.IntegerField(blank=True, default=1, null=True)
  141.     reason_code = models.CharField(max_length=255, blank=True)
  142.     remaining_settle = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  143.     settle_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  144.     settle_currency = models.CharField(max_length=32, blank=True)
  145.     shipping = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  146.     shipping_method = models.CharField(max_length=255, blank=True)
  147.     tax = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  148.     transaction_entity = models.CharField(max_length=255, blank=True)
  149.  
  150.     # Auction Variables
  151.     auction_buyer_id = models.CharField(max_length=64, blank=True)
  152.     auction_closing_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
  153.     auction_multi_item = models.IntegerField(blank=True, default=0, null=True)
  154.     for_auction = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  155.  
  156.     # Recurring Payments Variables
  157.     amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  158.     amount_per_cycle = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  159.     initial_payment_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  160.     next_payment_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
  161.     outstanding_balance = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  162.     payment_cycle = models.CharField(max_length=255, blank=True)  # Monthly
  163.     period_type = models.CharField(max_length=255, blank=True)
  164.     product_name = models.CharField(max_length=255, blank=True)
  165.     product_type = models.CharField(max_length=255, blank=True)
  166.     profile_status = models.CharField(max_length=255, blank=True)
  167.     recurring_payment_id = models.CharField(max_length=255, blank=True)  # I-FA4XVST722B9
  168.     rp_invoice_id = models.CharField(max_length=127, blank=True)  # 1335-7816-2936-1451
  169.     time_created = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
  170.  
  171.     # Subscription Variables
  172.     amount1 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  173.     amount2 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  174.     amount3 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  175.     mc_amount1 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  176.     mc_amount2 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  177.     mc_amount3 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  178.     password = models.CharField(max_length=24, blank=True)
  179.     period1 = models.CharField(max_length=255, blank=True)
  180.     period2 = models.CharField(max_length=255, blank=True)
  181.     period3 = models.CharField(max_length=255, blank=True)
  182.     reattempt = models.CharField(max_length=1, blank=True)
  183.     recur_times = models.IntegerField(blank=True, default=0, null=True)
  184.     recurring = models.CharField(max_length=1, blank=True)
  185.     retry_at = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
  186.     subscr_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
  187.     subscr_effective = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
  188.     subscr_id = models.CharField(max_length=19, blank=True)
  189.     username = models.CharField(max_length=64, blank=True)
  190.  
  191.     # Billing Agreement Variables
  192.     mp_id = models.CharField(max_length=128, blank=True, null=True)  # B-0G433009BJ555711U
  193.  
  194.     # Dispute Resolution Variables
  195.     case_creation_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
  196.     case_id = models.CharField(max_length=255, blank=True)
  197.     case_type = models.CharField(max_length=255, blank=True)
  198.  
  199.     # Variables not categorized
  200.     receipt_id = models.CharField(max_length=255, blank=True)  # 1335-7816-2936-1451
  201.     currency_code = models.CharField(max_length=32, default="USD", blank=True)
  202.     handling_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  203.  
  204.     # This undocumented variable apparently contains the same as the 'custom'
  205.     # field - http://stackoverflow.com/questions/8464442/set-transaction-subject-paypal-ipn
  206.     transaction_subject = models.CharField(max_length=256, blank=True)
  207.  
  208.     # @@@ Mass Pay Variables (Not Implemented, needs a separate model, for each transaction x)
  209.     # fraud_managment_pending_filters_x = models.CharField(max_length=255, blank=True)
  210.     # option_selection1_x = models.CharField(max_length=200, blank=True)
  211.     # option_selection2_x = models.CharField(max_length=200, blank=True)
  212.     # masspay_txn_id_x = models.CharField(max_length=19, blank=True)
  213.     # mc_currency_x = models.CharField(max_length=32, default="USD", blank=True)
  214.     # mc_fee_x = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  215.     # mc_gross_x = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  216.     # mc_handlingx = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
  217.     # payment_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
  218.     # payment_status = models.CharField(max_length=9, blank=True)
  219.     # reason_code = models.CharField(max_length=15, blank=True)
  220.     # receiver_email_x = models.EmailField(max_length=127, blank=True)
  221.     # status_x = models.CharField(max_length=9, blank=True)
  222.     # unique_id_x = models.CharField(max_length=13, blank=True)
  223.  
  224.     # Non-PayPal Variables - full IPN/PDT query and time fields.
  225.     ipaddress = models.GenericIPAddressField(blank=True, null=True)
  226.     flag = models.BooleanField(default=False, blank=True)
  227.     flag_code = models.CharField(max_length=16, blank=True)
  228.     flag_info = models.TextField(blank=True)
  229.     query = models.TextField(blank=True)  # What Paypal sent to us initially
  230.     response = models.TextField(blank=True)  # What we got back from our request
  231.     created_at = models.DateTimeField(auto_now_add=True)
  232.     updated_at = models.DateTimeField(auto_now=True)
  233.  
  234.     # Where did it come from?
  235.     from_view = models.CharField(max_length=6, null=True, blank=True)
  236.  
  237.     class Meta:
  238.         abstract = True
  239.         app_label = 'paypal_standard_base'  # Keep Django 1.7 quiet
  240.  
  241.     def __unicode__(self):
  242.         if self.is_transaction():
  243.             return self.format % ("Transaction", self.txn_id)
  244.         elif self.is_subscription():
  245.             return self.format % ("Subscription", self.subscr_id)
  246.         else:
  247.             return self.format % ("Recurring", self.recurring_payment_id)
  248.  
  249.     @cached_property
  250.     def posted_data_dict(self):
  251.         """
  252.        All the data that PayPal posted to us, as a correctly parsed dictionary of values.
  253.        """
  254.         if not self.query:
  255.             return None
  256.         from django.http import QueryDict
  257.         roughdecode = dict(item.split('=', 1) for item in self.query.split('&'))
  258.         encoding = roughdecode.get('charset', None)
  259.         if encoding is None:
  260.             encoding = DEFAULT_ENCODING
  261.         query = self.query.encode('ascii')
  262.         data = QueryDict(query, encoding=encoding)
  263.         return data.dict()
  264.  
  265.     def is_transaction(self):
  266.         try:
  267.             return len(self.txn_id) > 0
  268.         except TypeError, err:
  269.             return False
  270.  
  271.     def is_refund(self):
  272.         return self.payment_status == ST_PP_REFUNDED
  273.  
  274.     def is_reversed(self):
  275.         return self.payment_status == ST_PP_REVERSED
  276.  
  277.     def is_recurring(self):
  278.         try:
  279.             return len(self.recurring_payment_id) > 0
  280.         except TypeError, err:
  281.             return False
  282.  
  283.     def is_subscription(self):
  284.         warn_untested()
  285.         try:
  286.             return len(self.subscr_id) > 0
  287.         except TypeError, err:
  288.             return False
  289.  
  290.     def is_subscription_payment(self):
  291.         warn_untested()
  292.         return self.txn_type == "subscr_payment"
  293.  
  294.     def is_subscription_failed(self):
  295.         warn_untested()
  296.         return self.txn_type == "subscr_failed"
  297.  
  298.     def is_subscription_cancellation(self):
  299.         warn_untested()
  300.         return self.txn_type == "subscr_cancel"
  301.  
  302.     def is_subscription_end_of_term(self):
  303.         warn_untested()
  304.         return self.txn_type == "subscr_eot"
  305.  
  306.     def is_subscription_modified(self):
  307.         warn_untested()
  308.         return self.txn_type == "subscr_modify"
  309.  
  310.     def is_subscription_signup(self):
  311.         warn_untested()
  312.         return self.txn_type == "subscr_signup"
  313.  
  314.     def is_recurring_create(self):
  315.         return self.txn_type == "recurring_payment_profile_created"
  316.  
  317.     def is_recurring_payment(self):
  318.         return self.txn_type == "recurring_payment"
  319.  
  320.     def is_recurring_cancel(self):
  321.         return self.txn_type == "recurring_payment_profile_cancel"
  322.  
  323.     def is_recurring_skipped(self):
  324.         return self.txn_type == "recurring_payment_skipped"
  325.  
  326.     def is_recurring_failed(self):
  327.         return self.txn_type == "recurring_payment_failed"
  328.  
  329.     def is_recurring_suspended(self):
  330.         warn_untested()
  331.         return self.txn_type == "recurring_payment_suspended"
  332.  
  333.     def is_recurring_suspended_due_to_max_failed_payment(self):
  334.         warn_untested()
  335.         return self.txn_type == "recurring_payment_suspended_due_to_max_failed_payment"
  336.  
  337.     def is_billing_agreement(self):
  338.         warn_untested()
  339.         try:
  340.             return len(self.mp_id) > 0
  341.         except TypeError, err:
  342.             return False
  343.  
  344.     def is_billing_agreement_create(self):
  345.         warn_untested()
  346.         return self.txn_type == "mp_signup"
  347.  
  348.     def is_billing_agreement_cancel(self):
  349.         warn_untested()
  350.         return self.txn_type == "mp_cancel"
  351.  
  352.     def set_flag(self, info, code=None):
  353.         """Sets a flag on the transaction and also sets a reason."""
  354.         self.flag = True
  355.         self.flag_info += info
  356.         if code is not None:
  357.             warn_untested()
  358.             self.flag_code = code
  359.  
  360.     def clear_flag(self):
  361.         self.flag = False
  362.         self.flag_info = ""
  363.         self.flag_code = ""
  364.  
  365.     def verify(self):
  366.         """
  367.        Verifies an IPN and a PDT.
  368.        Checks for obvious signs of weirdness in the payment and flags appropriately.
  369.        """
  370.         self.response = self._postback().decode('ascii')
  371.         self.clear_flag()
  372.         self._verify_postback()
  373.         if not self.flag:
  374.             if self.is_transaction():
  375.                 if self.payment_status not in self.PAYMENT_STATUS_CHOICES:
  376.                     self.set_flag("Invalid payment_status. (%s)" % self.payment_status)
  377.                 if duplicate_txn_id(self):
  378.                     self.set_flag("Duplicate txn_id. (%s)" % self.txn_id)
  379.                 if hasattr(settings, 'PAYPAL_RECEIVER_EMAIL'):
  380.                     warn("Use of PAYPAL_RECEIVER_EMAIL in settings has been Deprecated.\n"
  381.                          "Check of valid email must be done when receiving the\n"
  382.                          "valid_ipn_received signal",
  383.                          DeprecationWarning)
  384.                     if self.receiver_email != settings.PAYPAL_RECEIVER_EMAIL:
  385.                         self.set_flag("Invalid receiver_email. (%s)" % self.receiver_email)
  386.             else:
  387.                 # @@@ Run a different series of checks on recurring payments.
  388.                 pass
  389.  
  390.         self.save()
  391.  
  392.     def verify_secret(self, form_instance, secret):
  393.         """Verifies an IPN payment over SSL using EWP."""
  394.         warn_untested()
  395.         if not check_secret(form_instance, secret):
  396.             self.set_flag("Invalid secret. (%s)") % secret
  397.         self.save()
  398.  
  399.     def get_endpoint(self):
  400.         """Set Sandbox endpoint if the test variable is present."""
  401.         if self.test_ipn:
  402.             return SANDBOX_POSTBACK_ENDPOINT
  403.         else:
  404.             return POSTBACK_ENDPOINT
  405.  
  406.     def send_signals(self):
  407.         """Shout for the world to hear whether a txn was successful."""
  408.         raise NotImplementedError
  409.  
  410.     def initialize(self, request):
  411.         """Store the data we'll need to make the postback from the request object."""
  412.         if request.method == 'GET':
  413.             # PDT only - this data is currently unused
  414.             self.query = request.META.get('QUERY_STRING', '')
  415.         elif request.method == 'POST':
  416.             # The following works if paypal sends an ASCII bytestring, which it does.
  417.             self.query = request.body.decode('ascii')
  418.         self.ipaddress = request.META.get('REMOTE_ADDR', '')
  419.  
  420.     def _postback(self):
  421.         """Perform postback to PayPal and store the response in self.response."""
  422.         raise NotImplementedError
  423.  
  424.     def _verify_postback(self):
  425.         """Check self.response is valid andcall self.set_flag if there is an error."""
  426.         raise NotImplementedError
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement