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