def authorize(self, amount, reference, token=None, opaque_data=None): """ Authorize (without capture) a payment for the given amount. :param float amount: The amount to pay :param str reference: The "invoiceNumber" in Authorize.net backend :param recordset token: The token of the payment method to charge, as a `payment.token` record :param dict opaque_data: The payment details obfuscated by Authorize.Net :return: a dict containing the response code, transaction id and transaction type :rtype: dict """ tx_data = self._prepare_tx_data(token=token, opaque_data=opaque_data) response = self._make_request( 'createTransactionRequest', { 'transactionRequest': { 'transactionType': 'authOnlyTransaction', 'amount': str(amount), **tx_data, 'order': { 'invoiceNumber': reference[:20], 'description': reference[:255], }, 'customerIP': payment_utils.get_customer_ip_address(), } }) return self._format_response(response, 'auth_only')
def _prepare_authorization_transaction_request(self, transaction_type, tx_data, amount, reference): return { 'transactionRequest': { 'transactionType': transaction_type, 'amount': str(amount), **tx_data, 'order': { 'invoiceNumber': reference[:20], 'description': reference[:255], }, 'customerIP': payment_utils.get_customer_ip_address(), } }
def _send_payment_request(self): """ Override of payment to send a payment request to Adyen. Note: self.ensure_one() :return: None :raise: UserError if the transaction is not linked to a token """ super()._send_payment_request() if self.provider != 'adyen': return # Make the payment request to Adyen if not self.token_id: raise UserError("Adyen: " + _("The transaction is not linked to a token.")) converted_amount = payment_utils.to_minor_currency_units( self.amount, self.currency_id, CURRENCY_DECIMALS.get(self.currency_id.name) ) data = { 'merchantAccount': self.acquirer_id.adyen_merchant_account, 'amount': { 'value': converted_amount, 'currency': self.currency_id.name, }, 'reference': self.reference, 'paymentMethod': { 'recurringDetailReference': self.token_id.acquirer_ref, }, 'shopperReference': self.token_id.adyen_shopper_reference, 'recurringProcessingModel': 'Subscription', 'shopperIP': payment_utils.get_customer_ip_address(), 'shopperInteraction': 'ContAuth', } response_content = self.acquirer_id._adyen_make_request( url_field_name='adyen_checkout_api_url', endpoint='/payments', payload=data, method='POST' ) # Handle the payment request response _logger.info( "payment request response for transaction with reference %s:\n%s", self.reference, pprint.pformat(response_content) ) self._handle_feedback_data('adyen', response_content)
def _prepare_authorization_transaction_request(self, transaction_type, tx_data, tx): # The billTo parameter is required for new ACH transactions (transactions without a payment.token), # but is not allowed for transactions with a payment.token. bill_to = {} if 'profile' not in tx_data: split_name = payment_utils.split_partner_name(tx.partner_name) partner_name = (tx.partner_name or "")[:50] # max length defined by the Authorize API bill_to = { 'billTo': { 'firstName': '' if tx.partner_id.is_company else split_name[0], 'lastName': split_name[1], # lastName is always required 'company': partner_name if tx.partner_id.is_company else '', 'address': tx.partner_address, 'city': tx.partner_city, 'state': tx.partner_state_id.name or '', 'zip': tx.partner_zip, 'country': tx.partner_country_id.name or '', } } # These keys have to be in the order defined in # https://apitest.authorize.net/xml/v1/schema/AnetApiSchema.xsd return { 'transactionRequest': { 'transactionType': transaction_type, 'amount': str(tx.amount), **tx_data, 'order': { 'invoiceNumber': tx.reference[:20], 'description': tx.reference[:255], }, 'customer': { 'email': tx.partner_email or '', }, **bill_to, 'customerIP': payment_utils.get_customer_ip_address(), } }
def _send_payment_request(self): """ Override of payment to send a payment request to Flutterwave. Note: self.ensure_one() :return: None :raise UserError: If the transaction is not linked to a token. """ super()._send_payment_request() if self.provider != 'flutterwave': return # Prepare the payment request to Flutterwave. if not self.token_id: raise UserError("Flutterwave: " + _("The transaction is not linked to a token.")) first_name, last_name = payment_utils.split_partner_name( self.partner_name) data = { 'token': self.token_id.acquirer_ref, 'email': self.token_id.flutterwave_customer_email, 'amount': self.amount, 'currency': self.currency_id.name, 'country': self.company_id.country_id.code, 'tx_ref': self.reference, 'first_name': first_name, 'last_name': last_name, 'ip': payment_utils.get_customer_ip_address(), } # Make the payment request to Flutterwave. response_content = self.acquirer_id._flutterwave_make_request( 'tokenized-charges', payload=data) # Handle the payment request response. _logger.info( "payment request response for transaction with reference %s:\n%s", self.reference, pprint.pformat(response_content)) self._handle_notification_data('flutterwave', response_content['data'])
def auth_and_capture(self, amount, reference, token=None, opaque_data=None): """Authorize and capture a payment for the given amount. Authorize and immediately capture a payment for the given payment.token record for the specified amount with reference as communication. :param str amount: transaction amount (up to 15 digits with decimal point) :param str reference: used as "invoiceNumber" in the Authorize.net backend :param record token: the payment.token record that must be charged :param str opaque_data: the transaction opaque_data obtained from Authorize.net :return: a dict containing the response code, transaction id and transaction type :rtype: dict """ tx_data = self._prepare_tx_data(token=token, opaque_data=opaque_data) response = self._make_request( 'createTransactionRequest', { 'transactionRequest': { 'transactionType': 'authCaptureTransaction', 'amount': str(amount), **tx_data, 'order': { 'invoiceNumber': reference[:20], 'description': reference[:255], }, 'customerIP': payment_utils.get_customer_ip_address(), } }) result = self._format_response(response, 'auth_capture') errors = response.get('transactionResponse', {}).get('errors') if errors: result['x_response_reason_text'] = '\n'.join( [e.get('errorText') for e in errors]) return result
def adyen_payments(self, acquirer_id, reference, converted_amount, currency_id, partner_id, payment_method, access_token, browser_info=None): """ Make a payment request and process the feedback data. :param int acquirer_id: The acquirer handling the transaction, as a `payment.acquirer` id :param str reference: The reference of the transaction :param int converted_amount: The amount of the transaction in minor units of the currency :param int currency_id: The currency of the transaction, as a `res.currency` id :param int partner_id: The partner making the transaction, as a `res.partner` id :param dict payment_method: The details of the payment method used for the transaction :param str access_token: The access token used to verify the provided values :param dict browser_info: The browser info to pass to Adyen :return: The JSON-formatted content of the response :rtype: dict """ # Check that the transaction details have not been altered. This allows preventing users # from validating transactions by paying less than agreed upon. if not payment_utils.check_access_token(access_token, reference, converted_amount, partner_id): raise ValidationError("Adyen: " + _("Received tampered payment request data.")) # Make the payment request to Adyen acquirer_sudo = request.env['payment.acquirer'].sudo().browse( acquirer_id).exists() tx_sudo = request.env['payment.transaction'].sudo().search([ ('reference', '=', reference) ]) data = { 'merchantAccount': acquirer_sudo.adyen_merchant_account, 'amount': { 'value': converted_amount, 'currency': request.env['res.currency'].browse( currency_id).name, # ISO 4217 }, 'reference': reference, 'paymentMethod': payment_method, 'shopperReference': acquirer_sudo._adyen_compute_shopper_reference(partner_id), 'recurringProcessingModel': 'CardOnFile', # Most susceptible to trigger a 3DS check 'shopperIP': payment_utils.get_customer_ip_address(), 'shopperInteraction': 'Ecommerce', 'storePaymentMethod': tx_sudo.tokenize, # True by default on Adyen side 'additionalData': { 'allow3DS2': True }, 'channel': 'web', # Required to support 3DS 'origin': acquirer_sudo.get_base_url(), # Required to support 3DS 'browserInfo': browser_info, # Required to support 3DS 'returnUrl': urls.url_join( acquirer_sudo.get_base_url(), # Include the reference in the return url to be able to match it after redirection. # The key 'merchantReference' is chosen on purpose to be the same as that returned # by the /payments endpoint of Adyen. f'/payment/adyen/return?merchantReference={reference}'), } response_content = acquirer_sudo._adyen_make_request( url_field_name='adyen_checkout_api_url', endpoint='/payments', payload=data, method='POST') # Handle the payment request response _logger.info( "payment request response for transaction with reference %s:\n%s", reference, pprint.pformat(response_content)) request.env['payment.transaction'].sudo()._handle_feedback_data( 'adyen', dict(response_content, merchantReference=reference), # Match the transaction ) return response_content