def get_refundable_transaction(transaction_id=None, external_id=None): """ Retrieves a refundable transaction from database or raises. raises: exceptions.GatewayException """ if transaction_id is None and external_id is None: raise exceptions.GatewayException('Transaction id is required') try: if transaction_id: transaction = Transaction.objects.get(id=transaction_id, gateway__name='stripe') else: transaction = Transaction.objects.get(external_id=external_id, gateway__name='stripe') except Transaction.DoesNotExist: raise exceptions.GatewayException( 'Transaction {} does not exist'.format(transaction_id)) except Transaction.MultipleObjectsReturned: raise exceptions.GatewayException( 'Multiple transactions with id {}'.format(transaction_id)) if transaction.status not in REFUNDABLE_TRANSACTION_STATUS: raise exceptions.GatewayException( 'Unable to refund transaction with status {}.'.format( transaction.status)) return transaction
def setup_recurring(request): invoice_id = request.data.get('invoice', None) payment_intent = json.loads(request.data.get('payment_intent', "{}")) payment_method = payment_intent.get('payment_method', None) if not payment_method: raise exceptions.GatewayException( _('Cannot process request without enough details. Missing Stripe payment method id.' )) if not invoice_id: raise exceptions.GatewayException(_('Missing invoice id.')) try: db_invoice = Invoice.objects.get(pk=invoice_id, client__in=request.user.clients.all()) except Invoice.DoesNotExist: raise exceptions.GatewayException( 'Invoice {} does not exist'.format(invoice_id)) stripe.api_key = conf.secret_key client = db_invoice.client # type: Client existing_recurrent_payments_config = RecurringPayments.objects.filter( client=client).first() if not existing_recurrent_payments_config: try: stripe_customer = stripe.Customer.create( payment_method=payment_method, address=dict( line1=client.address1 if client.address1 else None, city=client.city if client.city else None, country=client.country if client.country else None, state=client.state if client.state else None, postal_code=client.zip_code if client.zip_code else None, ), email=client.email, name=client.name, phone=client.phone, ) except Exception as e: raise exceptions.GatewayException( _('Could not create customer in Stripe: {}').format(str(e))) try: recurrent_payments_config = RecurringPayments.objects.create( client=client, active=True, stripe_payment_method=payment_method, stripe_customer_id=stripe_customer.id, ) create_new_recurring_payments_ordering( client=recurrent_payments_config.client, gateway_app_name=StripeConfig.name) except Exception as e: del e # unused else: LOG.error( 'Client {} has already set up recurrent payments for stripe.'. format(client.id)) relative_url = 'billing/invoices/{}'.format(invoice_id) return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, relative_url))
def pay_invoice(request): invoice_id = request.query_params.get('invoice') if invoice_id is None: raise exceptions.GatewayException("An 'invoice' parameter is required") try: inv = Invoice.objects.get(pk=invoice_id, client__in=request.user.clients.all()) except Invoice.DoesNotExist: raise exceptions.GatewayException( 'Invoice {} does not exist'.format(invoice_id)) if inv.balance <= 0: raise exceptions.InvoicePaymentException( 'Invoice {} is already paid'.format(invoice_id), invoice_id=invoice_id) amount = str(inv.balance) pay_message = _('Pay {} {} for invoice {}'.format(amount, inv.currency.pk, inv.pk)) recurring = request.query_params.get('recurring', False) recurring = True if (recurring == 'True' or recurring == 'true' or recurring == '1') else False if recurring: existing_recurrent_payments_config = RecurringPayments.objects.filter( client=inv.client).first() if existing_recurrent_payments_config: raise exceptions.GatewayException( _('Recurrent payments were already configured for stripe.')) token_form = SetupRecurringPaymentsForm(initial={'invoice': inv.pk}) stripe.api_key = conf.secret_key payment_intent = stripe.PaymentIntent.create( amount=utils.convert_amount_to_api(inv.balance, inv.currency.pk), currency=inv.currency.pk.lower(), setup_future_usage='off_session', metadata={"invoice": inv.pk}, ) return render(request=request, template_name='stripe/setup_recurring.html', context={ 'payment_vars': json.dumps({'publicKey': conf.public_key}), 'pay_message': pay_message, 'client_secret': payment_intent.client_secret, 'token_form': token_form }) token_form = SimpleInvoicePaymentForm(initial={'invoice': inv.pk}) return render(request=request, template_name='stripe/pay_invoice.html', context={ 'payment_vars': json.dumps({'publicKey': conf.public_key}), 'pay_message': pay_message, 'token_form': token_form })
def callback(request): """ The gateway callback method :type request: rest_framework.request.Request """ stripe.api_key = conf.secret_key try: event = stripe.Webhook.construct_event( payload=request.body, sig_header=request.META.get( 'STRIPE_SIGNATURE', request.META.get('HTTP_STRIPE_SIGNATURE')), secret=conf.signing_secret) except Exception as e: LOG.error(e) raise exceptions.GatewayException('Invalid payload') event_dict = event.to_dict() if event_dict['type'] == "payment_intent.succeeded": intent = event_dict['data']['object'] charges = intent.get('charges', {}) charges_list = charges.get('data', []) charge_dict = charges_list[len(charges_list) - 1] # use latest charge process_charge(charge_dict) elif event_dict['type'] == "payment_intent.payment_failed": pass return Response({'detail': 'OK'}, status=status.HTTP_200_OK)
def capture(request): transaction_id = request.query_params.get('transaction') transaction = Transaction.objects.get(id=transaction_id) invoice = transaction.invoice payu_client = PayUClient(invoice_id=invoice.id, request=request) try: response = payu_client.capture_order( transaction_external_id=transaction.external_id) except Exception as e: raise gateway_exceptions.GatewayException( 'Invalid payu capture: {}'.format(str(e))) if response.status_code == 200 or response.status_code == 201: transaction.status = TransactionStatus.CONFIRMED try: transaction.save() except Exception as e: # do nothing if it's being updated on the callback del e # unused return Response({'detail': 'Ok'}) response_text = json.loads(response.text) status = response_text.get('status') status_desc = status.get('statusDesc') raise Exception('Could not make capture action. {}'.format( status_desc if status_desc else ''))
def refund(request): transaction_id = request.query_params.get('transaction') refund_amount = request.query_params.get('refund_amount') trans = Transaction.objects.get(id=transaction_id) transaction_amount = trans.amount transaction_extra = trans.extra api_invoice_id = utils.invoice_id_to_api(trans.invoice_id) currency = trans.currency_id timestamp = utils.generate_timestamp() nonce = utils.generate_nonce() assert refund_amount is None or refund_amount <= transaction_amount, ( 'Refund amount should be less than' ' the transaction amount.') is_recurring = transaction_extra.get('is_recurring', False) if is_recurring: # FIXME: fix refund to recurring transaction # recurring_data = RecurringPayments.objects.filter(client=trans.invoice.client).first() rrn = transaction_extra.get('RRN', '') int_ref = transaction_extra.get('INT_REF', '') else: rrn = transaction_extra.get('RRN', '') int_ref = transaction_extra.get('INT_REF', '') if refund_amount and refund_amount < transaction_amount: trtype = 25 # partial refund else: trtype = 24 # full refund refund_amount = transaction_amount refund_amount = str(refund_amount) p_sign = utils.calculate_p_sign(conf.encryption_key, api_invoice_id, refund_amount, currency, rrn, int_ref, trtype, conf.terminal, timestamp, nonce, conf.callback_url) form_vars = dict(ORDER=api_invoice_id, AMOUNT=refund_amount, CURRENCY=currency, RRN=rrn, INT_REF=int_ref, TRTYPE=trtype, TERMINAL=conf.terminal, TIMESTAMP=timestamp, NONCE=nonce, BACKREF=conf.callback_url, P_SIGN=p_sign) romcard_form = RomcardCaptureRefundForm(form_vars) romcard_form_action = conf.url if romcard_form.is_valid(): return render(request=request, template_name='romcard/refund_or_capture.html', context={ 'redirect_message': _('Redirecting to RomCard'), 'page_title': _('Refund payment'), 'form': romcard_form, 'form_action': romcard_form_action }) else: LOG.error('Invalid romcard data: {}'.format(romcard_form.errors)) raise exceptions.GatewayException('Invalid romcard data: {}'.format( romcard_form.errors))
def capture(request): transaction_id = request.query_params.get('transaction') transaction = Transaction.objects.get(id=transaction_id) now = utcnow() data = { 'MERCHANT': conf.merchant, 'ORDER_REF': transaction.external_id, 'ORDER_AMOUNT': str(transaction.amount), 'ORDER_CURRENCY': transaction.currency.code, 'IDN_DATE': now.strftime('%Y-%m-%d %H:%M:%S'), } # TODO: from python 3.6 use **data instead of getting each value as keys are ordered in the dict order_hash = PayURoUtils.calculate_signature([ data.get('MERCHANT'), data.get('ORDER_REF'), data.get('ORDER_AMOUNT'), data.get('ORDER_CURRENCY'), data.get('IDN_DATE') ]) data['ORDER_HASH'] = order_hash response = requests.post(url=CAPTURE_ORDER_URL, data=data) if response.status_code == 200 or response.status_code == 201: response_text = response.text response_text = response_text.replace('<EPAYMENT>', '') response_params = response_text.split('|') order_ref = response_params[0] response_code = response_params[1] if response_code == '1': existing_transaction = Transaction.objects.filter( external_id=order_ref).first() if existing_transaction: existing_transaction.status = TransactionStatus.CONFIRMED existing_transaction.save(update_fields=['status']) else: raise gateway_exceptions.GatewayException( _('Could not find existing transaction to mark as captured in db.' )) else: raise gateway_exceptions.GatewayException( _('Could not perform capture action. Details: {}').format( CAPTURE_ERROR_CODE_MEANINGS.get(response_code))) else: raise gateway_exceptions.GatewayException( _('Error when making request to capture transaction.')) return Response({'detail': _('Ok')})
def refund(request): if request.method == 'GET': transaction_id = request.query_params.get('transaction') transaction = get_refundable_transaction(transaction_id=transaction_id) refund_form = StripeRefundForm( initial={'transaction_id': transaction.external_id}) return render(request=request, template_name='stripe/refund_prep.html', context={'refund_form': refund_form}) else: # TODO: use request.data for StripeRefundForm when we support partial refunds and choosing amount is possible refund_form_data = copy.deepcopy(request.data) transaction_id = refund_form_data['transaction_id'] db_transaction = Transaction.objects.filter( external_id=transaction_id).first() # type: Transaction refund_form_data['amount'] = db_transaction.amount refund_form = StripeRefundForm(refund_form_data) if refund_form.is_valid(): stripe.api_key = conf.secret_key external_id = refund_form.cleaned_data['transaction_id'] transaction = get_refundable_transaction(external_id=external_id) try: re = stripe.Refund.create( charge=external_id, amount=utils.convert_amount_to_api( amount=refund_form.cleaned_data['amount'], currency=transaction.currency_id), reason=refund_form.cleaned_data.get( 'reason', 'requested_by_user')) except stripe.error.InvalidRequestError as e: LOG.exception(e) raise exceptions.GatewayException(e) except StripeError as e: LOG.exception(e) raise exceptions.GatewayException(_('Refund failed')) process_refund(re) return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, 'staff/billing/journal')) else: return render(request=request, template_name='stripe/refund_prep.html', context={'refund_form': refund_form})
def pay_invoice(request): invoice_id = request.query_params.get('invoice') try: Invoice.objects.get(pk=invoice_id, client__in=request.user.clients.all()) # type: Invoice except Invoice.DoesNotExist: raise gateway_exceptions.GatewayException(_('Invoice {} does not exist').format(invoice_id)) try: configure() payment = create_payment(invoice_id) except Exception as create_payment_exception: raise gateway_exceptions.InvoicePaymentException(message=str(create_payment_exception), invoice_id=invoice_id) return HttpResponseRedirect(get_approval_url(payment))
def pay_invoice(request): invoice_id = request.query_params.get('invoice') try: Invoice.objects.get( pk=invoice_id, client__in=request.user.clients.all()) # type: Invoice except Invoice.DoesNotExist: raise gateway_exceptions.GatewayException( _('Invoice {} does not exist').format(invoice_id)) try: payu_client = PayUClient(invoice_id=invoice_id, request=request) return payu_client.create_order() except Exception as create_payment_exception: raise gateway_exceptions.InvoicePaymentException( message=str(create_payment_exception), invoice_id=invoice_id)
def charge(request): """Endpoint for creating a charge based on a token""" token_form = SimpleInvoicePaymentForm(request.POST) if not token_form.is_valid(): raise exceptions.GatewayException(token_form.errors) invoice_id = token_form.cleaned_data['invoice'] charge_token = token_form.cleaned_data['token'] try: inv = Invoice.objects.get(pk=invoice_id, client__in=request.user.clients.all()) except Invoice.DoesNotExist: raise exceptions.InvoicePaymentException( 'Invoice {} does not exist'.format(invoice_id), invoice_id=invoice_id) if inv.balance <= 0: raise exceptions.InvoicePaymentException( 'Invoice {} is already paid'.format(invoice_id), invoice_id=invoice_id) stripe.api_key = conf.secret_key token_id = charge_token.get('id') if token_id is None: raise exceptions.InvoicePaymentException('Charge failed', invoice_id=invoice_id) try: charge_result = stripe.Charge.create( amount=utils.convert_amount_to_api(inv.balance, inv.currency.pk), currency=inv.currency.pk.lower(), description=_('Invoice {} payment').format(inv.pk), metadata={"invoice": inv.pk}, source=token_id, idempotency_key=None) except CardError as e: LOG.exception(e) body = e.json_body err = body.get('error', {}) raise exceptions.InvoicePaymentException(err.get( 'message', 'Charge failed'), invoice_id=invoice_id) except StripeError as e: LOG.exception(e) raise exceptions.InvoicePaymentException('Charge failed', invoice_id=invoice_id) process_charge(charge=charge_result) relative_url = 'billing/invoices/{}'.format(invoice_id) return HttpResponseRedirect( fleio_join_url(settings.FRONTEND_URL, relative_url))
def capture(request): transaction_id = request.query_params.get('transaction') trans = Transaction.objects.get(id=transaction_id) invoice = trans.invoice api_invoice_id = utils.invoice_id_to_api(invoice.pk) amount = str(trans.amount) currency = trans.currency_id transaction_extra = trans.extra timestamp = utils.generate_timestamp() nonce = utils.generate_nonce() trtype = utils.TransactionType.SALES_COMPLETION p_sign = utils.calculate_p_sign(conf.encryption_key, api_invoice_id, amount, currency, trans.external_id, transaction_extra.get('INT_REF'), trtype, conf.terminal, timestamp, nonce, conf.callback_url) form_vars = { 'ORDER': api_invoice_id, 'AMOUNT': amount, 'CURRENCY': currency, 'RRN': trans.external_id, 'INT_REF': transaction_extra.get('INT_REF'), 'TRTYPE': trtype, 'TERMINAL': conf.terminal, 'TIMESTAMP': timestamp, 'NONCE': nonce, 'BACKREF': conf.callback_url, 'P_SIGN': p_sign } romcard_form = RomcardCaptureRefundForm(form_vars) romcard_form_action = conf.url if romcard_form.is_valid(): return render(request=request, template_name='romcard/refund_or_capture.html', context={ 'redirect_message': _('Redirecting to RomCard'), 'page_title': _('Capture payment'), 'form': romcard_form, 'form_action': romcard_form_action }) else: LOG.error('Invalid romcard data: {}'.format(romcard_form.errors)) raise exceptions.GatewayException('Invalid romcard data: {}'.format( romcard_form.errors))
def process_charge(charge): """Process a charge result""" gateway = Gateway.objects.get(name='stripe') external_id = charge.id transaction_id = None transaction_status = charge_status(charge) invoice_id = charge.metadata.get('invoice') if external_id is not None: try: existing_transaction = Transaction.objects.get( external_id=external_id, gateway=gateway) transaction_id = existing_transaction.id except Transaction.DoesNotExist: real_amount = utils.convert_amount_from_api( charge.amount, charge.currency) serializer_data = { 'invoice': invoice_id, 'external_id': external_id, 'amount': real_amount, 'currency': charge.currency.upper(), 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=real_amount), 'date_initiated': datetime.datetime.fromtimestamp(charge.created), 'extra': {}, 'status': transaction_status } tr_ser = AddTransactionSerializer(data=serializer_data) if tr_ser.is_valid(raise_exception=False): new_transaction = tr_ser.save() transaction_id = new_transaction.id else: LOG.error('Could not process charge for stripe: {}'.format( json.dumps(tr_ser.errors))) raise exceptions.InvoicePaymentException( 'Could not process charge for invoice {}.', invoice_id=invoice_id) else: # Update the transaction status only existing_transaction.status = transaction_status existing_transaction.save(update_fields=['status']) gateway.log_callback(external_id=external_id, status=charge.status, data=charge, error=(charge.status == 'failed'), error_code=charge.failure_code, error_info=charge.failure_message) if transaction_status == TransactionStatus.FAILURE: raise exceptions.GatewayException(charge.failure_message or 'error') else: if transaction_status == TransactionStatus.SUCCESS: activity_helper.start_generic_activity( category_name='stripe', activity_class='stripe payment', invoice_id=invoice_id) invoice_add_payment.delay(invoice_id=invoice_id, amount=utils.convert_amount_from_api( charge.amount, charge.currency), currency_code=charge.currency.upper(), transaction_id=transaction_id) activity_helper.end_activity()
def refund(request): gateway = Gateway.objects.get(name='payu') transaction_id = request.query_params.get('transaction') transaction = Transaction.objects.get(id=transaction_id) invoice = transaction.invoice payu_client = PayUClient(invoice_id=invoice.id, request=request) if transaction.status == Transaction.TRANSACTION_STATUS.CONFIRMED: # do a refund for transaction status confirmed try: response = payu_client.refund( transaction_external_id=transaction.external_id) except Exception as e: raise gateway_exceptions.GatewayException( 'Invalid payu refund action: {}'.format(str(e))) if response.status_code == 200 or response.status_code == 201: response_text = json.loads(response.text) refund_data = response_text.get('refund', {}) new_refund_transaction = { 'invoice': invoice.id, 'external_id': transaction.external_id, 'amount': str( PayUUtils.get_payu_amount_in_fleio_amount( refund_data.get('amount'))), 'currency': refund_data.get('currencyCode'), 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(transaction.amount)), 'date_initiated': refund_data.get('creationDateTime'), 'extra': { 'refundId': refund_data.get('refundId'), 'extRefundId': refund_data.get('extRefundId'), }, 'refunded_transaction': transaction.pk, 'status': TransactionStatus.REFUNDED } transaction_serializer = AddTransactionSerializer( data=new_refund_transaction) if transaction_serializer.is_valid(raise_exception=False): new_transaction = transaction_serializer.save() activity_helper.start_generic_activity( category_name='payu', activity_class='payu payment refund', invoice_id=invoice.id) invoice_refund_payment.delay( transaction_id=transaction_id, amount=PayUUtils.get_payu_amount_in_fleio_amount( refund_data.get('amount')), to_client_credit=False, new_transaction_id=new_transaction.pk) activity_helper.end_activity() else: LOG.error('PayU transaction error: {}'.format( transaction_serializer.errors)) raise gateway_exceptions.InvoicePaymentException( 'Transaction error', invoice_id=invoice.id) return Response({'detail': 'Ok'}) else: # do a cancellation request for when transaction was not yet confirmed try: payu_client.cancel(transaction_external_id=transaction.external_id) except Exception as e: raise gateway_exceptions.GatewayException( 'Invalid payu refund action: {}'.format(str(e))) return Response({'detail': 'Ok'}) response_text = json.loads(response.text) status = response_text.get('status') status_desc = status.get('statusDesc') raise Exception('Could not make capture action. {}'.format( status_desc if status_desc else ''))
def callback(request): if not PayUUtils.validate_open_payu_signature( open_payu_signature=request.headers.get('OpenPayu-Signature', None), body=request.body, ): raise gateway_exceptions.GatewayException( 'Did not receive or received invalid OpenPayu-Signature') gateway = Gateway.objects.get(name='payu') order_details = request.data.get('order', None) if not order_details: raise Exception('No order details') external_id = order_details.get('orderId') invoice_id = PayUUtils.get_invoice_id_from_external_order_id( external_order_id=order_details.get('extOrderId')) transaction_status = order_details.get('status') total_amount = PayUUtils.get_payu_amount_in_fleio_amount( amount=order_details.get('totalAmount')) if order_details.get('status') == PayUTransactionStatus.canceled: # process refund for pre auth transaction existing_transaction = Transaction.objects.filter( external_id=external_id, gateway=gateway, refunded_transaction__isnull=True).first() if not existing_transaction: raise gateway_exceptions.GatewayException( 'Could not process cancellation notification because there is no transaction to refund' ) new_refund_transaction = { 'invoice': invoice_id, 'external_id': external_id, 'amount': str(total_amount), 'currency': order_details.get('currencyCode'), 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(total_amount)), 'date_initiated': utcnow(), 'extra': {}, 'refunded_transaction': existing_transaction.pk, 'status': TransactionStatus.REFUNDED } transaction_serializer = AddTransactionSerializer( data=new_refund_transaction) if transaction_serializer.is_valid(raise_exception=False): new_transaction = transaction_serializer.save() activity_helper.start_generic_activity( category_name='payu', activity_class='payu payment refund', invoice_id=existing_transaction.invoice.id) invoice_refund_payment.delay( transaction_id=existing_transaction.id, amount=total_amount, to_client_credit=False, new_transaction_id=new_transaction.pk) activity_helper.end_activity() else: LOG.error('PayU transaction error: {}'.format( transaction_serializer.errors)) raise gateway_exceptions.GatewayException( 'Could not process cancellation notification because serialized data is invalid' ) return Response({'detail': 'Ok'}) # process pre-auth, confirmation notifications existing_transaction = Transaction.objects.filter( external_id=external_id, gateway=gateway, refunded_transaction__isnull=True).first() if existing_transaction: # Update the transaction status and mark invoice as paid (still needs capture if automatic capture is not # enabled in PayU) if client successfully made the payment existing_transaction.status = PayUTransactionStatus.to_transaction_model_status.get( transaction_status) existing_transaction.save(update_fields=['status']) if (transaction_status == PayUTransactionStatus.waiting_for_confirmation or (transaction_status == PayUTransactionStatus.completed and existing_transaction.invoice and existing_transaction.invoice.is_unpaid())): activity_helper.start_generic_activity( category_name='payu', activity_class='payu payment', invoice_id=invoice_id) invoice_add_payment.delay( invoice_id=invoice_id, amount=total_amount, currency_code=order_details.get('currencyCode'), transaction_id=existing_transaction.id) activity_helper.end_activity() return Response({'detail': 'Ok'}) elif transaction_status == PayUTransactionStatus.pending: serializer_data = { 'invoice': invoice_id, 'external_id': external_id, 'amount': total_amount, 'currency': order_details.get('currencyCode'), 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(total_amount)), 'date_initiated': order_details.get('orderCreateDate'), 'extra': {}, 'status': PayUTransactionStatus.to_transaction_model_status.get( transaction_status) } add_transaction_serializer = AddTransactionSerializer( data=serializer_data) if add_transaction_serializer.is_valid(raise_exception=False): add_transaction_serializer.save() else: LOG.error('PayU transaction error: {}'.format( add_transaction_serializer.errors)) raise gateway_exceptions.InvoicePaymentException( 'Transaction error', invoice_id=invoice_id) return Response({'detail': 'Ok'})
def refund(request): gateway = Gateway.objects.get(name='payuro') transaction_id = request.query_params.get('transaction') transaction = Transaction.objects.get(id=transaction_id) invoice = transaction.invoice now = utcnow() data = { 'MERCHANT': conf.merchant, 'ORDER_REF': transaction.external_id, 'ORDER_AMOUNT': str(transaction.amount), 'ORDER_CURRENCY': transaction.currency.code, 'IRN_DATE': now.strftime('%Y-%m-%d %H:%M:%S'), 'AMOUNT': str(transaction.amount), } # TODO: from python 3.6 use **data instead of getting each value as keys are ordered in the dict order_hash = PayURoUtils.calculate_signature([ data.get('MERCHANT'), data.get('ORDER_REF'), data.get('ORDER_AMOUNT'), data.get('ORDER_CURRENCY'), data.get('IRN_DATE'), data.get('AMOUNT') ]) data['ORDER_HASH'] = order_hash response = requests.post(url=REFUND_ORDER_URL, data=data) if response.status_code == 200 or response.status_code == 201: response_text = response.text response_text = response_text.replace('<EPAYMENT>', '') response_params = response_text.split('|') order_ref = response_params[0] response_code = response_params[1] if response_code == '1': existing_transaction = Transaction.objects.filter( external_id=order_ref).first() if existing_transaction: new_refund_transaction = { 'invoice': invoice.id, 'external_id': transaction.external_id, 'amount': str(transaction.amount), 'currency': transaction.currency.code, 'gateway': gateway.pk, 'fee': gateway.get_fee( amount=decimal.Decimal(transaction.amount)), 'date_initiated': now, 'extra': {}, 'refunded_transaction': transaction.pk, 'status': TransactionStatus.REFUNDED } transaction_serializer = AddTransactionSerializer( data=new_refund_transaction) if transaction_serializer.is_valid(raise_exception=False): new_transaction = transaction_serializer.save() activity_helper.start_generic_activity( category_name='payuro', activity_class='payuro payment refund', invoice_id=invoice.id) invoice_refund_payment.delay( transaction_id=transaction_id, amount=transaction.amount, to_client_credit=False, new_transaction_id=new_transaction.pk) activity_helper.end_activity() else: LOG.error( _('PayU Ro transaction error: {}').format( transaction_serializer.errors)) raise gateway_exceptions.GatewayException( _('Transaction error {}').format( transaction_serializer.errors)) else: raise gateway_exceptions.GatewayException( _('Could not find existing transaction to mark as captured in db.' )) else: raise gateway_exceptions.GatewayException( _('Could not perform capture action. Details: {}').format( REFUND_ERROR_CODE_MEANINGS.get(response_code))) else: raise gateway_exceptions.GatewayException( _('Error when making request to capture transaction.')) return Response({'detail': _('Ok')})
def callback(request): request_body = request.body if not PayURoUtils.verify_hash(request_body): raise gateway_exceptions.GatewayException( _('Did not receive or received invalid signature.')) gateway = Gateway.objects.get(name='payuro') invoice_id = request.data.get('REFNOEXT', None) if not invoice_id: raise gateway_exceptions.GatewayException( _('Missing fleio invoice id.')) external_id = request.data.get('REFNO') total_amount = request.data.get('IPN_TOTALGENERAL', None) currency = request.data.get('CURRENCY') status = request.data.get('ORDERSTATUS') invoice = Invoice.objects.get(id=invoice_id) if 'TOKEN_HASH' in request.data: try: recurrent_payments_config = RecurringPayments.objects.create( client=invoice.client, active=True, token_hash=request.data['TOKEN_HASH'], ipn_cc_mask=request.data['IPN_CC_MASK'], ipn_cc_exp_date=request.data['IPN_CC_EXP_DATE'], ) create_new_recurring_payments_ordering( client=recurrent_payments_config.client, gateway_app_name=PayURoConfig.name) except Exception as e: del e # unused pass if status == PayUROOrderStatus.preauth: transaction_status = PayUROOrderStatus.to_fleio_transaction_status[ PayUROOrderStatus.preauth] serializer_data = { 'invoice': invoice_id, 'external_id': external_id, 'amount': total_amount, 'currency': currency, 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(total_amount)), 'date_initiated': request.data.get('SALEDATE'), 'extra': {}, 'status': transaction_status } add_transaction_serializer = AddTransactionSerializer( data=serializer_data) if add_transaction_serializer.is_valid(raise_exception=False): db_transaction = add_transaction_serializer.save() activity_helper.start_generic_activity( category_name='payuro', activity_class='payuro payment', invoice_id=invoice_id) invoice_add_payment.delay(invoice_id=invoice_id, amount=total_amount, currency_code=currency, transaction_id=db_transaction.id) activity_helper.end_activity() else: LOG.error( _('PayU Ro transaction error: {}').format( add_transaction_serializer.errors)) raise gateway_exceptions.GatewayException('Add transaction error') ipn_response = PayURoUtils.build_ipn_response(params=request_body) return Response({'detail': ipn_response})
def pay_invoice(request): invoice_id = request.query_params.get('invoice') recurring = request.query_params.get('recurring', False) recurring = True if (recurring == 'True' or recurring == 'true' or recurring == '1') else False if invoice_id is None: raise exceptions.GatewayException("An 'invoice' parameter is required") try: inv = Invoice.objects.get(pk=invoice_id, client__in=request.user.clients.all()) except Invoice.DoesNotExist: raise exceptions.GatewayException( 'Invoice {} does not exist'.format(invoice_id)) if inv.balance <= 0: raise exceptions.GatewayException( 'Invoice {} is already paid'.format(invoice_id)) trtype = str(utils.TransactionType.PRE_AUTHORIZATION) language_code = 'ro' if request.LANGUAGE_CODE == 'ro' else 'en' lang = language_code description = 'Invoice {}'.format(inv.pk) timestamp = utils.generate_timestamp() nonce = utils.generate_nonce() amount = str(inv.balance) api_invoice_id = utils.invoice_id_to_api(invoice_id) recur_exp = utils.generate_recur_timestamp(days=conf.recur_exp) romcard_gateway = Gateway.objects.filter( name=get_gateway_name_from_app_name( gateway_app_name=RomcardConfig.name)).first() if not romcard_gateway: raise exceptions.GatewayException(_('Could not find the gateway.')) args = [ conf.encryption_key, amount, inv.currency.pk, api_invoice_id, description, conf.merchant_name, conf.merchant_url, conf.merchant_no, conf.terminal, conf.email, trtype, '', '', timestamp, nonce, conf.callback_url ] if romcard_gateway.recurring_payments_enabled and recurring: args.append(conf.recur_days) args.append(recur_exp) elif romcard_gateway.recurring_payments_enabled: args.append('') args.append('') p_sign = utils.calculate_p_sign(*args) fvars = { 'AMOUNT': amount, 'CURRENCY': inv.currency.pk, 'ORDER': api_invoice_id, 'DESC': description, 'MERCH_NAME': conf.merchant_name, 'MERCH_URL': conf.merchant_url, 'MERCHANT': conf.merchant_no, 'TERMINAL': conf.terminal, 'EMAIL': conf.email, 'TRTYPE': trtype, 'COUNTRY': '', 'MERCH_GMT': '', 'TIMESTAMP': timestamp, 'NONCE': nonce, 'BACKREF': conf.callback_url } if romcard_gateway.recurring_payments_enabled: fvars['RECUR_FREQ'] = '' if not recurring else conf.recur_days fvars['RECUR_EXP'] = '' if not recurring else recur_exp fvars['P_SIGN'] = p_sign fvars['LANG'] = lang if recurring: client = get_client_by_invoice_id(invoice_id=invoice_id) try: RecurringPayments.objects.create(client=client) except IntegrityError: # already exists for this client pass else: recurrent_payments_config = RecurringPayments.objects.filter( client=inv.client).first() if recurrent_payments_config: _remove_recurrent_payments_config(recurrent_payments_config) if recurring or romcard_gateway.recurring_payments_enabled: romcard_form = RomcardSubscribeForm(fvars) else: romcard_form = RomcardPayForm(fvars) romcard_form_action = conf.url if romcard_form.is_valid(): return render(request=request, template_name='romcard/pay_invoice.html', context={ 'redirect_message': _('Redirecting to RomCard'), 'page_title': _('Invoice payment'), 'form': romcard_form, 'form_action': romcard_form_action }) else: LOG.error('Invalid romcard data: {}'.format(romcard_form.errors)) raise exceptions.GatewayException('Invalid data provided')
def pay_invoice(request): invoice_id = request.query_params.get('invoice') recurring = request.query_params.get('recurring', False) recurring = True if (recurring == 'True' or recurring == 'true' or recurring == '1') else False if invoice_id is None: raise exceptions.GatewayException(_('Missing invoice parameter.')) try: db_invoice = Invoice.objects.get( pk=invoice_id, client__in=request.user.clients.all()) # type: Invoice except Invoice.DoesNotExist: raise exceptions.GatewayException( _('Invoice {} does not exist').format(invoice_id)) if db_invoice.balance <= 0: raise exceptions.GatewayException( _('Invoice {} is already paid').format(invoice_id)) client = db_invoice.client # type: Client now = utcnow() # params order_ref = '{}'.format(db_invoice.id) order_date = now.strftime('%Y-%m-%d %H-%M-%S') multi_items = True if db_invoice.items.count() > 1 else False order_pname = [ '{}'.format(item.item_type) for item in db_invoice.items.all() ] if multi_items: order_pcode = [] counter = 1 for item in db_invoice.items.all(): order_pcode.append('{}{}'.format(item.item_type, str(counter))) counter = counter + 1 else: order_pcode = [ '{}'.format(item.item_type) for item in db_invoice.items.all() ] order_pinfo = [ '{}'.format(item.item_type) for item in db_invoice.items.all() ] order_price = [str(item.amount) for item in db_invoice.items.all()] order_price_type = ['GROSS' for x in db_invoice.items.all()] order_qty = ['1' for x in db_invoice.items.all()] order_vat = ['0' for x in db_invoice.items.all()] pay_method = 'CCVISAMC' order_shipping = '' discount = '0' client_phone = client.phone if client.phone else '-' client_country = client.country.upper() if client.country else '' language = request.user.language.upper() if request.user.language else 'RO' testorder = conf.testorder back_ref = fleio_join_url(settings.FRONTEND_URL, 'billing/invoices') order_hash = PayURoUtils.calculate_signature(params=[ conf.merchant, order_ref, order_date, order_pname, order_pcode, order_pinfo, order_price, order_qty, order_vat, order_shipping, db_invoice.currency.code, discount, pay_method, order_price_type, testorder ]) return render( request=request, template_name='payuro/pay_invoice.html', context={ 'redirect_message': _('Redirecting to PayU'), 'page_title': _('Invoice payment'), 'form_action': CREATE_ORDER_URL, # inputs 'MERCHANT': conf.merchant, 'ORDER_REF': order_ref, 'ORDER_DATE': order_date, 'MULTI_ITEMS': multi_items, 'ORDER_PNAME': order_pname if len(order_pname) > 1 else order_pname[0], 'ORDER_PCODE': order_pcode if len(order_pcode) > 1 else order_pcode[0], 'ORDER_PINFO': order_pinfo if len(order_pinfo) > 1 else order_pinfo[0], 'ORDER_PRICE': order_price if len(order_price) > 1 else order_price[0], 'ORDER_PRICE_TYPE': order_price_type if len(order_price_type) > 1 else order_price_type[0], 'ORDER_QTY': order_qty if len(order_qty) > 1 else order_qty[0], 'ORDER_VAT': order_vat if len(order_vat) > 1 else order_vat[0], 'PRICES_CURRENCY': db_invoice.currency.code, 'PAY_METHOD': pay_method, 'ORDER_SHIPPING': order_shipping, 'TESTORDER': testorder, 'DISCOUNT': discount, 'BILL_FNAME': client.first_name, 'BILL_LNAME': client.last_name, 'BILL_EMAIL': client.email, 'BILL_PHONE': client_phone, 'BILL_COUNTRYCODE': client_country, 'LANGUAGE': language, 'BACK_REF': back_ref, 'ORDER_HASH': str(order_hash, encoding='utf-8'), 'RECURRING': True if recurring else False, })
def process_refund(refund): # FIXME(tomo): This does not support partial refunds # Partial refunds should be allowed if the invoice balance is higher than the requested amount # Right now, creating a partial refund will set the transaction to refunded even if you refund # less than the payment value. gateway = Gateway.objects.get(name='stripe') external_id = refund.id existing_transaction_id = None invoice_id = None if refund.status == 'succeeded': if external_id is not None: try: existing_transaction = Transaction.objects.get( external_id=refund.charge, gateway=gateway) existing_transaction_id = existing_transaction.pk invoice_id = existing_transaction.invoice_id except Transaction.DoesNotExist: raise exceptions.GatewayException('Transaction does not exist') else: existing_transaction.status = TransactionStatus.REFUNDED existing_transaction.save(update_fields=['status']) gateway.log_callback(external_id=external_id, status=refund.status, data=refund, error=False) # Create a new refund transaction real_amount = utils.convert_amount_from_api(refund.amount, refund.currency) created_date = datetime.datetime.fromtimestamp(refund.created) new_transaction_status = TransactionStatus.REFUNDED new_refund_transaction = { 'invoice': invoice_id, 'external_id': external_id, 'amount': real_amount, 'currency': refund.currency, 'gateway': gateway.pk, 'fee': gateway.get_fee(amount=decimal.Decimal(real_amount)), 'date_initiated': created_date, 'extra': { 'balance_transaction': refund.balance_transaction }, 'refunded_transaction': existing_transaction_id, 'status': new_transaction_status } tr_ser = AddTransactionSerializer(data=new_refund_transaction) if tr_ser.is_valid(raise_exception=False): new_transaction = tr_ser.save() new_transaction_id = new_transaction.id activity_helper.start_generic_activity( category_name='stripe', activity_class='stripe payment refund', invoice_id=invoice_id) invoice_refund_payment.delay( transaction_id=new_transaction.refunded_transaction.pk, amount=utils.convert_amount_from_api(refund.amount, refund.currency), to_client_credit=False, new_transaction_id=new_transaction_id) activity_helper.end_activity() else: LOG.error(tr_ser.errors) raise exceptions.InvoicePaymentException( 'Unable to process refund', invoice_id=invoice_id) elif refund.status == 'failed': gateway.log_callback(external_id=external_id, status=refund.status, data=refund, error=(refund.status == 'failed'), error_info=refund.failure_reason) raise exceptions.GatewayException( _('Refund for transaction {} failed').format(external_id))