PageRenderTime 57ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/pypal/service/adaptive_payment.py

https://github.com/birknilson/pypal
Python | 225 lines | 202 code | 15 blank | 8 comment | 5 complexity | b6d90e550c1a929bb3d6b96e33696feb MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. import logging
  3. from pypal import currency
  4. from pypal.util import check_required, set_nonempty_param
  5. PRODUCTION_ENDPOINT = 'https://svcs.paypal.com'
  6. SANDBOX_ENDPOINT = 'https://svcs.sandbox.paypal.com'
  7. ACTION_PAY = 'PAY'
  8. ACTION_CREATE = 'CREATE'
  9. ACTION_PAY_PRIMARY = 'PAY_PRIMARY'
  10. SUPPORTED_PAY_ACTIONS = frozenset([ACTION_PAY,
  11. ACTION_CREATE,
  12. ACTION_PAY_PRIMARY])
  13. FEE_PAYER_SENDER = 'SENDER'
  14. FEE_PAYER_PRIMARY_RECEIVER = 'PRIMARYRECEIVER'
  15. FEE_PAYER_EACH_RECEIVER = 'EACHRECEIVER'
  16. FEE_PAYER_SECONDARY_RECEIVERS = 'SECONDARYONLY'
  17. SUPPORTED_FEE_PAYERS = frozenset([FEE_PAYER_SENDER,
  18. FEE_PAYER_PRIMARY_RECEIVER,
  19. FEE_PAYER_EACH_RECEIVER,
  20. FEE_PAYER_SECONDARY_RECEIVERS])
  21. EXECUTE_STATUS_CREATED = 'CREATED'
  22. EXECUTE_STATUS_COMPLETED = 'COMPLETED'
  23. EXECUTE_STATUS_INCOMPLETE = 'INCOMPLETE'
  24. EXECUTE_STATUS_ERROR = 'ERROR'
  25. EXECUTE_STATUS_REVERSAL_ERROR = 'REVERSALERROR'
  26. EXECUTE_STATUS_PROCESSING = 'PROCESSING'
  27. EXECUTE_STATUS_PENDING = 'PENDING'
  28. ##############################################################################
  29. # FUNCTIONS WHICH FURTHER AIDS IMPLEMENTATION OF THIS SERVICE
  30. ##############################################################################
  31. class ReceiverList(list):
  32. """An extension of the native list type which ensures all contained items
  33. are dictionaries - containing the necessary arguments needed per receiver.
  34. """
  35. def __init__(self, iterable):
  36. if iterable:
  37. self.extend(iterable)
  38. def append(self, obj):
  39. email = obj.get('email', None)
  40. amount = obj.get('amount', None)
  41. if not email and not amount:
  42. return False
  43. sanitized = {'email': obj.get('email'),
  44. 'amount': obj.get('amount'),
  45. 'primary': obj.get('primary', 'false')}
  46. super(type(self), self).append(sanitized)
  47. def extend(self, iterable):
  48. for obj in iterable:
  49. self.append(obj)
  50. def call(client, method, params):
  51. """A wrapper of the ``'pypal.Client.call'`` method which
  52. will set the API endpoints for this service depending
  53. on the environment, i.e sandbox or not.
  54. :param client: An instance of ``'pypal.Client'``
  55. :param method: The API method to execute
  56. :param params: The arguments to send
  57. """
  58. endpoint = (PRODUCTION_ENDPOINT, SANDBOX_ENDPOINT)
  59. endpoint = endpoint[int(client.config.in_sandbox)]
  60. return client.call('AdaptivePayments', method,
  61. endpoint=endpoint, **params)
  62. def get_payment_url(client,
  63. action_type,
  64. currency_code,
  65. cancel_url,
  66. return_url,
  67. ipn_callback_url=None,
  68. receivers=None,
  69. fees_payer=None,
  70. extra={},
  71. embedded=False):
  72. """Executes the Pay API call and returns the intended redirect URL
  73. directly using the necessary pay key returned in the PayPal response.
  74. This function is a wrapper of ``'pay'`` which will execute the necessary
  75. API calls and using the response this function will generate the URL.
  76. """
  77. response = pay(**locals())
  78. if not response.success:
  79. return None
  80. pay_key = response.get('payKey', None)
  81. if not pay_key:
  82. return None
  83. return generate_pay_url(client, pay_key, embedded=embedded)
  84. def generate_pay_url(client, pay_key, embedded=False):
  85. """Retrieves the pay key associated with prepared payment procedures and
  86. generates the intended URL to redirect end-users in order to finialize
  87. payments.
  88. :param client: An instance of ``'pypal.Client'``
  89. :param pay_key: The payment token received from PayPal
  90. :param embedded: Whether or not to generate an url which
  91. is intended for an embedded payment experience.
  92. """
  93. paths = ('/cgi-bin/webscr?cmd=_ap-payment&paykey=%s',
  94. '/webapps/adaptivepayment/flow/pay?paykey=%s')
  95. return client.get_paypal_url(paths[embedded] % pay_key)
  96. ##############################################################################
  97. # FUNCTIONS WHICH DIRECTLY CORRESPONDS TO PAYPAL API CALLS
  98. ##############################################################################
  99. def pay(client,
  100. action_type,
  101. currency_code,
  102. cancel_url,
  103. return_url,
  104. ipn_callback_url,
  105. receivers=None,
  106. fees_payer=None,
  107. extra={}):
  108. """Execute the Pay API call which will prepare the payment procedure.
  109. Most importantly it will return a pay key which should be utilized in
  110. order to identify the transaction.
  111. :param client: An instance of ``'pypal.Client'``
  112. :param action_type: The payment action type
  113. :param currency_code: Which currency code to utilize in the transaction
  114. :param cancel_url: The URL which the end-user is sent to on
  115. payment cancellation.
  116. :param return_url: The URL which the end-user is sent to on completed
  117. payment, in all cases be it success or failure.
  118. :param ipn_callback_url: The URL which will receive the IPN notifications
  119. related to this operation. The Adaptive Payment API
  120. requires this to be explicitly set and does not
  121. fallback on the default IPN URL for the
  122. application.
  123. :param receivers: A list of the receivers of this transaction
  124. :param fees_payer: Who will pay the PayPal fees
  125. :param extra: Additional key-value arguments to send to PayPal
  126. """
  127. check_required(locals(), ('cancel_url', 'return_url', 'currency_code',
  128. 'action_type', 'receivers', 'ipn_callback_url'))
  129. if not currency.is_valid_code(currency_code):
  130. raise ValueError('Given currency code (%s) '
  131. 'is not supported' % currency_code)
  132. if action_type not in SUPPORTED_PAY_ACTIONS:
  133. raise ValueError('Given payment action (%s) is not any of the '
  134. 'supported types; %s' % (action_type,
  135. SUPPORTED_PAY_ACTIONS))
  136. if fees_payer and fees_payer not in SUPPORTED_FEE_PAYERS:
  137. raise ValueError('Given value (%s) for the fees_payer argument '
  138. 'is not supported by PayPal' % fees_payer)
  139. if not isinstance(receivers, ReceiverList):
  140. if not isinstance(receivers, (list, tuple)):
  141. receivers = [receivers]
  142. receivers = ReceiverList(receivers)
  143. extra.update({'actionType': action_type,
  144. 'receiverList': { 'receiver': receivers },
  145. 'currencyCode': currency_code,
  146. 'cancelUrl': cancel_url,
  147. 'returnUrl': return_url})
  148. set_nonempty_param(extra, 'ipnNotificationUrl', ipn_callback_url)
  149. set_nonempty_param(extra, 'feesPayer', fees_payer)
  150. return call(client, 'Pay', extra)
  151. def get_payment_options(client, pay_key):
  152. return call(client, 'GetPaymentOptions', {'payKey': pay_key})
  153. def set_payment_options(client,
  154. pay_key,
  155. receiver_options,
  156. display_options=None,
  157. sender_options=None,
  158. shipping_address_id=None,
  159. initiating_entity=None,
  160. extra={}):
  161. """Execute the SetPaymentOptions API call which will customize
  162. behavior of the payment procedure at PayPal.
  163. :param client: An instance of ``'pypal.Client'``
  164. :param pay_key: The PayPal token associated with the transaction
  165. :param sender_options: Dictionary containing sender customizations
  166. :param receiver_options: Dictionary containing receiver customizations
  167. :param display_options: Dictionary containing display customizations
  168. :param shipping_address_id: The PayPal identifier for the shipping
  169. address to set.
  170. :param initiating_entity: Dictionary containing initiating entity
  171. customizations.
  172. :param extra: Additional key-value arguments to send to PayPal
  173. """
  174. extra['payKey'] = pay_key
  175. set_nonempty_param(extra, 'initiatingEntity', initiating_entity)
  176. set_nonempty_param(extra, 'displayOptions', display_options)
  177. set_nonempty_param(extra, 'shippingAddressId', shipping_address_id)
  178. set_nonempty_param(extra, 'senderOptions', sender_options)
  179. set_nonempty_param(extra, 'receiverOptions', receiver_options)
  180. return call(client, 'SetPaymentOptions', extra)
  181. def execute(client, pay_key):
  182. return call(client, 'ExecutePayment', {'payKey': pay_key})
  183. def get_shipping_addresses(client, key):
  184. """Execute the GetShippingAddresses API call which will retrieve
  185. the shipping address which was set by the buyer.
  186. :param token: Either a payment or preapproval key
  187. """
  188. return call(client, 'GetShippingAddresses', {'key': key})